Skip to content

Commit 88b9875

Browse files
authored
Defer decoding known answers until needed (#983)
1 parent 05c4329 commit 88b9875

2 files changed

Lines changed: 26 additions & 9 deletions

File tree

zeroconf/_handlers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ def async_response( # pylint: disable=unused-argument
348348
threadsafe.
349349
"""
350350
known_answers = DNSRRSet(
351-
itertools.chain(*(msg.answers for msg in msgs if not _message_is_probe(msg)))
351+
itertools.chain.from_iterable(msg.answers for msg in msgs if not _message_is_probe(msg))
352352
)
353353
query_res = _QueryResponse(self.cache, msgs)
354354

zeroconf/_protocol.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
import enum
2424
import struct
25-
from typing import Any, Dict, List, Optional, Sequence, Set, TYPE_CHECKING, Tuple, Union, cast
25+
from typing import Any, Callable, Dict, List, Optional, Sequence, Set, TYPE_CHECKING, Tuple, Union, cast
2626

2727

2828
from ._dns import DNSAddress, DNSHinfo, DNSNsec, DNSPointer, DNSQuestion, DNSRecord, DNSService, DNSText
@@ -96,23 +96,39 @@ def __init__(self, data: bytes, scope_id: Optional[int] = None, now: Optional[fl
9696
self.name_cache: Dict[int, List[str]] = {}
9797
self.seen_pointers: Set[int] = set()
9898
self.questions: List[DNSQuestion] = []
99-
self.answers: List[DNSRecord] = []
99+
self._answers: List[DNSRecord] = []
100100
self.id = 0
101101
self.num_questions = 0
102102
self.num_answers = 0
103103
self.num_authorities = 0
104104
self.num_additionals = 0
105105
self.valid = False
106+
self._read_others = False
106107
self.now = now or current_time_millis()
107108
self.scope_id = scope_id
109+
self._parse_data(self._initial_parse)
108110

109-
try:
110-
self.read_header()
111-
self.read_questions()
111+
def _initial_parse(self) -> None:
112+
"""Parse the data needed to initalize the packet object."""
113+
self.read_header()
114+
self.read_questions()
115+
if not self.num_questions:
112116
self.read_others()
113-
self.valid = True
117+
self.valid = True
118+
119+
def _parse_data(self, parser_call: Callable) -> None:
120+
"""Parse part of the packet and catch exceptions."""
121+
try:
122+
parser_call()
114123
except DECODE_EXCEPTIONS:
115-
self.log_exception_warning('Choked at offset %d while unpacking %r', self.offset, data)
124+
self.log_exception_warning('Choked at offset %d while unpacking %r', self.offset, self.data)
125+
126+
@property
127+
def answers(self) -> List[DNSRecord]:
128+
"""Answers in the packet."""
129+
if not self._read_others:
130+
self._parse_data(self.read_others)
131+
return self._answers
116132

117133
def __repr__(self) -> str:
118134
return '<DNSIncoming:{%s}>' % ', '.join(
@@ -170,6 +186,7 @@ def read_unsigned_short(self) -> int:
170186
def read_others(self) -> None:
171187
"""Reads the answers, authorities and additionals section of the
172188
packet"""
189+
self._read_others = True
173190
n = self.num_answers + self.num_authorities + self.num_additionals
174191
for _ in range(n):
175192
domain = self.read_name()
@@ -192,7 +209,7 @@ def read_others(self) -> None:
192209
exc_info=True,
193210
)
194211
if rec is not None:
195-
self.answers.append(rec)
212+
self._answers.append(rec)
196213

197214
def read_record(self, domain: str, type_: int, class_: int, ttl: int, length: int) -> Optional[DNSRecord]:
198215
"""Read known records types and skip unknown ones."""

0 commit comments

Comments
 (0)