Ensure zeroconf uses the newest non-link local address in discovery (#58257)

This commit is contained in:
J. Nick Koston 2021-10-23 08:50:19 -10:00 committed by GitHub
parent 50e0c58310
commit 5958e6a3f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 62 additions and 10 deletions

View File

@ -469,13 +469,15 @@ def info_from_service(service: AsyncServiceInfo) -> HaServiceInfo | None:
if isinstance(value, bytes):
properties[key] = value.decode("utf-8")
if not service.addresses:
addresses = service.addresses
if not addresses:
return None
if (host := _first_non_link_local_or_v6_address(addresses)) is None:
return None
address = service.addresses[0]
return {
"host": str(ip_address(address)),
"host": str(host),
"port": service.port,
"hostname": service.server,
"type": service.type,
@ -484,6 +486,15 @@ def info_from_service(service: AsyncServiceInfo) -> HaServiceInfo | None:
}
def _first_non_link_local_or_v6_address(addresses: list[bytes]) -> str | None:
"""Return the first ipv6 or non-link local ipv4 address."""
for address in addresses:
ip_addr = ip_address(address)
if not ip_addr.is_link_local or ip_addr.version == 6:
return str(ip_addr)
return None
def _suppress_invalid_properties(properties: dict) -> None:
"""Suppress any properties that will cause zeroconf to fail to startup."""

View File

@ -2,7 +2,7 @@
"domain": "zeroconf",
"name": "Zero-configuration networking (zeroconf)",
"documentation": "https://www.home-assistant.io/integrations/zeroconf",
"requirements": ["zeroconf==0.36.8"],
"requirements": ["zeroconf==0.36.9"],
"dependencies": ["network", "api"],
"codeowners": ["@bdraco"],
"quality_scale": "internal",

View File

@ -32,7 +32,7 @@ sqlalchemy==1.4.23
voluptuous-serialize==2.4.0
voluptuous==0.12.2
yarl==1.6.3
zeroconf==0.36.8
zeroconf==0.36.9
pycryptodome>=3.6.6

View File

@ -2465,7 +2465,7 @@ youtube_dl==2021.06.06
zengge==0.2
# homeassistant.components.zeroconf
zeroconf==0.36.8
zeroconf==0.36.9
# homeassistant.components.zha
zha-quirks==0.0.62

View File

@ -1427,7 +1427,7 @@ yeelight==0.7.8
youless-api==0.14
# homeassistant.components.zeroconf
zeroconf==0.36.8
zeroconf==0.36.9
# homeassistant.components.zha
zha-quirks==0.0.62

View File

@ -1,5 +1,6 @@
"""Test Zeroconf component setup process."""
from ipaddress import ip_address
from typing import Any
from unittest.mock import call, patch
from zeroconf import InterfaceChoice, IPVersion, ServiceStateChange
@ -39,7 +40,9 @@ def service_update_mock(ipv6, zeroconf, services, handlers, *, limit_service=Non
handlers[0](zeroconf, service, f"_name.{service}", ServiceStateChange.Added)
def get_service_info_mock(service_type, name, *args, **kwargs):
def get_service_info_mock(
service_type: str, name: str, *args: Any, **kwargs: Any
) -> AsyncServiceInfo:
"""Return service info for get_service_info."""
return AsyncServiceInfo(
service_type,
@ -53,7 +56,9 @@ def get_service_info_mock(service_type, name, *args, **kwargs):
)
def get_service_info_mock_without_an_address(service_type, name):
def get_service_info_mock_without_an_address(
service_type: str, name: str
) -> AsyncServiceInfo:
"""Return service info for get_service_info without any addresses."""
return AsyncServiceInfo(
service_type,
@ -633,6 +638,42 @@ async def test_info_from_service_with_addresses(hass):
assert info is None
async def test_info_from_service_with_link_local_address_first(hass):
"""Test that the link local address is ignored."""
service_type = "_test._tcp.local."
service_info = get_service_info_mock(service_type, f"test.{service_type}")
service_info.addresses = ["169.254.12.3", "192.168.66.12"]
info = zeroconf.info_from_service(service_info)
assert info["host"] == "192.168.66.12"
async def test_info_from_service_with_link_local_address_second(hass):
"""Test that the link local address is ignored."""
service_type = "_test._tcp.local."
service_info = get_service_info_mock(service_type, f"test.{service_type}")
service_info.addresses = ["192.168.66.12", "169.254.12.3"]
info = zeroconf.info_from_service(service_info)
assert info["host"] == "192.168.66.12"
async def test_info_from_service_with_link_local_address_only(hass):
"""Test that the link local address is ignored."""
service_type = "_test._tcp.local."
service_info = get_service_info_mock(service_type, f"test.{service_type}")
service_info.addresses = ["169.254.12.3"]
info = zeroconf.info_from_service(service_info)
assert info is None
async def test_info_from_service_prefers_ipv4(hass):
"""Test that ipv4 addresses are preferred."""
service_type = "_test._tcp.local."
service_info = get_service_info_mock(service_type, f"test.{service_type}")
service_info.addresses = ["2001:db8:3333:4444:5555:6666:7777:8888", "192.168.66.12"]
info = zeroconf.info_from_service(service_info)
assert info["host"] == "192.168.66.12"
async def test_get_instance(hass, mock_async_zeroconf):
"""Test we get an instance."""
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})