From fd1c0635560304490704ef7a6677bff4fddbcdfc Mon Sep 17 00:00:00 2001 From: Timothee 'TTimo' Besset Date: Sun, 25 Apr 2021 22:40:20 -1000 Subject: [PATCH 1/2] Skip socket creation if add_multicast_member fails (windows) Fixes a regression caused by refactoring error in #331 --- zeroconf/__init__.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/zeroconf/__init__.py b/zeroconf/__init__.py index d8890565..a66797d6 100644 --- a/zeroconf/__init__.py +++ b/zeroconf/__init__.py @@ -2255,7 +2255,7 @@ def new_socket( def add_multicast_member( listen_socket: socket.socket, interface: Union[str, Tuple[Tuple[str, int, int], int]], -) -> None: +) -> bool: # This is based on assumptions in normalize_interface_choice is_v6 = isinstance(interface, tuple) err_einval = {errno.EINVAL} @@ -2279,19 +2279,20 @@ def add_multicast_member( 'it is expected to happen on some systems', interface, ) - return None + return False elif _errno == errno.EADDRNOTAVAIL: log.info( 'Address not available when adding %s to multicast ' 'group, it is expected to happen on some systems', interface, ) - return None + return False elif _errno in err_einval: log.info('Interface of %s does not support multicast, ' 'it is expected in WSL', interface) - return None + return False else: raise + return True def new_respond_socket( @@ -2339,8 +2340,10 @@ def create_sockets( for i in normalized_interfaces: if not unicast: - add_multicast_member(cast(socket.socket, listen_socket), i) - respond_socket = new_respond_socket(i, apple_p2p=apple_p2p) + if add_multicast_member(cast(socket.socket, listen_socket), i): + respond_socket = new_respond_socket(i, apple_p2p=apple_p2p) + else: + respond_socket = None else: respond_socket = new_socket( port=0, From cc2405f9091fe5297ecbc54183394b67c0529798 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 28 Apr 2021 07:19:16 -1000 Subject: [PATCH 2/2] Ensure known socket errors cause add_multicast_member to return False --- zeroconf/test.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/zeroconf/test.py b/zeroconf/test.py index fce2512f..1feb598c 100644 --- a/zeroconf/test.py +++ b/zeroconf/test.py @@ -5,6 +5,7 @@ """ Unit tests for zeroconf.py """ import copy +import errno import logging import os import platform @@ -15,8 +16,7 @@ import unittest import unittest.mock from threading import Event -from typing import Dict, Optional # noqa # used in type hints -from typing import cast +from typing import Dict, Optional, cast # noqa # used in type hints import pytest @@ -2130,3 +2130,18 @@ def test_dns_compression_rollback_for_corruption(): # ensure there is no corruption with the dns compression incoming = r.DNSIncoming(packet) assert incoming.valid is True + + +@pytest.mark.parametrize( + "errno,expected_result", + [(errno.EADDRINUSE, False), (errno.EADDRNOTAVAIL, False), (errno.EINVAL, False), (0, True)], +) +def test_add_multicast_member_socket_errors(errno, expected_result): + """Test we handle socket errors when adding multicast members.""" + if errno: + setsockopt_mock = unittest.mock.Mock(side_effect=OSError(errno, "Error: {}".format(errno))) + else: + setsockopt_mock = unittest.mock.Mock() + fileno_mock = unittest.mock.PropertyMock(return_value=10) + socket_mock = unittest.mock.Mock(setsockopt=setsockopt_mock, fileno=fileno_mock) + assert r.add_multicast_member(socket_mock, "0.0.0.0") == expected_result