Skip to content

Commit 14e3ad5

Browse files
emontnemeryjstasiak
authored andcommitted
Fix TTL handling for published service, align default TTL with RFC6762 (python-zeroconf#113)
Honor TTL passed in service registration Set default TTL to 120 s as recommended by RFC6762
1 parent fe62ba3 commit 14e3ad5

File tree

2 files changed

+96
-7
lines changed

2 files changed

+96
-7
lines changed

test_zeroconf.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,94 @@ def test_incoming_ipv6(self):
538538
pass
539539

540540

541+
class TestRegistrar(unittest.TestCase):
542+
543+
def test_ttl(self):
544+
545+
# instantiate a zeroconf instance
546+
zc = Zeroconf(interfaces=['127.0.0.1'])
547+
548+
# service definition
549+
type_ = "_test-srvc-type._tcp.local."
550+
name = "xxxyyy"
551+
registration_name = "%s.%s" % (name, type_)
552+
553+
desc = {'path': '/~paulsm/'}
554+
info = ServiceInfo(
555+
type_, registration_name,
556+
socket.inet_aton("10.0.1.2"), 80, 0, 0,
557+
desc, "ash-2.local.")
558+
559+
# we are going to monkey patch the zeroconf send to check packet sizes
560+
old_send = zc.send
561+
562+
# needs to be a list so that we can modify it in our phony send
563+
nbr_answers = [0, None]
564+
nbr_additionals = [0, None]
565+
nbr_authorities = [0, None]
566+
567+
def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT):
568+
"""Sends an outgoing packet."""
569+
for answer, time_ in out.answers:
570+
nbr_answers[0] += 1
571+
assert answer.ttl == expected_ttl
572+
for answer in out.additionals:
573+
nbr_additionals[0] += 1
574+
assert answer.ttl == expected_ttl
575+
for answer in out.authorities:
576+
nbr_authorities[0] += 1
577+
assert answer.ttl == expected_ttl
578+
old_send(out, addr=addr, port=port)
579+
580+
# monkey patch the zeroconf send
581+
zc.send = send
582+
583+
# register service with default TTL
584+
expected_ttl = r._DNS_TTL
585+
zc.register_service(info)
586+
assert nbr_answers[0] == 12 and nbr_additionals[0] == 0 and nbr_authorities[0] == 3
587+
nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0
588+
589+
# query
590+
query = r.DNSOutgoing(r._FLAGS_QR_QUERY | r._FLAGS_AA)
591+
query.add_question(r.DNSQuestion(info.type, r._TYPE_PTR, r._CLASS_IN))
592+
query.add_question(r.DNSQuestion(info.name, r._TYPE_SRV, r._CLASS_IN))
593+
query.add_question(r.DNSQuestion(info.name, r._TYPE_TXT, r._CLASS_IN))
594+
query.add_question(r.DNSQuestion(info.server, r._TYPE_A, r._CLASS_IN))
595+
zc.handle_query(query, r._MDNS_ADDR, r._MDNS_PORT)
596+
assert nbr_answers[0] == 4 and nbr_additionals[0] == 1 and nbr_authorities[0] == 0
597+
nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0
598+
599+
# unregister
600+
expected_ttl = 0
601+
zc.unregister_service(info)
602+
assert nbr_answers[0] == 12 and nbr_additionals[0] == 0 and nbr_authorities[0] == 0
603+
nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0
604+
605+
# register service with custom TTL
606+
expected_ttl = r._DNS_TTL * 2
607+
assert expected_ttl != r._DNS_TTL
608+
zc.register_service(info, ttl=expected_ttl)
609+
assert nbr_answers[0] == 12 and nbr_additionals[0] == 0 and nbr_authorities[0] == 3
610+
nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0
611+
612+
# query
613+
query = r.DNSOutgoing(r._FLAGS_QR_QUERY | r._FLAGS_AA)
614+
query.add_question(r.DNSQuestion(info.type, r._TYPE_PTR, r._CLASS_IN))
615+
query.add_question(r.DNSQuestion(info.name, r._TYPE_SRV, r._CLASS_IN))
616+
query.add_question(r.DNSQuestion(info.name, r._TYPE_TXT, r._CLASS_IN))
617+
query.add_question(r.DNSQuestion(info.server, r._TYPE_A, r._CLASS_IN))
618+
zc.handle_query(query, r._MDNS_ADDR, r._MDNS_PORT)
619+
assert nbr_answers[0] == 4 and nbr_additionals[0] == 1 and nbr_authorities[0] == 0
620+
nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0
621+
622+
# unregister
623+
expected_ttl = 0
624+
zc.unregister_service(info)
625+
assert nbr_answers[0] == 12 and nbr_additionals[0] == 0 and nbr_authorities[0] == 0
626+
nbr_answers[0] = nbr_additionals[0] = nbr_authorities[0] = 0
627+
628+
541629
class TestDNSCache(unittest.TestCase):
542630

