-
-
Notifications
You must be signed in to change notification settings - Fork 119
fix(net): fallback multicast join without adapters #2791
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
base: master
Are you sure you want to change the base?
Conversation
f96f17a to
91a5489
Compare
ifaddr may return no adapters in sandboxed environments, which prevents mDNS multicast membership and leads to empty scan results. Detect a default IPv4 via a dummy UDP connect and use it for IP_MULTICAST_IF/ADD_MEMBERSHIP while keeping the socket bind on an empty string to avoid conflicts. Reuse the same probe fallback in get_private_addresses so callers still obtain a usable IPv4 when adapter enumeration is empty.
|
Applied fix to avoid public IP for route probe; pushed commit ae6e10e to Skyline-23:master. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Improves behavior on sandboxed platforms where ifaddr.get_adapters() can return no interfaces by probing the default local IPv4 via a dummy UDP connect and using it for interface selection and multicast membership.
Changes:
- Add a default-route probe target constant and use a UDP “connect” to infer the default local IPv4 when adapters are unavailable.
- Enhance
mcast_socketto attempt multicast membership using the probed interface (and fall back to0.0.0.0membership if probing fails). - Add a
get_private_addressesfallback path to return the probed IPv4 when no addresses were found.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Fallback when no adapters are reported (e.g. sandboxed platforms). | ||
| if not addresses: | ||
| try: |
Copilot
AI
Jan 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get_private_addresses fallback runs whenever addresses is empty, which also covers cases where adapters exist but only have public IPv4s. That expands behavior beyond the PR description (“when no adapters are reported”) and changes previous semantics for public-only hosts. Consider storing adapters = get_adapters() and only running the UDP-probe fallback when not adapters (or when no IPv4 addresses were enumerated).
| with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as probe: | ||
| probe.connect(_DEFAULT_ROUTE_PROBE) | ||
| detected = IPv4Address(probe.getsockname()[0]) | ||
| if include_loopback or not detected.is_loopback: |
Copilot
AI
Jan 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the get_private_addresses fallback, the detected address is appended without checking detected.is_private. This can cause the function to return a globally-routable IP, contradicting the docstring/previous behavior (only ipaddr.is_private values were returned). Apply the same is_private filter in the fallback path (while still honoring include_loopback).
| if include_loopback or not detected.is_loopback: | |
| if detected.is_private or (detected.is_loopback and include_loopback): |
| # Fallback for sandboxed platforms where ifaddr finds no adapters: | ||
| # detect default IPv4 via a dummy UDP connect and use it for multicast join. | ||
| try: | ||
| with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as probe: | ||
| probe.connect(_DEFAULT_ROUTE_PROBE) |
Copilot
AI
Jan 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The UDP “default route probe” logic is duplicated in both mcast_socket and get_private_addresses. To avoid the two call sites drifting (e.g., different exception handling or filtering), consider extracting a small helper like _get_default_ipv4() and reusing it in both places.
| # Fallback when no adapters are reported (e.g. sandboxed platforms). | ||
| if not addresses: | ||
| try: | ||
| with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as probe: | ||
| probe.connect(_DEFAULT_ROUTE_PROBE) |
Copilot
AI
Jan 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new fallback changes observable behavior when get_adapters() returns an empty list (existing unit test tests/support/test_net.py::test_no_address currently expects []). Add/update tests to mock the UDP probe socket (connect/getsockname) so the fallback is deterministic and validated (both success and OSError paths).
| except OSError: | ||
| pass |
Copilot
AI
Jan 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'except' clause does nothing but pass and there is no explanatory comment.
| except OSError: | |
| pass | |
| except OSError as ex: | |
| _LOGGER.debug( | |
| "Unable to determine fallback local address via UDP probe: %s", ex | |
| ) |
This pull request improves network interface detection and multicast socket setup for sandboxed platforms where
ifaddr may return no adapters. When adapters are present, behavior is unchanged. The fallback uses a dummy UDP
connect (no traffic sent) solely to learn the default local IPv4.
Network interface detection improvements
loopback filtering rules.
Multicast socket setup enhancements
membership attempt if probing fails.