Skip to content

Commit

Permalink
Orders ipv6 addresses before v4 in resolution.
Browse files Browse the repository at this point in the history
When performing  mdns or dns resolution for a given hostname v4
addresses are always returned for the connection over v6 when an address
supports dual stacks.

This allows for ipv6 transitioning and testing in a dual stack
environment. It also follows common practice when picking the right
address to try first.

Not addressed by this commit is the ability to fall back to other
resolved addresses should the first address fail. This feature is
addressed by other todo's in the project.
  • Loading branch information
tlm committed Jul 4, 2022
1 parent b5a85fb commit e2f5524
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 22 deletions.
19 changes: 13 additions & 6 deletions aioesphomeapi/host_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ async def _async_resolve_host_zeroconf(
return []

addrs: List[AddrInfo] = []
for raw in info.addresses_by_version(zeroconf.IPVersion.All):
rawAddrs = info.addresses_by_version(
zeroconf.IPVersion.V6Only
) + info.addresses_by_version(zeroconf.IPVersion.V4Only)
for raw in rawAddrs:
is_ipv6 = len(raw) == 16
sockaddr: Sockaddr
if is_ipv6:
Expand Down Expand Up @@ -135,7 +138,8 @@ async def _async_resolve_host_getaddrinfo(host: str, port: int) -> List[AddrInfo
except OSError as err:
raise APIConnectionError(f"Error resolving IP address: {err}")

addrs: List[AddrInfo] = []
addrsV4: List[AddrInfo] = []
addrsV6: List[AddrInfo] = []
for family, type_, proto, _, raw in res:
sockaddr: Sockaddr
if family == socket.AF_INET:
Expand All @@ -152,10 +156,13 @@ async def _async_resolve_host_getaddrinfo(host: str, port: int) -> List[AddrInfo
# Unknown family
continue

addrs.append(
AddrInfo(family=family, type=type_, proto=proto, sockaddr=sockaddr)
)
return addrs
addrInfo = AddrInfo(family=family, type=type_, proto=proto, sockaddr=sockaddr)
if family == socket.AF_INET6:
addrsV6.append(addrInfo)
else:
addrsV4.append(addrInfo)

return addrsV6 + addrsV4


def _async_ip_address_to_addrs(host: str, port: int) -> List[AddrInfo]:
Expand Down
40 changes: 24 additions & 16 deletions tests/test_host_resolver.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import asyncio
import socket
from typing import List

import pytest
import zeroconf
from mock import AsyncMock, MagicMock, patch

import aioesphomeapi.host_resolver as hr
Expand All @@ -17,12 +19,6 @@ def async_zeroconf():
@pytest.fixture
def addr_infos():
return [
hr.AddrInfo(
family=socket.AF_INET,
type=socket.SOCK_STREAM,
proto=socket.IPPROTO_TCP,
sockaddr=hr.IPv4Sockaddr(address="10.0.0.42", port=6052),
),
hr.AddrInfo(
family=socket.AF_INET6,
type=socket.SOCK_STREAM,
Expand All @@ -34,16 +30,28 @@ def addr_infos():
scope_id=0,
),
),
hr.AddrInfo(
family=socket.AF_INET,
type=socket.SOCK_STREAM,
proto=socket.IPPROTO_TCP,
sockaddr=hr.IPv4Sockaddr(address="10.0.0.42", port=6052),
),
]


@pytest.mark.asyncio
async def test_resolve_host_zeroconf(async_zeroconf, addr_infos):
def address_for_version(version: zeroconf.IPVersion) -> List[bytes]:
if version == zeroconf.IPVersion.V6Only:
return [
b"\x20\x01\x0d\xb8\x85\xa3\x00\x00\x00\x00\x8a\x2e\x03\x70\x73\x34",
]
return [
b"\x0A\x00\x00\x2A",
]

info = MagicMock()
info.addresses_by_version.return_value = [
b"\n\x00\x00*",
b" \x01\r\xb8\x85\xa3\x00\x00\x00\x00\x8a.\x03ps4",
]
info.addresses_by_version.side_effect = address_for_version
async_zeroconf.async_get_service_info = AsyncMock(return_value=info)
async_zeroconf.async_close = AsyncMock()

Expand Down Expand Up @@ -71,18 +79,18 @@ async def test_resolve_host_getaddrinfo(event_loop, addr_infos):
with patch.object(event_loop, "getaddrinfo") as mock:
mock.return_value = [
(
socket.AF_INET,
socket.AF_INET6,
socket.SOCK_STREAM,
socket.IPPROTO_TCP,
"canon1",
("10.0.0.42", 6052),
"canon2",
("2001:db8:85a3::8a2e:370:7334", 6052, 0, 0),
),
(
socket.AF_INET6,
socket.AF_INET,
socket.SOCK_STREAM,
socket.IPPROTO_TCP,
"canon2",
("2001:db8:85a3::8a2e:370:7334", 6052, 0, 0),
"canon1",
("10.0.0.42", 6052),
),
(-1, socket.SOCK_STREAM, socket.IPPROTO_TCP, "canon3", ("10.0.0.42", 6052)),
]
Expand Down

0 comments on commit e2f5524

Please sign in to comment.