fix: degrade getsockopt read failures instead of aborting · python-zeroconf/python-zeroconf@37f6b1a · GitHub
Skip to content

Commit 37f6b1a

Browse files
committed
fix: degrade getsockopt read failures instead of aborting
make_wrapped_transport runs on the startup/connection path; re-raising a failed IPV6_MULTICAST_IF read there could abort instance startup to protect multicast_index, which only selects the interface for a benign group leave that drop_multicast_member already tolerates. Fall back to the default index with a debug log instead. Apply the same graceful fallback to _listen_socket_supports (assume dual-stack) so a getsockopt failure can't abort a rescan, and the two paths agree. Soften the _core config-commit comment, which overstated the invariant on a partial reconcile failure.
1 parent 4086ae2 commit 37f6b1a

4 files changed

Lines changed: 26 additions & 33 deletions

File tree

src/zeroconf/_core.py

Lines changed: 2 additions & 1 deletion

src/zeroconf/_engine.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
import asyncio
2626
import itertools
2727
import socket
28-
import sys
2928
import threading
3029
from typing import TYPE_CHECKING, cast
3130

31+
from ._logger import log
3232
from ._record_update import RecordUpdate
3333
from ._utils.asyncio import get_running_loop, run_coro_with_timeout
3434
from ._utils.net import (
@@ -79,13 +79,12 @@ def _listen_socket_supports(
7979
supported = True
8080
try:
8181
supported = not listen_socket.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY)
82-
except OSError:
83-
# Windows rejects reading IPV6_V6ONLY on some sockets; assume supported
84-
# there (consistent with make_wrapped_transport) so a read failure can't
85-
# drive a rebuild loop. Elsewhere the read does not fail, so surface a
86-
# genuine error rather than mask an unreceivable family as supported.
87-
if sys.platform != "win32":
88-
raise
82+
except OSError as exc:
83+
# Reading IPV6_V6ONLY can fail (Windows rejects it on some sockets;
84+
# other platforms shouldn't). Assume dual-stack so a read failure can't
85+
# abort the rescan; at worst this skips a rebuild the next reconcile
86+
# re-evaluates, consistent with make_wrapped_transport's fallback.
87+
log.debug("Unable to read IPV6_V6ONLY, assuming dual-stack: %s", exc)
8988
return supported
9089

9190

src/zeroconf/_transport.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@
2424

2525
import asyncio
2626
import socket
27-
import sys
2827
from typing import cast
2928

29+
from ._logger import log
30+
3031

3132
def _strip_zone(address: str) -> str:
3233
"""Drop a ``%zone`` suffix from an IPv6 address string."""
@@ -103,17 +104,14 @@ def make_wrapped_transport(transport: asyncio.DatagramTransport) -> _WrappedTran
103104
if is_ipv6:
104105
# IPV6_MULTICAST_IF holds the interface index new_respond_socket
105106
# joined the group with; capture it so a later group leave uses the
106-
# same index. Windows rejects reading the option (WSAEINVAL); there
107-
# the leave falls back to the default interface as it did before.
107+
# same index. This is on the startup/connection path, and the index
108+
# only selects the interface for a future (benign) group leave, so a
109+
# read failure (Windows rejects it with WSAEINVAL; other platforms
110+
# shouldn't) keeps the default index 0 rather than aborting setup.
108111
try:
109112
multicast_index = sock.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF)
110-
except OSError:
111-
# Windows rejects reading IPV6_MULTICAST_IF (WSAEINVAL); the
112-
# default index 0 set above is kept there. On other platforms this
113-
# read does not fail, so re-raise rather than mask a genuine error
114-
# into a wrong-interface group leave.
115-
if sys.platform != "win32":
116-
raise
113+
except OSError as exc:
114+
log.debug("Unable to read IPV6_MULTICAST_IF, using default index 0: %s", exc)
117115
return _WrappedTransport(
118116
transport=transport,
119117
is_ipv6=is_ipv6,

tests/test_interface_update.py

Lines changed: 9 additions & 14 deletions

0 commit comments

Comments
 (0)