Skip to content

Commit 575097e

Browse files
committed
Avoid including additionals when the answer is suppressed by known-answer supression
- If the PTR or SRV record was suppressed we should not include the additionals per https://tools.ietf.org/html/rfc6763#section-12.1
1 parent aea2c8a commit 575097e

2 files changed

Lines changed: 120 additions & 3 deletions

File tree

tests/test_handlers.py

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import unittest.mock
1313

1414
import zeroconf as r
15-
from zeroconf import ServiceInfo, Zeroconf
15+
from zeroconf import ServiceInfo, Zeroconf, current_time_millis
1616
from zeroconf import const
1717

1818
from . import _clear_cache
@@ -292,3 +292,113 @@ def test_unicast_response():
292292
# unregister
293293
zc.unregister_service(info)
294294
zc.close()
295+
296+
297+
def test_known_answer_supression():
298+
zc = Zeroconf(interfaces=['127.0.0.1'])
299+
type_ = "_knownservice._tcp.local."
300+
name = "knownname"
301+
registration_name = "%s.%s" % (name, type_)
302+
desc = {'path': '/~paulsm/'}
303+
server_name = "ash-2.local."
304+
info = ServiceInfo(
305+
type_, registration_name, 80, 0, 0, desc, server_name, addresses=[socket.inet_aton("10.0.1.2")]
306+
)
307+
zc.register_service(info)
308+
309+
now = current_time_millis()
310+
311+
# Test PTR supression
312+
generated = r.DNSOutgoing(const._FLAGS_QR_QUERY)
313+
question = r.DNSQuestion(type_, const._TYPE_PTR, const._CLASS_IN)
314+
generated.add_question(question)
315+
packets = generated.packets()
316+
unicast_out, multicast_out = zc.query_handler.response(
317+
r.DNSIncoming(packets[0]), "1.2.3.4", const._MDNS_PORT
318+
)
319+
assert unicast_out is None
320+
assert multicast_out is not None and multicast_out.answers
321+
322+
generated = r.DNSOutgoing(const._FLAGS_QR_QUERY)
323+
question = r.DNSQuestion(type_, const._TYPE_PTR, const._CLASS_IN)
324+
generated.add_question(question)
325+
generated.add_answer_at_time(info.dns_pointer(), now)
326+
packets = generated.packets()
327+
unicast_out, multicast_out = zc.query_handler.response(
328+
r.DNSIncoming(packets[0]), "1.2.3.4", const._MDNS_PORT
329+
)
330+
assert unicast_out is None
331+
# If the answer is suppressed, the additional should be suppresed as well
332+
assert not multicast_out or not multicast_out.answers
333+
334+
# Test A supression
335+
generated = r.DNSOutgoing(const._FLAGS_QR_QUERY)
336+
question = r.DNSQuestion(server_name, const._TYPE_A, const._CLASS_IN)
337+
generated.add_question(question)
338+
packets = generated.packets()
339+
unicast_out, multicast_out = zc.query_handler.response(
340+
r.DNSIncoming(packets[0]), "1.2.3.4", const._MDNS_PORT
341+
)
342+
assert unicast_out is None
343+
assert multicast_out is not None and multicast_out.answers
344+
345+
generated = r.DNSOutgoing(const._FLAGS_QR_QUERY)
346+
question = r.DNSQuestion(server_name, const._TYPE_A, const._CLASS_IN)
347+
generated.add_question(question)
348+
for dns_address in info.dns_addresses():
349+
generated.add_answer_at_time(dns_address, now)
350+
packets = generated.packets()
351+
unicast_out, multicast_out = zc.query_handler.response(
352+
r.DNSIncoming(packets[0]), "1.2.3.4", const._MDNS_PORT
353+
)
354+
assert unicast_out is None
355+
assert not multicast_out or not multicast_out.answers
356+
357+
# Test SRV supression
358+
generated = r.DNSOutgoing(const._FLAGS_QR_QUERY)
359+
question = r.DNSQuestion(registration_name, const._TYPE_SRV, const._CLASS_IN)
360+
generated.add_question(question)
361+
packets = generated.packets()
362+
unicast_out, multicast_out = zc.query_handler.response(
363+
r.DNSIncoming(packets[0]), "1.2.3.4", const._MDNS_PORT
364+
)
365+
assert unicast_out is None
366+
assert multicast_out is not None and multicast_out.answers
367+
368+
generated = r.DNSOutgoing(const._FLAGS_QR_QUERY)
369+
question = r.DNSQuestion(registration_name, const._TYPE_SRV, const._CLASS_IN)
370+
generated.add_question(question)
371+
generated.add_answer_at_time(info.dns_service(), now)
372+
packets = generated.packets()
373+
unicast_out, multicast_out = zc.query_handler.response(
374+
r.DNSIncoming(packets[0]), "1.2.3.4", const._MDNS_PORT
375+
)
376+
assert unicast_out is None
377+
# If the answer is suppressed, the additional should be suppresed as well
378+
assert not multicast_out or not multicast_out.answers
379+
380+
# Test TXT supression
381+
generated = r.DNSOutgoing(const._FLAGS_QR_QUERY)
382+
question = r.DNSQuestion(registration_name, const._TYPE_TXT, const._CLASS_IN)
383+
generated.add_question(question)
384+
packets = generated.packets()
385+
unicast_out, multicast_out = zc.query_handler.response(
386+
r.DNSIncoming(packets[0]), "1.2.3.4", const._MDNS_PORT
387+
)
388+
assert unicast_out is None
389+
assert multicast_out is not None and multicast_out.answers
390+
391+
generated = r.DNSOutgoing(const._FLAGS_QR_QUERY)
392+
question = r.DNSQuestion(registration_name, const._TYPE_TXT, const._CLASS_IN)
393+
generated.add_question(question)
394+
generated.add_answer_at_time(info.dns_text(), now)
395+
packets = generated.packets()
396+
unicast_out, multicast_out = zc.query_handler.response(
397+
r.DNSIncoming(packets[0]), "1.2.3.4", const._MDNS_PORT
398+
)
399+
assert unicast_out is None
400+
assert not multicast_out or not multicast_out.answers
401+
402+
# unregister
403+
zc.unregister_service(info)
404+
zc.close()

zeroconf/_handlers.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ def _answer_service_type_enumeration_query(self, msg: DNSIncoming, out: DNSOutgo
7575
def _answer_ptr_query(self, msg: DNSIncoming, out: DNSOutgoing, question: DNSQuestion) -> None:
7676
"""Answer a PTR query."""
7777
for service in self.registry.get_infos_type(question.name):
78+
dns_pointer = service.dns_pointer()
79+
if dns_pointer.suppressed_by(msg):
80+
continue
7881
out.add_answer(msg, service.dns_pointer())
7982
# Add recommended additional answers according to
8083
# https://tools.ietf.org/html/rfc6763#section-12.1.
@@ -103,8 +106,12 @@ def _answer_non_ptr_query(self, msg: DNSIncoming, out: DNSOutgoing, question: DN
103106
if question.type in (_TYPE_TXT, _TYPE_ANY):
104107
out.add_answer(msg, service.dns_text())
105108
if question.type == _TYPE_SRV:
106-
for dns_address in service.dns_addresses():
107-
out.add_additional_answer(dns_address)
109+
dns_service = service.dns_service()
110+
if not dns_service.suppressed_by(msg):
111+
# Add recommended additional answers according to
112+
# https://datatracker.ietf.org/doc/html/rfc6763#section-12.2
113+
for dns_address in service.dns_addresses():
114+
out.add_additional_answer(dns_address)
108115

109116
def response( # pylint: disable=unused-argument
110117
self, msg: DNSIncoming, addr: Optional[str], port: int

0 commit comments

Comments
 (0)