Prevent ipv6 discovery messages for Sonos (#139648)

This commit is contained in:
Pete Sage 2025-03-12 15:19:09 -04:00 committed by GitHub
parent bad109dec5
commit 1f6658fca0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 58 additions and 1 deletions

View File

@ -7,6 +7,7 @@ from collections import OrderedDict
from dataclasses import dataclass, field from dataclasses import dataclass, field
import datetime import datetime
from functools import partial from functools import partial
from ipaddress import AddressValueError, IPv4Address
import logging import logging
import socket import socket
from typing import Any, cast from typing import Any, cast
@ -208,6 +209,14 @@ class SonosDiscoveryManager:
async def async_subscribe_to_zone_updates(self, ip_address: str) -> None: async def async_subscribe_to_zone_updates(self, ip_address: str) -> None:
"""Test subscriptions and create SonosSpeakers based on results.""" """Test subscriptions and create SonosSpeakers based on results."""
try:
_ = IPv4Address(ip_address)
except AddressValueError:
_LOGGER.debug(
"Sonos integration only supports IPv4 addresses, invalid ip_address received: %s",
ip_address,
)
return
soco = SoCo(ip_address) soco = SoCo(ip_address)
# Cache now to avoid household ID lookup during first ZoneGroupState processing # Cache now to avoid household ID lookup during first ZoneGroupState processing
await self.hass.async_add_executor_job( await self.hass.async_add_executor_job(

View File

@ -31,6 +31,8 @@ class SonosDiscoveryFlowHandler(DiscoveryFlowHandler[Awaitable[bool]], domain=DO
hostname = discovery_info.hostname hostname = discovery_info.hostname
if hostname is None or not hostname.lower().startswith("sonos"): if hostname is None or not hostname.lower().startswith("sonos"):
return self.async_abort(reason="not_sonos_device") return self.async_abort(reason="not_sonos_device")
if discovery_info.ip_address.version != 4:
return self.async_abort(reason="not_ipv4_address")
if discovery_manager := self.hass.data.get(DATA_SONOS_DISCOVERY_MANAGER): if discovery_manager := self.hass.data.get(DATA_SONOS_DISCOVERY_MANAGER):
host = discovery_info.host host = discovery_info.host
mdns_name = discovery_info.name mdns_name = discovery_info.name

View File

@ -8,7 +8,8 @@
"abort": { "abort": {
"not_sonos_device": "Discovered device is not a Sonos device", "not_sonos_device": "Discovered device is not a Sonos device",
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]", "single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]" "no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
"not_ipv4_address": "No IPv4 address in SSDP discovery information"
} }
}, },
"issues": { "issues": {

View File

@ -123,6 +123,22 @@ async def test_zeroconf_form(
assert len(mock_manager.mock_calls) == 2 assert len(mock_manager.mock_calls) == 2
async def test_zeroconf_form_not_ipv4(
hass: HomeAssistant, zeroconf_payload: ZeroconfServiceInfo
) -> None:
"""Test we pass Zeroconf discoveries to the manager."""
mock_manager = hass.data[DATA_SONOS_DISCOVERY_MANAGER] = MagicMock()
zeroconf_payload.ip_address = ip_address("2001:db8:3333:4444:5555:6666:7777:8888")
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_ZEROCONF},
data=zeroconf_payload,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "not_ipv4_address"
assert mock_manager.call_count == 0
async def test_ssdp_discovery(hass: HomeAssistant, soco) -> None: async def test_ssdp_discovery(hass: HomeAssistant, soco) -> None:
"""Test that SSDP discoveries create a config flow.""" """Test that SSDP discoveries create a config flow."""

View File

@ -455,3 +455,32 @@ async def test_async_poll_manual_hosts_8(
assert "media_player.garage" in entity_registry.entities assert "media_player.garage" in entity_registry.entities
assert "media_player.studio" in entity_registry.entities assert "media_player.studio" in entity_registry.entities
await hass.async_block_till_done(wait_background_tasks=True) await hass.async_block_till_done(wait_background_tasks=True)
async def _setup_hass_ipv6_address_not_supported(hass: HomeAssistant):
await async_setup_component(
hass,
sonos.DOMAIN,
{
"sonos": {
"media_player": {
"interface_addr": "127.0.0.1",
"hosts": ["2001:db8:3333:4444:5555:6666:7777:8888"],
}
}
},
)
await hass.async_block_till_done()
async def test_ipv6_not_supported(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Tests that invalid ipv4 addresses do not generate stack dump."""
with caplog.at_level(logging.DEBUG):
caplog.clear()
await _setup_hass_ipv6_address_not_supported(hass)
await hass.async_block_till_done()
assert "invalid ip_address received" in caplog.text
assert "2001:db8:3333:4444:5555:6666:7777:8888" in caplog.text