Skip to content

Web Workflow: network mDNS hostname often differs from local-device mDNS hostname (links & redirects don't work) #6869

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
anecdata opened this issue Sep 6, 2022 · 11 comments · Fixed by #7295 or #9699
Assignees
Milestone

Comments

@anecdata
Copy link
Member

anecdata commented Sep 6, 2022

CircuitPython version

Adafruit CircuitPython 8.0.0-beta.0 on 2022-08-18; Adafruit QT Py ESP32S2 with ESP32S2

Code/REPL

# From another device on the network, "augmented" names are found:
m = mdns.Server(wifi.radio)
for service in m.find(service_type="_circuitpython", protocol="_tcp", timeout=5):
        print(service.instance_name)
        print(service.hostname)
        print(f"{service.ipv4_address}:{service.port}")
"""
Adafruit QT Py ESP32S2-5
cpy-f66961-281
192.168.6.180:80
"""

# So these work:
# curl -v -L http://192.168.6.180/cp/devices.json
# curl -v -L http://cpy-f66961-281/cp/devices.json

# But the device itself does not seem to know about the augmented name:
print(m.hostname)
# cpy-f66961
print(m.instance_name)
# cpy-f66961
# NOTE: this is completely different from the Instance Name reported from other devices using .find

# So this does not work:
# curl -v -L http://cpy-f66961.local/cp/devices.json

Behavior

From the network perspective, CircuitPython mDNS hostnames often have a hyphen and integer appended to the hostname. CircuitPython mDNS server.find code will expose these augmented hostnames (and instance names), and browsers and curl will find the devices using these augmented names.

Anyone who has encountered things like iPhone (3) has encountered a related issue, I suspect.

However, the device itself [currently] does not recognize itself with these augmented names. The Web API will report something like:

Hostname:
cpy-f66961
IP:
192.168.6.180

and clicking the hostname link on the Web API will fail.

Redirects also fail since the device doesn't answer to its nominal hostname:

% curl -v -L http://circuitpython.local/cp/devices.json
*   Trying 192.168.6.180:80...
* Connected to circuitpython.local (192.168.6.180) port 80 (#0)
> GET /cp/devices.json HTTP/1.1
> Host: circuitpython.local
> User-Agent: curl/7.79.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 307 Temporary Redirect
< Connection: close
< Content-Length: 0
< Location: http://cpy-f66961.local/cp/devices.json
< Access-Control-Allow-Credentials: true
< Vary: Origin, Accept, Upgrade
< Access-Control-Allow-Origin: 
< 
* Closing connection 0
* Issue another request to this URL: 'http://cpy-f66961.local/cp/devices.json'
* Could not resolve host: cpy-f66961.local
* Closing connection 1
curl: (6) Could not resolve host: cpy-f66961.local

or simply:

% curl -v -L http://cpy-f66961.local/cp/devices.json 
* Could not resolve host: cpy-f66961.local
* Closing connection 0
curl: (6) Could not resolve host: cpy-f66961.local

Description

When a device mDNS hostname has been augmented:

  1. the "Hostname:" link on the Web API web page will fail
  2. the File REST API breaks

Additional information

Since the device knows its own IPv4 address, perhaps one solution could be to redirect to the IPv4 address instead of to the network-invalid mDNS local-device hostname, and not redirect at all if the entry URL is the IP address.

@anecdata anecdata added the bug label Sep 6, 2022
@makermelissa
Copy link
Collaborator

Ok, this sounds like it's not yet properly handling mDNS Hostname conflict naming. While in theory, every device should have a unique name because of using the MAC address, this isn't always the case such as with an improperly setup network where multiple mDNS resolvers keep trying to resolve the same host.

This is good info because there may need to be some updates to code.circuitpython.org to deal with this there too.

@dhalbert dhalbert added this to the 8.0.0 milestone Sep 13, 2022
@tannewt
Copy link
Member

tannewt commented Nov 29, 2022

@anecdata How many devices do you have on the network? Are you relying on web workflow to start wifi or are you starting it everytime yourself in code.py?

@anecdata
Copy link
Member Author

anecdata commented Nov 29, 2022

Typically less than half a dozen CircuitPython devices with web workflow enabled. I don't have any collisions in the lower three octets of the MAC.

Currently on devices where web workflow is enabled, web workflow starts up wifi (but I can do it either way, that's not strictly necessary - wifi has to be prepared to detect disconnection and re-connect anyway).

@tannewt
Copy link
Member

tannewt commented Nov 29, 2022

This seems like we'll need to add the mac address to the instance name to make it unique. That may be causing the collision.

The IDF public API doesn't provide a way to read back the "mangled" hostname or instance name unfortunately. That causes the mismatch.

I'll explore more tomorrow. Thanks for the details!

@tannewt
Copy link
Member

tannewt commented Nov 30, 2022

@anecdata Could you please give me more details of your network setup and what code is running on each device and what device it is? I was able to get the IDF to mangle the instance name but not the hostname.

@anecdata
Copy link
Member Author

anecdata commented Nov 30, 2022

Not sure what all is useful to you... Multiple industrial-strength APs, all with the same SSID(s). Relatively complex home network wired / wifi, but robust. A lot of devices on the network, a bunch of Apple devices, some Raspberry Pi, myriad other things. There are a lot of CircuitPython devices, though only a handful running web workflow (and none anymore doing non-web-workflow mDNS).

Typically I'm using Safari to interact with the web workflow pages, could be something to do with Mac cacheing. But accessing from Chromium on a Raspberry Pi also requires the numeric extension to the mDNS hostname (note that no devices ever show up in the Here are other CircuitPython devices on your network: list on that platform).

My assumption has been that mDNS is really just between the device advertising its hostname and instance name on an IPv4:port, and the device trying to access it (no central name server).

The web workflow devices are all ESP32-S2 (QT Py), now running 8.0.0-beta.3 or -beta.4. Four of them are running rudimentary adafruit_httpserver just for testing. Another is running a typical web client. Another is just chilling in the REPL. All are in Station mode, no APs.

Typical web workflow display as follows:

Here are other CircuitPython devices on your network:

Adafruit QT Py ESP32S2-4 (cpy-d96468-22)
Adafruit QT Py ESP32S2-6 (cpy-706b80)
Adafruit QT Py ESP32S2-5 (cpy-a58fbc-83)
Adafruit QT Py ESP32S2-2 (cpy-70e2de-12)

The Here are other CircuitPython devices on your network: rarely finds all of the devices, typically 0-3.

A cpy-aaeb36 hostname on one device's web workflow page shows up to other devices' web workflow pages as Adafruit QT Py ESP32S2 (cpy-aaeb36-666).

Neradoc has also seen this behavior, various discussions on Discord. The behavior has been consistent throughout the 8.0.0-beta period.

@Neradoc
Copy link

Neradoc commented Dec 1, 2022

I've been doing tests with the following python code, adapted from the zeroconf library examples:

from zeroconf import ServiceBrowser, ServiceListener, Zeroconf
from zeroconf import ZeroconfServiceTypes

class MyListener(ServiceListener):

    def update_service(self, zc: Zeroconf, type_: str, name: str) -> None:
        info = zc.get_service_info(type_, name)
        print(f">>> Service {name} updated")
        print("   ", info.server)

    def add_service(self, zc: Zeroconf, type_: str, name: str) -> None:
        info = zc.get_service_info(type_, name)
        print(f"+++ Service {name} added")
        print("   ", info.server)

zeroconf = Zeroconf()
listener = MyListener()

browser2 = ServiceBrowser(zeroconf, "_circuitpython._tcp.local.", listener)
try:
    input("Press enter to exit...\n\n")
except KeyboardInterrupt:
    pass
finally:
    zeroconf.close()

It detects and prints new MDNS services and updates.
If I only run one device with web workflow, it just lists it once, and nothing else seems to happen.

+++ Service Adafruit FunHouse._circuitpython._tcp.local. added
    cpy-1737d6.local.

If I add a second device, it's detected and so far so good.

+++ Service Adafruit QT Py ESP32C3._circuitpython._tcp.local. added
    cpy-30d644.local.

But if then I do requests to circuitpython.local, using for example: curl http://circuitpython.local/
I get one or two updates every time:

>>> Service Adafruit FunHouse._circuitpython._tcp.local. updated
    cpy-1737d6-3.local.
>>> Service Adafruit FunHouse._circuitpython._tcp.local. updated
    cpy-1737d6-4.local.
>>> Service Adafruit FunHouse._circuitpython._tcp.local. updated
    cpy-1737d6-6.local.
>>> Service Adafruit FunHouse._circuitpython._tcp.local. updated
    cpy-1737d6-7.local.

This only seems to happen if there is more than one device.
If I add a third device, the number inflation seems to only happen to n - 1 devices.
One of the devices (the newest added apparently) seems just fine while the others add numbers.

+++ Service Adafruit Feather ESP32 V2._circuitpython._tcp.local. added
    cpy-d37674.local.

A few queries later:

>>> Service Adafruit QT Py ESP32C3._circuitpython._tcp.local. updated
    cpy-30d644-15.local.
>>> Service Adafruit FunHouse._circuitpython._tcp.local. updated
    cpy-1737d6-36.local.

@tannewt
Copy link
Member

tannewt commented Dec 1, 2022

Thank you both! I'll try again to replicate it today.

The collision should only happen if two devices share a hostname (which they shouldn't.) Instance names can have this happen too but it isn't critical for the API to work.

@tannewt
Copy link
Member

tannewt commented Dec 1, 2022

I think this change in the IDF will fix it: adafruit/esp-idf#9

Thanks to @anecdata for finding that it is caused by a collision of circuitpython.local causing the mac based hostname to be mangled.

dhalbert pushed a commit that referenced this issue Dec 2, 2022
cpy-MAC hostnames were being mangled on circuitpython.local
conflicts.

Fixes #6869
@anecdata
Copy link
Member Author

This issue of spontaneously-incrementing mDNS hostnames (e.g., "blahblah-4") appears to be back in CP 9. CP 8.2.10 seems fine. @DonutCat spotted it, and there's been discussion on Discord last evening and prior in #help-with-circuitpython.

Adafruit CircuitPython 9.1.4 on 2024-09-17; Adafruit QT Py ESP32-S3 4MB Flash 2MB PSRAM with ESP32S3

No code.py file.

settings.toml:

CIRCUITPY_WIFI_SSID="Redacted"
CIRCUITPY_WIFI_PASSWORD="Redacted"
CIRCUITPY_WEB_API_PASSWORD="passw0rd"

(and a similar file on another S3)

Device 34, after reset:

% curl -iL http://cpy-****34.local
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Length: 644
Content-Type: text/html

Device 84, after reset:

% curl -iL http://cpy-****84.local
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Length: 644
Content-Type: text/html

Try circuitpython.local:

% curl -iL http://circuitpython.local
HTTP/1.1 307 Temporary Redirect
Connection: close
Content-Length: 0
Location: http://cpy-****34.local/
Access-Control-Allow-Credentials: true
Vary: Origin, Accept, Upgrade
Access-Control-Allow-Origin: *

HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Length: 644
Content-Type: text/html

First time is fine, but after redirecting from circuitpython.local, the hostname has changed (though the redirect to the old name may still be cached... somewhere):

% curl -iL http://circuitpython.local
HTTP/1.1 307 Temporary Redirect
Connection: close
Content-Length: 0
Location: http://cpy-****34.local/
Access-Control-Allow-Credentials: true
Vary: Origin, Accept, Upgrade
Access-Control-Allow-Origin: *

curl: (6) Could not resolve host: cpy-****34.local

It's incremented occasionally even without visiting http://circuitpython.local.

Success to the incremented hostname:

% curl -iL http://cpy-****34-2.local
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Length: 644
Content-Type: text/html

Screenshot 2024-09-20 at 10 01 43 AM
Screenshot 2024-09-20 at 10 01 51 AM

Things get stranger if the hostname is changed in code.py, but that could be a second-order effect. Instance name also gets incremented.

@dhalbert
Copy link
Collaborator

make latexpdf worked with Sphinx 7.4.7. Then I updated did pip install --upgrade -r requirements-doc.txt, and Sphinx got upgraded to 8.1.0. Then make latexpdf failed. Checking further.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants