Skip to content

Commit 37edde2

Browse files
authored
fix: accept uppercase .local. trailer in service_type_name (#1747)
1 parent 0e201f7 commit 37edde2

2 files changed

Lines changed: 26 additions & 2 deletions

File tree

src/zeroconf/_utils/name.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ def service_type_name(type_: str, *, strict: bool = True) -> str: # pylint: dis
8383
# https://datatracker.ietf.org/doc/html/rfc6763#section-7.2
8484
raise BadTypeInNameException(f"Full name ({type_}) must be > 256 bytes")
8585

86-
if type_.endswith((_TCP_PROTOCOL_LOCAL_TRAILER, _NONTCP_PROTOCOL_LOCAL_TRAILER)):
86+
# RFC 1035 §2.3.3 / RFC 6762 §16 — DNS name comparisons are case-insensitive.
87+
type_lower = type_.lower()
88+
if type_lower.endswith((_TCP_PROTOCOL_LOCAL_TRAILER, _NONTCP_PROTOCOL_LOCAL_TRAILER)):
8789
remaining = type_[: -len(_TCP_PROTOCOL_LOCAL_TRAILER)].split(".")
8890
trailer = type_[-len(_TCP_PROTOCOL_LOCAL_TRAILER) :]
8991
has_protocol = True
@@ -92,7 +94,7 @@ def service_type_name(type_: str, *, strict: bool = True) -> str: # pylint: dis
9294
f"Type '{type_}' must end with "
9395
f"'{_TCP_PROTOCOL_LOCAL_TRAILER}' or '{_NONTCP_PROTOCOL_LOCAL_TRAILER}'"
9496
)
95-
elif type_.endswith(_LOCAL_TRAILER):
97+
elif type_lower.endswith(_LOCAL_TRAILER):
9698
remaining = type_[: -len(_LOCAL_TRAILER)].split(".")
9799
trailer = type_[-len(_LOCAL_TRAILER) + 1 :]
98100
has_protocol = False

tests/utils/test_name.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,28 @@ def test_service_type_name_non_strict_compliant_names(instance_name, service_typ
6161
assert instance_name_from_service_info(info, strict=False) == instance_name
6262

6363

64+
@pytest.mark.parametrize(
65+
"type_, expected",
66+
(
67+
("_http._tcp.LOCAL.", "_http._tcp.LOCAL."),
68+
("_http._TCP.local.", "_http._TCP.local."),
69+
("_HTTP._tcp.local.", "_HTTP._tcp.local."),
70+
("Instance._http._tcp.LOCAL.", "_http._tcp.LOCAL."),
71+
("_ntp._udp.LOCAL.", "_ntp._udp.LOCAL."),
72+
("_ntp._UDP.local.", "_ntp._UDP.local."),
73+
("Instance._ntp._udp.LOCAL.", "_ntp._udp.LOCAL."),
74+
),
75+
)
76+
def test_service_type_name_uppercase_trailer(type_, expected):
77+
"""RFC 1035 §2.3.3 / RFC 6762 §16 — DNS names are case-insensitive."""
78+
assert nameutils.service_type_name(type_) == expected
79+
80+
81+
def test_service_type_name_uppercase_local_non_strict():
82+
"""Non-strict mode accepts uppercase .LOCAL. trailer without a protocol label."""
83+
assert nameutils.service_type_name("Localhost.LOCAL.", strict=False) == "LOCAL."
84+
85+
6486
def test_possible_types():
6587
"""Test possible types from name."""
6688
assert nameutils.possible_types(".") == set()

0 commit comments

Comments
 (0)