-
-
Notifications
You must be signed in to change notification settings - Fork 126
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -14,6 +14,9 @@ | |||||||||||||
|
|
||||||||||||||
| _LOGGER = logging.getLogger(__name__) | ||||||||||||||
|
|
||||||||||||||
| # Private RFC1918 target used only to select a default route. | ||||||||||||||
| _DEFAULT_ROUTE_PROBE = ("10.255.255.1", 1) | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| def unused_port() -> int: | ||||||||||||||
| """Return a port that is unused on the current host.""" | ||||||||||||||
|
|
@@ -47,6 +50,26 @@ def mcast_socket(address: Optional[str], port: int = 0) -> socket.socket: | |||||||||||||
| with suppress(OSError): | ||||||||||||||
| membership = socket.inet_aton("224.0.0.251") + socket.inet_aton(address) | ||||||||||||||
| sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, membership) | ||||||||||||||
| else: | ||||||||||||||
| # 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) | ||||||||||||||
| detected = probe.getsockname()[0] | ||||||||||||||
| sock.setsockopt( | ||||||||||||||
| socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(detected) | ||||||||||||||
| ) | ||||||||||||||
| membership = socket.inet_aton("224.0.0.251") + socket.inet_aton( | ||||||||||||||
| detected | ||||||||||||||
| ) | ||||||||||||||
| sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, membership) | ||||||||||||||
| except OSError: | ||||||||||||||
| with suppress(OSError): | ||||||||||||||
| membership = socket.inet_aton("224.0.0.251") + socket.inet_aton( | ||||||||||||||
| "0.0.0.0" | ||||||||||||||
| ) | ||||||||||||||
| sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, membership) | ||||||||||||||
|
|
||||||||||||||
| _LOGGER.debug("Binding on %s:%d", address or "*", port) | ||||||||||||||
| sock.bind((address or "", port)) | ||||||||||||||
|
|
@@ -74,6 +97,17 @@ def get_private_addresses(include_loopback=True) -> List[IPv4Address]: | |||||||||||||
| if ipaddr.is_private: | ||||||||||||||
| addresses.append(ipaddr) | ||||||||||||||
|
|
||||||||||||||
| # Fallback when no adapters are reported (e.g. sandboxed platforms). | ||||||||||||||
| if not addresses: | ||||||||||||||
| try: | ||||||||||||||
|
Comment on lines
+100
to
+102
|
||||||||||||||
| with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as probe: | ||||||||||||||
| probe.connect(_DEFAULT_ROUTE_PROBE) | ||||||||||||||
|
Comment on lines
+100
to
+104
|
||||||||||||||
| detected = IPv4Address(probe.getsockname()[0]) | ||||||||||||||
| if include_loopback or not detected.is_loopback: | ||||||||||||||
|
||||||||||||||
| if include_loopback or not detected.is_loopback: | |
| if detected.is_private or (detected.is_loopback and include_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.
'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 | |
| ) |
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_socketandget_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.