Skip to content

Commit 0cdba98

Browse files
authored
Suppress additionals when answer is suppressed (#690)
1 parent 993a82e commit 0cdba98

2 files changed

Lines changed: 198 additions & 97 deletions

File tree

tests/test_handlers.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,3 +701,131 @@ def test_known_answer_supression_service_type_enumeration_query():
701701
zc.registry.remove(info)
702702
zc.registry.remove(info2)
703703
zc.close()
704+
705+
706+
def test_qu_response_only_sends_additionals_if_sends_answer():
707+
"""Test that a QU response does not send additionals unless it sends the answer as well."""
708+
# instantiate a zeroconf instance
709+
zc = Zeroconf(interfaces=['127.0.0.1'])
710+
711+
type_ = "_addtest1._tcp.local."
712+
name = "knownname"
713+
registration_name = "%s.%s" % (name, type_)
714+
desc = {'path': '/~paulsm/'}
715+
server_name = "ash-2.local."
716+
info = ServiceInfo(
717+
type_, registration_name, 80, 0, 0, desc, server_name, addresses=[socket.inet_aton("10.0.1.2")]
718+
)
719+
zc.registry.add(info)
720+
721+
type_2 = "_addtest2._tcp.local."
722+
name = "knownname"
723+
registration_name2 = "%s.%s" % (name, type_2)
724+
desc = {'path': '/~paulsm/'}
725+
server_name2 = "ash-3.local."
726+
info2 = ServiceInfo(
727+
type_2, registration_name2, 80, 0, 0, desc, server_name2, addresses=[socket.inet_aton("10.0.1.2")]
728+
)
729+
zc.registry.add(info2)
730+
731+
ptr_record = info.dns_pointer()
732+
733+
# Add the PTR record to the cache
734+
zc.cache.add(ptr_record)
735+
736+
# Add the A record to the cache with 50% ttl remaining
737+
a_record = info.dns_addresses()[0]
738+
a_record._set_created_ttl(current_time_millis() - (a_record.ttl * 1000 / 2), a_record.ttl)
739+
assert not a_record.is_recent(current_time_millis())
740+
zc.cache.add(a_record)
741+
742+
# With QU should respond to only unicast when the answer has been recently multicast
743+
# even if the additional has not been recently multicast
744+
query = r.DNSOutgoing(const._FLAGS_QR_QUERY)
745+
question = r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN)
746+
question.unique = True # Set the QU bit
747+
assert question.unicast is True
748+
query.add_question(question)
749+
750+
unicast_out, multicast_out = zc.query_handler.response(
751+
[r.DNSIncoming(packet) for packet in query.packets()], "1.2.3.4", const._MDNS_PORT
752+
)
753+
assert multicast_out is None
754+
assert a_record in unicast_out.additionals
755+
assert unicast_out.answers[0][0] == ptr_record
756+
757+
# Remove the 50% A record and add a 100% A record
758+
zc.cache.remove(a_record)
759+
a_record = info.dns_addresses()[0]
760+
assert a_record.is_recent(current_time_millis())
761+
zc.cache.add(a_record)
762+
# With QU should respond to only unicast when the answer has been recently multicast
763+
# even if the additional has not been recently multicast
764+
query = r.DNSOutgoing(const._FLAGS_QR_QUERY)
765+
question = r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN)
766+
question.unique = True # Set the QU bit
767+
assert question.unicast is True
768+
query.add_question(question)
769+
770+
unicast_out, multicast_out = zc.query_handler.response(
771+
[r.DNSIncoming(packet) for packet in query.packets()], "1.2.3.4", const._MDNS_PORT
772+
)
773+
assert multicast_out is None
774+
assert a_record in unicast_out.additionals
775+
assert unicast_out.answers[0][0] == ptr_record
776+
777+
# Remove the 100% PTR record and add a 50% PTR record
778+
zc.cache.remove(ptr_record)
779+
ptr_record._set_created_ttl(current_time_millis() - (ptr_record.ttl * 1000 / 2), ptr_record.ttl)
780+
assert not ptr_record.is_recent(current_time_millis())
781+
zc.cache.add(ptr_record)
782+
# With QU should respond to only multicast since the has less
783+
# than 75% of its ttl remaining
784+
query = r.DNSOutgoing(const._FLAGS_QR_QUERY)
785+
question = r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN)
786+
question.unique = True # Set the QU bit
787+
assert question.unicast is True
788+
query.add_question(question)
789+
790+
unicast_out, multicast_out = zc.query_handler.response(
791+
[r.DNSIncoming(packet) for packet in query.packets()], "1.2.3.4", const._MDNS_PORT
792+
)
793+
assert multicast_out.answers[0][0] == ptr_record
794+
assert a_record in multicast_out.additionals
795+
assert info.dns_text() in multicast_out.additionals
796+
assert info.dns_service() in multicast_out.additionals
797+
798+
assert unicast_out is None
799+
800+
# Ask 2 QU questions, with info the PTR is at 50%, with info2 the PTR is at 100%
801+
# We should get back a unicast reply for info2, but info should be multicasted since its within 75% of its TTL
802+
# With QU should respond to only multicast since the has less
803+
# than 75% of its ttl remaining
804+
query = r.DNSOutgoing(const._FLAGS_QR_QUERY)
805+
question = r.DNSQuestion(info.type, const._TYPE_PTR, const._CLASS_IN)
806+
question.unique = True # Set the QU bit
807+
assert question.unicast is True
808+
query.add_question(question)
809+
810+
question = r.DNSQuestion(info2.type, const._TYPE_PTR, const._CLASS_IN)
811+
question.unique = True # Set the QU bit
812+
assert question.unicast is True
813+
query.add_question(question)
814+
zc.cache.add(info2.dns_pointer()) # Add 100% TTL for info2 to the cache
815+
816+
unicast_out, multicast_out = zc.query_handler.response(
817+
[r.DNSIncoming(packet) for packet in query.packets()], "1.2.3.4", const._MDNS_PORT
818+
)
819+
assert multicast_out.answers[0][0] == info.dns_pointer()
820+
assert info.dns_addresses()[0] in multicast_out.additionals
821+
assert info.dns_text() in multicast_out.additionals
822+
assert info.dns_service() in multicast_out.additionals
823+
824+
assert unicast_out.answers[0][0] == info2.dns_pointer()
825+
assert info2.dns_addresses()[0] in unicast_out.additionals
826+
assert info2.dns_text() in unicast_out.additionals
827+
assert info2.dns_service() in unicast_out.additionals
828+
829+
# unregister
830+
zc.registry.remove(info)
831+
zc.close()

0 commit comments

Comments
 (0)