543631
def test_order(self):

zeroconf.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
_MDNS_ADDR = '224.0.0.251'
7272
_MDNS_PORT = 5353
7373
_DNS_PORT = 53
74-
_DNS_TTL = 60 * 60 # one hour default TTL
74+
_DNS_TTL = 120 # two minutes default TTL as recommended by RFC6762
7575

7676
_MAX_MSG_TYPICAL = 1460 # unused
7777
_MAX_MSG_ABSOLUTE = 8966
@@ -1782,6 +1782,7 @@ def register_service(self, info, ttl=_DNS_TTL, allow_name_change=False):
17821782
of 60 seconds. Zeroconf will then respond to requests for
17831783
information for that service. The name of the service may be
17841784
changed if needed to make it unique on the network."""
1785+
info.ttl = ttl
17851786
self.check_service(info, allow_name_change)
17861787
self.services[info.name.lower()] = info
17871788
if info.type in self.servicetypes:
@@ -1917,7 +1918,7 @@ def check_service(self, info, allow_name_change):
19171918
self.debug = out
19181919
out.add_question(DNSQuestion(info.type, _TYPE_PTR, _CLASS_IN))
19191920
out.add_authorative_answer(DNSPointer(
1920-
info.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, info.name))
1921+
info.type, _TYPE_PTR, _CLASS_IN, info.ttl, info.name))
19211922
self.send(out)
19221923
i += 1
19231924
next_time += _CHECK_TIME
@@ -1995,7 +1996,7 @@ def handle_query(self, msg, addr, port):
19951996
out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA)
19961997
out.add_answer(msg, DNSPointer(
19971998
service.type, _TYPE_PTR,
1998-
_CLASS_IN, _DNS_TTL, service.name))
1999+
_CLASS_IN, service.ttl, service.name))
19992000
else:
20002001
try:
20012002
if out is None:
@@ -2008,7 +2009,7 @@ def handle_query(self, msg, addr, port):
20082009
out.add_answer(msg, DNSAddress(
20092010
question.name, _TYPE_A,
20102011
_CLASS_IN | _CLASS_UNIQUE,
2011-
_DNS_TTL, service.address))
2012+
service.ttl, service.address))
20122013

20132014
service = self.services.get(question.name.lower(), None)
20142015
if not service:
@@ -2017,16 +2018,16 @@ def handle_query(self, msg, addr, port):
20172018
if question.type in (_TYPE_SRV, _TYPE_ANY):
20182019
out.add_answer(msg, DNSService(
20192020
question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE,
2020-
_DNS_TTL, service.priority, service.weight,
2021+
service.ttl, service.priority, service.weight,
20212022
service.port, service.server))
20222023
if question.type in (_TYPE_TXT, _TYPE_ANY):
20232024
out.add_answer(msg, DNSText(
20242025
question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE,
2025-
_DNS_TTL, service.text))
2026+
service.ttl, service.text))
20262027
if question.type == _TYPE_SRV:
20272028
out.add_additional_answer(DNSAddress(
20282029
service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE,
2029-
_DNS_TTL, service.address))
2030+
service.ttl, service.address))
20302031
except Exception: # TODO stop catching all Exceptions
20312032
self.log_exception_warning()
20322033

0 commit comments

Comments
 (0)