Skip to content

Commit 496ac44

Browse files
che0bdraco
andauthored
Ensure zeroconf can be loaded when the system disables IPv6 (#933)
Co-authored-by: J. Nick Koston <nick@koston.org>
1 parent 206671a commit 496ac44

8 files changed

Lines changed: 43 additions & 9 deletions

File tree

tests/services/test_info.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ def test_service_info_rejects_expired_records(self):
192192
assert info.properties[b"ci"] == b"2"
193193
zc.close()
194194

195+
@unittest.skipIf(not has_working_ipv6(), 'Requires IPv6')
196+
@unittest.skipIf(os.environ.get('SKIP_IPV6'), 'IPv6 tests disabled')
195197
def test_get_info_partial(self):
196198

197199
zc = r.Zeroconf(interfaces=['127.0.0.1'])
@@ -576,6 +578,8 @@ async def test_multiple_a_addresses():
576578
await aiozc.async_close()
577579

578580

581+
@unittest.skipIf(not has_working_ipv6(), 'Requires IPv6')
582+
@unittest.skipIf(os.environ.get('SKIP_IPV6'), 'IPv6 tests disabled')
579583
def test_filter_address_by_type_from_service_info():
580584
"""Verify dns_addresses can filter by ipversion."""
581585
desc = {'path': '/~paulsm/'}

tests/test_asyncio.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import asyncio
77
import logging
8+
import os
89
import socket
910
import time
1011
import threading
@@ -32,7 +33,7 @@
3233
from zeroconf._services.info import ServiceInfo
3334
from zeroconf._utils.time import current_time_millis
3435

35-
from . import _clear_cache
36+
from . import _clear_cache, has_working_ipv6
3637

3738
log = logging.getLogger('zeroconf')
3839
original_logging_level = logging.NOTSET
@@ -349,6 +350,9 @@ async def test_async_wait_unblocks_on_update() -> None:
349350
@pytest.mark.asyncio
350351
async def test_service_info_async_request() -> None:
351352
"""Test registering services broadcasts and query with AsyncServceInfo.async_request."""
353+
if not has_working_ipv6() or os.environ.get('SKIP_IPV6'):
354+
pytest.skip('Requires IPv6')
355+
352356
aiozc = AsyncZeroconf(interfaces=['127.0.0.1'])
353357
type_ = "_test1-srvc-type._tcp.local."
354358
name = "xxxyyy"

tests/test_dns.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
""" Unit tests for zeroconf._dns. """
55

66
import logging
7+
import os
78
import socket
89
import time
910
import unittest
@@ -18,6 +19,8 @@
1819
ServiceInfo,
1920
)
2021

22+
from . import has_working_ipv6
23+
2124
log = logging.getLogger('zeroconf')
2225
original_logging_level = logging.NOTSET
2326

@@ -52,6 +55,8 @@ def test_dns_pointer_repr(self):
5255
pointer = r.DNSPointer('irrelevant', const._TYPE_PTR, const._CLASS_IN, const._DNS_OTHER_TTL, '123')
5356
repr(pointer)
5457

58+
@unittest.skipIf(not has_working_ipv6(), 'Requires IPv6')
59+
@unittest.skipIf(os.environ.get('SKIP_IPV6'), 'IPv6 tests disabled')
5560
def test_dns_address_repr(self):
5661
address = r.DNSAddress('irrelevant', const._TYPE_SOA, const._CLASS_IN, 1, b'a')
5762
assert repr(address).endswith("b'a'")

tests/test_handlers.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import asyncio
77
import logging
8+
import os
89
import pytest
910
import socket
1011
import time
@@ -19,7 +20,7 @@
1920
from zeroconf.asyncio import AsyncZeroconf
2021

2122

22-
from . import _clear_cache, _inject_response
23+
from . import _clear_cache, _inject_response, has_working_ipv6
2324

2425
log = logging.getLogger('zeroconf')
2526
original_logging_level = logging.NOTSET
@@ -274,6 +275,8 @@ def test_ptr_optimization():
274275
zc.close()
275276

276277

278+
@unittest.skipIf(not has_working_ipv6(), 'Requires IPv6')
279+
@unittest.skipIf(os.environ.get('SKIP_IPV6'), 'IPv6 tests disabled')
277280
def test_any_query_for_ptr():
278281
"""Test that queries for ANY will return PTR records."""
279282
zc = Zeroconf(interfaces=['127.0.0.1'])
@@ -301,6 +304,8 @@ def test_any_query_for_ptr():
301304
zc.close()
302305

303306

