Skip to content

Commit c035925

Browse files
authored
Switch to using DNSRRSet in RecordManager (#735)
1 parent 50af944 commit c035925

2 files changed

Lines changed: 39 additions & 24 deletions

File tree

zeroconf/_dns.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,3 +423,10 @@ def suppresses(self, record: DNSRecord) -> bool:
423423
self._lookup = {record: record for record in self._records}
424424
other = self._lookup.get(record)
425425
return bool(other and other.ttl > (record.ttl / 2))
426+
427+
def __contains__(self, record: DNSRecord) -> bool:
428+
"""Returns true if the rrset contains the record."""
429+
if self._lookup is None:
430+
# Build the hash table so we can lookup the record independent of the ttl
431+
self._lookup = {record: record for record in self._records}
432+
return record in self._lookup

zeroconf/_handlers.py

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -311,42 +311,33 @@ def async_updates_from_response(self, msg: DNSIncoming) -> None:
311311
other_adds: List[DNSRecord] = []
312312
removes: List[DNSRecord] = []
313313
now = msg.now
314-
for record in msg.answers:
315-
316-
updated = True
314+
unique_types: Set[Tuple[str, int, int]] = set()
317315

316+
for record in msg.answers:
318317
if record.unique: # https://tools.ietf.org/html/rfc6762#section-10.2
319-
# rfc6762#section-10.2 para 2
320-
# Since unique is set, all old records with that name, rrtype,
321-
# and rrclass that were received more than one second ago are declared
322-
# invalid, and marked to expire from the cache in one second.
323-
for entry in self.cache.get_all_by_details(record.name, record.type, record.class_):
324-
if entry == record:
325-
updated = False
326-
if record.created - entry.created > 1000 and entry not in msg.answers:
327-
# Expire in 1s
328-
entry.set_created_ttl(now, 1)
329-
330-
expired = record.is_expired(now)
318+
unique_types.add((record.name, record.type, record.class_))
319+
331320
maybe_entry = self.cache.get(record)
332-
if not expired:
321+
if not record.is_expired(now):
333322
if maybe_entry is not None:
334323
maybe_entry.reset_ttl(record)
335324
else:
336325
if isinstance(record, DNSAddress):
337326
address_adds.append(record)
338327
else:
339328
other_adds.append(record)
340-
if updated:
341-
updates.append(record)
329+
updates.append(record)
330+
# This is likely a goodbye since the record is
331+
# expired and exists in the cache
342332
elif maybe_entry is not None:
343333
updates.append(record)
344334
removes.append(record)
345335

346-
if not updates and not address_adds and not other_adds and not removes:
347-
return
336+
if unique_types:
337+
self._async_mark_unique_cached_records_older_than_1s_to_expire(unique_types, msg.answers, now)
348338

349-
self.async_updates(now, updates)
339+
if updates:
340+
self.async_updates(now, updates)
350341
# The cache adds must be processed AFTER we trigger
351342
# the updates since we compare existing data
352343
# with the new data and updating the cache
@@ -362,12 +353,29 @@ def async_updates_from_response(self, msg: DNSIncoming) -> None:
362353
# zc.get_service_info will see the cached value
363354
# but ONLY after all the record updates have been
364355
# processsed.
365-
self.cache.async_add_records(itertools.chain(address_adds, other_adds))
356+
if other_adds or address_adds:
357+
self.cache.async_add_records(itertools.chain(address_adds, other_adds))
366358
# Removes are processed last since
367359
# ServiceInfo could generate an un-needed query
368360
# because the data was not yet populated.
369-
self.cache.async_remove_records(removes)
370-
self.async_updates_complete()
361+
if removes:
362+
self.cache.async_remove_records(removes)
363+
if updates:
364+
self.async_updates_complete()
365+
366+
def _async_mark_unique_cached_records_older_than_1s_to_expire(
367+
self, unique_types: Set[Tuple[str, int, int]], answers: List[DNSRecord], now: float
368+
) -> None:
369+
# rfc6762#section-10.2 para 2
370+
# Since unique is set, all old records with that name, rrtype,
371+
# and rrclass that were received more than one second ago are declared
372+
# invalid, and marked to expire from the cache in one second.
373+
answers_rrset = DNSRRSet(answers)
374+
for name, type_, class_ in unique_types:
375+
for entry in self.cache.get_all_by_details(name, type_, class_):
376+
if (now - entry.created > 1000) and entry not in answers_rrset:
377+
# Expire in 1s
378+
entry.set_created_ttl(now, 1)
371379

372380
def add_listener(
373381
self, listener: RecordUpdateListener, question: Optional[Union[DNSQuestion, List[DNSQuestion]]]

0 commit comments

Comments
 (0)