Dr.Random
Dr.Random

Reputation: 500

Resolve mDNS in local network with javascript from https origin

My goal

Ping an esp32 device which is in a local network without internet and without https.

Reason

I have a node js server with a PWA hosted on render.com. This server is communicating with my esp32 which is in a local network. ESP32 connected to the server via websockets and sends data to it.

User opens the PWA which is default https. I want to check if the user is in it's own local network by pinging my esp32. If the ping was a success i want to redirect the user to the local webpage ( because of network speed ).

The Problem

The main problem which prevents me to do this simply and elegantly is mixed content policy. My PWA site just can't access any http endpoint because it is served over https. (even if it is in a local network) My esp32 has already a continous https connection to my server and there is not enough resources to host an HTTP webserver concurrently.

What i have tried

Try 1.

I'm using helmet on node.js to further secure my site. I added the following config to helmet in order to let my pwa ping my esp32 by mDNS.

app.use(helmet({
    contentSecurityPolicy: {
        directives: {
            defaultSrc: ["'self'"],
            connectSrc: ["'self'", "https://esp.local/", "http://esp.local/"],
        }
    },
    hsts:{
        maxAge:0
    },
    crossOriginEmbedderPolicy: false,
    crossOriginResourcePolicy: { policy: "cross-origin" }
}));

This doesn't work because of mixed content. Browser ( chrome ) refuses to connect.

Try 2.

I noticed that when i want to access my http esp32 with https the browser ( chrome ) spits out a net::ERR_CONNECTION_REFUSED message to the console, indicating that my ESP32 is in fact in the local network but refused the https connection. After that I have tried to power off my esp32 and repeat the fetch again. If my esp32 is not alive the error is different. net::ERR_CONNECTION_TIMED_OUT or net::ERR_NAME_NOT_RESOLVED. I tought that it was good news because i can just capture the net::ERR_CONNECTION_REFUSED error and redirect my user to the local web app.

Here is my approach

async function isLocalNetwork() {
    const resp = await fetch('https://esp.local/ping').catch((err) => console.log(err));
    console.log(resp);
    return resp.ok;
}

// Redirect to local ESP32 frontend if the client is in the local network
(async () => {
    if (await isLocalNetwork()) {
        //window.location.href = 'http://esp.local/'; // Redirect works regardless of ssl
        alert("Can redirect!");
    }else{
        alert("Can't redirect!");
    }
})();

The problem is that neither the response nor the error contains these browser errors. I than tried with try catch blocks

async function isLocalNetwork() {
    try {
        const resp = await fetch('https://esp.local/ping');
        return resp.ok;
    } catch (err) {
        console.error(err); // Log the error object for debugging
        if (err.code === "ECONNREFUSED") {
            console.log("Connection refused"); // Handle connection refused error
        } else if (err.code === "ENOTFOUND") {
            console.log("Name not resolved"); // Handle name not resolved error
        } else {
            console.log("Other network error"); // Handle other network errors
        }
        return false; // Return false indicating connection failure
    }
}

This also does not contain the net:: errors i see on the console.

Chrome spits out this to the console btw:

main.js:1 
GET https://esp.local/ping net::ERR_CONNECTION_REFUSED

I have also tried global window.onError listener but this does not capture the fetch errors also. In my final desperation i wanted to read from console the raw string message. I tought i would parse it raw and if i find the error string i just redirect. But JS can't read from the console because of security reasons....

Try 3.

I have found a network discovery script on github which looked promising: https://github.com/samyk/webscan

Hovewer if it would work it wouldn't be enough because it finds the IP addresses and the esp32 can be configured to allow DHCP so the IP can change.

Summary

I don't ask a big thing. I just want to ping my device in the local network. I don't want to transmit data and don't want to do anything heavy. I just want to know if the site user and my device is on the same local network. Is this a big ask?

Maybe I need to do this the other way around? Check the user's public IP in the node.js server and compare the connected esp32's public IP to it. If it matches, redirect the user?

I hope somebody can help me find a clever way of doing this.

Upvotes: 1

Views: 192

Answers (0)

Related Questions