307+
@unittest.skipIf(not has_working_ipv6(), 'Requires IPv6')
308+
@unittest.skipIf(os.environ.get('SKIP_IPV6'), 'IPv6 tests disabled')
304309
def test_aaaa_query():
305310
"""Test that queries for AAAA records work."""
306311
zc = Zeroconf(interfaces=['127.0.0.1'])
@@ -326,6 +331,8 @@ def test_aaaa_query():
326331
zc.close()
327332

328333

334+
@unittest.skipIf(not has_working_ipv6(), 'Requires IPv6')
335+
@unittest.skipIf(os.environ.get('SKIP_IPV6'), 'IPv6 tests disabled')
329336
def test_a_and_aaaa_record_fate_sharing():
330337
"""Test that queries for AAAA always return A records in the additionals."""
331338
zc = Zeroconf(interfaces=['127.0.0.1'])

tests/test_protocol.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import copy
77
import logging
8+
import os
89
import socket
910
import struct
1011
import unittest
@@ -18,6 +19,8 @@
1819
DNSText,
1920
)
2021

22+
from . import has_working_ipv6
23+
2124
log = logging.getLogger('zeroconf')
2225
original_logging_level = logging.NOTSET
2326

@@ -468,6 +471,8 @@ def test_incoming_circular_reference(self):
468471
)
469472
).valid
470473

474+
@unittest.skipIf(not has_working_ipv6(), 'Requires IPv6')
475+
@unittest.skipIf(os.environ.get('SKIP_IPV6'), 'IPv6 tests disabled')
471476
def test_incoming_ipv6(self):
472477
addr = "2606:2800:220:1:248:1893:25c8:1946" # example.com
473478
packed = socket.inet_pton(socket.AF_INET6, addr)

tests/utils/test_net.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ def test_add_multicast_member():
190190
with patch("socket.socket.setsockopt", side_effect=OSError(errno.ENODEV, None)):
191191
assert netutils.add_multicast_member(sock, ('2001:db8::', 1, 1)) is False
192192

193+
# No IPv6 support should return False for IPv6
194+
with patch("socket.inet_pton", side_effect=OSError()):
195+
assert netutils.add_multicast_member(sock, ('2001:db8::', 1, 1)) is False
196+
193197
# No error should return True
194198
with patch("socket.socket.setsockopt"):
195199
assert netutils.add_multicast_member(sock, interface) is True

zeroconf/_utils/net.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import ifaddr
3232

3333
from .._logger import log
34-
from ..const import _IPPROTO_IPV6, _MDNS_ADDR6_BYTES, _MDNS_ADDR_BYTES, _MDNS_PORT
34+
from ..const import _IPPROTO_IPV6, _MDNS_ADDR, _MDNS_ADDR6, _MDNS_PORT
3535

3636

3737
@enum.unique
@@ -259,11 +259,20 @@ def add_multicast_member(
259259
log.debug('Adding %r (socket %d) to multicast group', interface, listen_socket.fileno())
260260
try:
261261
if is_v6:
262+
try:
263+
mdns_addr6_bytes = socket.inet_pton(socket.AF_INET6, _MDNS_ADDR6)
264+
except OSError:
265+
log.info(
266+
'Unable to translate IPv6 address when adding %s to multicast group, '
267+
'this can happen if IPv6 is disabled on the system',
268+
interface,
269+
)
270+
return False
262271
iface_bin = struct.pack('@I', cast(int, interface[1]))
263-
_value = _MDNS_ADDR6_BYTES + iface_bin
272+
_value = mdns_addr6_bytes + iface_bin
264273
listen_socket.setsockopt(_IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, _value)
265274
else:
266-
_value = _MDNS_ADDR_BYTES + socket.inet_aton(cast(str, interface))
275+
_value = socket.inet_aton(_MDNS_ADDR) + socket.inet_aton(cast(str, interface))
267276
listen_socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, _value)
268277
except socket.error as e:
269278
_errno = get_errno(e)

zeroconf/const.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
USA
2121
"""
2222

23-
import contextlib
2423
import re
2524
import socket
2625

@@ -44,10 +43,7 @@
4443
# Some DNS constants
4544

4645
_MDNS_ADDR = '224.0.0.251'
47-
_MDNS_ADDR_BYTES = socket.inet_aton(_MDNS_ADDR)
4846
_MDNS_ADDR6 = 'ff02::fb'
49-
with contextlib.suppress(OSError): # can't use AF_INET6, IPv6 is disabled
50-
_MDNS_ADDR6_BYTES = socket.inet_pton(socket.AF_INET6, _MDNS_ADDR6)
5147
_MDNS_PORT = 5353
5248
_DNS_PORT = 53
5349
_DNS_HOST_TTL = 120 # two minute for host records (A, SRV etc) as-per RFC6762

0 commit comments

Comments
 (0)