From 4d0e236c6ac9c13c7f7d59dddd7b13dc3cf854bf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 4 Feb 2023 16:29:03 -0600 Subject: [PATCH] Ignore invalid zeroconf names from devices with broken firmwares (#87414) --- homeassistant/components/zeroconf/__init__.py | 15 +++++++- tests/components/zeroconf/test_init.py | 38 ++++++++++++++++++- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zeroconf/__init__.py b/homeassistant/components/zeroconf/__init__.py index 6b54aa18961..088e46aa1cb 100644 --- a/homeassistant/components/zeroconf/__init__.py +++ b/homeassistant/components/zeroconf/__init__.py @@ -15,7 +15,12 @@ import sys from typing import Any, Final, cast import voluptuous as vol -from zeroconf import InterfaceChoice, IPVersion, ServiceStateChange +from zeroconf import ( + BadTypeInNameException, + InterfaceChoice, + IPVersion, + ServiceStateChange, +) from zeroconf.asyncio import AsyncServiceInfo from homeassistant import config_entries @@ -399,7 +404,13 @@ class ZeroconfDiscovery: self, zeroconf: HaZeroconf, service_type: str, name: str ) -> None: """Process a zeroconf update.""" - async_service_info = AsyncServiceInfo(service_type, name) + try: + async_service_info = AsyncServiceInfo(service_type, name) + except BadTypeInNameException as ex: + # Some devices broadcast a name that is not a valid DNS name + # This is a bug in the device firmware and we should ignore it + _LOGGER.debug("Bad name in zeroconf record: %s: %s", name, ex) + return await async_service_info.async_request(zeroconf, 3000) info = info_from_service(async_service_info) diff --git a/tests/components/zeroconf/test_init.py b/tests/components/zeroconf/test_init.py index 5175c9715da..ae461a5ca0f 100644 --- a/tests/components/zeroconf/test_init.py +++ b/tests/components/zeroconf/test_init.py @@ -3,7 +3,12 @@ from ipaddress import ip_address from typing import Any from unittest.mock import call, patch -from zeroconf import InterfaceChoice, IPVersion, ServiceStateChange +from zeroconf import ( + BadTypeInNameException, + InterfaceChoice, + IPVersion, + ServiceStateChange, +) from zeroconf.asyncio import AsyncServiceInfo from homeassistant.components import zeroconf @@ -564,6 +569,37 @@ async def test_homekit_match_partial_space(hass, mock_async_zeroconf): } +async def test_device_with_invalid_name(hass, mock_async_zeroconf, caplog): + """Test we ignore devices with an invalid name.""" + with patch.dict( + zc_gen.ZEROCONF, + {"_hap._tcp.local.": [{"domain": "homekit_controller"}]}, + clear=True, + ), patch.dict( + zc_gen.HOMEKIT, + {"LIFX": "lifx"}, + clear=True, + ), patch.object( + hass.config_entries.flow, "async_init" + ) as mock_config_flow, patch.object( + zeroconf, + "HaAsyncServiceBrowser", + side_effect=lambda *args, **kwargs: service_update_mock( + *args, **kwargs, limit_service="_hap._tcp.local." + ), + ) as mock_service_browser, patch( + "homeassistant.components.zeroconf.AsyncServiceInfo", + side_effect=BadTypeInNameException, + ): + assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}}) + hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) + await hass.async_block_till_done() + + assert len(mock_service_browser.mock_calls) == 1 + assert len(mock_config_flow.mock_calls) == 0 + assert "Bad name in zeroconf record" in caplog.text + + async def test_homekit_match_partial_dash(hass, mock_async_zeroconf): """Test configured options for a device are loaded via config entry.""" with patch.dict(