Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/layers/dns.py: 45%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

848 statements  

1# SPDX-License-Identifier: GPL-2.0-only 

2# This file is part of Scapy 

3# See https://scapy.net/ for more information 

4# Copyright (C) Philippe Biondi <phil@secdev.org> 

5 

6""" 

7DNS: Domain Name System 

8 

9This implements: 

10- RFC1035: Domain Names 

11- RFC6762: Multicast DNS 

12- RFC6763: DNS-Based Service Discovery 

13""" 

14 

15import abc 

16import operator 

17import itertools 

18import socket 

19import struct 

20import time 

21import warnings 

22 

23from scapy.arch import ( 

24 get_if_addr, 

25 get_if_addr6, 

26 read_nameservers, 

27) 

28from scapy.ansmachine import AnsweringMachine 

29from scapy.base_classes import Net, ScopedIP 

30from scapy.config import conf 

31from scapy.compat import raw, chb, bytes_encode, plain_str 

32from scapy.error import log_runtime, warning, Scapy_Exception 

33from scapy.packet import Packet, bind_layers, Raw 

34from scapy.fields import ( 

35 BitEnumField, 

36 BitField, 

37 ByteEnumField, 

38 ByteField, 

39 ConditionalField, 

40 Field, 

41 FieldLenField, 

42 FieldListField, 

43 FlagsField, 

44 I, 

45 IP6Field, 

46 IntField, 

47 MACField, 

48 MultipleTypeField, 

49 PacketListField, 

50 ShortEnumField, 

51 ShortField, 

52 StrField, 

53 StrLenField, 

54 UTCTimeField, 

55 XStrFixedLenField, 

56 XStrLenField, 

57) 

58from scapy.interfaces import resolve_iface 

59from scapy.sendrecv import sr1, sr 

60from scapy.supersocket import StreamSocket 

61from scapy.plist import SndRcvList, _PacketList, QueryAnswer 

62from scapy.pton_ntop import inet_ntop, inet_pton 

63from scapy.utils import pretty_list 

64from scapy.volatile import RandShort 

65 

66from scapy.layers.l2 import Ether 

67from scapy.layers.inet import IP, DestIPField, IPField, UDP, TCP 

68from scapy.layers.inet6 import IPv6 

69 

70from typing import ( 

71 Any, 

72 List, 

73 Optional, 

74 Tuple, 

75 Type, 

76 Union, 

77) 

78 

79 

80# https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4 

81dnstypes = { 

82 0: "RESERVED", 

83 1: "A", 2: "NS", 3: "MD", 4: "MF", 5: "CNAME", 6: "SOA", 7: "MB", 8: "MG", 

84 9: "MR", 10: "NULL", 11: "WKS", 12: "PTR", 13: "HINFO", 14: "MINFO", 

85 15: "MX", 16: "TXT", 17: "RP", 18: "AFSDB", 19: "X25", 20: "ISDN", 

86 21: "RT", 22: "NSAP", 23: "NSAP-PTR", 24: "SIG", 25: "KEY", 26: "PX", 

87 27: "GPOS", 28: "AAAA", 29: "LOC", 30: "NXT", 31: "EID", 32: "NIMLOC", 

88 33: "SRV", 34: "ATMA", 35: "NAPTR", 36: "KX", 37: "CERT", 38: "A6", 

89 39: "DNAME", 40: "SINK", 41: "OPT", 42: "APL", 43: "DS", 44: "SSHFP", 

90 45: "IPSECKEY", 46: "RRSIG", 47: "NSEC", 48: "DNSKEY", 49: "DHCID", 

91 50: "NSEC3", 51: "NSEC3PARAM", 52: "TLSA", 53: "SMIMEA", 55: "HIP", 

92 56: "NINFO", 57: "RKEY", 58: "TALINK", 59: "CDS", 60: "CDNSKEY", 

93 61: "OPENPGPKEY", 62: "CSYNC", 63: "ZONEMD", 64: "SVCB", 65: "HTTPS", 

94 99: "SPF", 100: "UINFO", 101: "UID", 102: "GID", 103: "UNSPEC", 104: "NID", 

95 105: "L32", 106: "L64", 107: "LP", 108: "EUI48", 109: "EUI64", 249: "TKEY", 

96 250: "TSIG", 256: "URI", 257: "CAA", 258: "AVC", 259: "DOA", 

97 260: "AMTRELAY", 32768: "TA", 32769: "DLV", 65535: "RESERVED" 

98} 

99 

100 

101dnsqtypes = {251: "IXFR", 252: "AXFR", 253: "MAILB", 254: "MAILA", 255: "ALL"} 

102dnsqtypes.update(dnstypes) 

103dnsclasses = {1: 'IN', 2: 'CS', 3: 'CH', 4: 'HS', 255: 'ANY'} 

104 

105 

106# 12/2023 from https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml # noqa: E501 

107dnssecalgotypes = {0: "Reserved", 1: "RSA/MD5", 2: "Diffie-Hellman", 3: "DSA/SHA-1", # noqa: E501 

108 4: "Reserved", 5: "RSA/SHA-1", 6: "DSA-NSEC3-SHA1", 

109 7: "RSASHA1-NSEC3-SHA1", 8: "RSA/SHA-256", 9: "Reserved", 

110 10: "RSA/SHA-512", 11: "Reserved", 12: "GOST R 34.10-2001", 

111 13: "ECDSA Curve P-256 with SHA-256", 14: "ECDSA Curve P-384 with SHA-384", # noqa: E501 

112 15: "Ed25519", 16: "Ed448", 

113 252: "Reserved for Indirect Keys", 253: "Private algorithms - domain name", # noqa: E501 

114 254: "Private algorithms - OID", 255: "Reserved"} 

115 

116# 12/2023 from https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml 

117dnssecdigesttypes = {0: "Reserved", 1: "SHA-1", 2: "SHA-256", 3: "GOST R 34.11-94", 4: "SHA-384"} # noqa: E501 

118 

119# 12/2023 from https://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml # noqa: E501 

120dnssecnsec3algotypes = {0: "Reserved", 1: "SHA-1"} 

121 

122 

123def dns_get_str(s, full=None, _ignore_compression=False): 

124 """This function decompresses a string s, starting 

125 from the given pointer. 

126 

127 :param s: the string to decompress 

128 :param full: (optional) the full packet (used for decompression) 

129 

130 :returns: (decoded_string, end_index, left_string) 

131 """ 

132 # _ignore_compression is for internal use only 

133 max_length = len(s) 

134 # The result = the extracted name 

135 name = b"" 

136 # Will contain the index after the pointer, to be returned 

137 after_pointer = None 

138 processed_pointers = [] # Used to check for decompression loops 

139 bytes_left = None 

140 _fullpacket = False # s = full packet 

141 pointer = 0 

142 while True: 

143 if abs(pointer) >= max_length: 

144 log_runtime.info( 

145 "DNS RR prematured end (ofs=%i, len=%i)", pointer, len(s) 

146 ) 

147 break 

148 cur = s[pointer] # get pointer value 

149 pointer += 1 # make pointer go forward 

150 if cur & 0xc0: # Label pointer 

151 if after_pointer is None: 

152 # after_pointer points to where the remaining bytes start, 

153 # as pointer will follow the jump token 

154 after_pointer = pointer + 1 

155 if _ignore_compression: 

156 # skip 

157 pointer += 1 

158 continue 

159 if pointer >= max_length: 

160 log_runtime.info( 

161 "DNS incomplete jump token at (ofs=%i)", pointer 

162 ) 

163 break 

164 if not full: 

165 raise Scapy_Exception("DNS message can't be compressed " + 

166 "at this point!") 

167 # Follow the pointer 

168 pointer = ((cur & ~0xc0) << 8) + s[pointer] 

169 if pointer in processed_pointers: 

170 warning("DNS decompression loop detected") 

171 break 

172 if len(processed_pointers) >= 20: 

173 warning("More than 20 jumps in a single DNS decompression ! " 

174 "Dropping (evil packet)") 

175 break 

176 if not _fullpacket: 

177 # We switch our s buffer to full, so we need to remember 

178 # the previous context 

179 bytes_left = s[after_pointer:] 

180 s = full 

181 max_length = len(s) 

182 _fullpacket = True 

183 processed_pointers.append(pointer) 

184 continue 

185 elif cur > 0: # Label 

186 # cur = length of the string 

187 name += s[pointer:pointer + cur] + b"." 

188 pointer += cur 

189 else: # End 

190 break 

191 if after_pointer is not None: 

192 # Return the real end index (not the one we followed) 

193 pointer = after_pointer 

194 if bytes_left is None: 

195 bytes_left = s[pointer:] 

196 # name, remaining 

197 return name or b".", bytes_left 

198 

199 

200def _is_ptr(x): 

201 """ 

202 Heuristic to guess if bytes are an encoded DNS pointer. 

203 """ 

204 return ( 

205 (x and x[-1] == 0) or 

206 (len(x) >= 2 and (x[-2] & 0xc0) == 0xc0) 

207 ) 

208 

209 

210def dns_encode(x, check_built=False): 

211 """Encodes a bytes string into the DNS format 

212 

213 :param x: the string 

214 :param check_built: detect already-built strings and ignore them 

215 :returns: the encoded bytes string 

216 """ 

217 if not x or x == b".": 

218 return b"\x00" 

219 

220 if check_built and _is_ptr(x): 

221 # The value has already been processed. Do not process it again 

222 return x 

223 

224 # Truncate chunks that cannot be encoded (more than 63 bytes..) 

225 x = b"".join(chb(len(y)) + y for y in (k[:63] for k in x.split(b"."))) 

226 if x[-1:] != b"\x00": 

227 x += b"\x00" 

228 return x 

229 

230 

231def DNSgetstr(*args, **kwargs): 

232 """Legacy function. Deprecated""" 

233 warnings.warn( 

234 "DNSgetstr is deprecated. Use dns_get_str instead.", 

235 DeprecationWarning 

236 ) 

237 return dns_get_str(*args, **kwargs)[:-1] 

238 

239 

240def dns_compress(pkt): 

241 """This function compresses a DNS packet according to compression rules. 

242 """ 

243 if DNS not in pkt: 

244 raise Scapy_Exception("Can only compress DNS layers") 

245 pkt = pkt.copy() 

246 dns_pkt = pkt.getlayer(DNS) 

247 dns_pkt.clear_cache() 

248 build_pkt = raw(dns_pkt) 

249 

250 def field_gen(dns_pkt): 

251 """Iterates through all DNS strings that can be compressed""" 

252 for lay in [dns_pkt.qd, dns_pkt.an, dns_pkt.ns, dns_pkt.ar]: 

253 if not lay: 

254 continue 

255 for current in lay: 

256 for field in current.fields_desc: 

257 if isinstance(field, DNSStrField) or \ 

258 (isinstance(field, MultipleTypeField) and 

259 current.type in [2, 3, 4, 5, 12, 15, 39, 47]): 

260 # Get the associated data and store it accordingly # noqa: E501 

261 dat = current.getfieldval(field.name) 

262 yield current, field.name, dat 

263 

264 def possible_shortens(dat): 

265 """Iterates through all possible compression parts in a DNS string""" 

266 if dat == b".": # we'd lose by compressing it 

267 return 

268 yield dat 

269 for x in range(1, dat.count(b".")): 

270 yield dat.split(b".", x)[x] 

271 data = {} 

272 for current, name, dat in field_gen(dns_pkt): 

273 for part in possible_shortens(dat): 

274 # Encode the data 

275 encoded = dns_encode(part, check_built=True) 

276 if part not in data: 

277 # We have no occurrence of such data, let's store it as a 

278 # possible pointer for future strings. 

279 # We get the index of the encoded data 

280 index = build_pkt.index(encoded) 

281 # The following is used to build correctly the pointer 

282 fb_index = ((index >> 8) | 0xc0) 

283 sb_index = index - (256 * (fb_index - 0xc0)) 

284 pointer = chb(fb_index) + chb(sb_index) 

285 data[part] = [(current, name, pointer, index + 1)] 

286 else: 

287 # This string already exists, let's mark the current field 

288 # with it, so that it gets compressed 

289 data[part].append((current, name)) 

290 _in = data[part][0][3] 

291 build_pkt = build_pkt[:_in] + build_pkt[_in:].replace( 

292 encoded, 

293 b"\0\0", 

294 1 

295 ) 

296 break 

297 # Apply compression rules 

298 for ck in data: 

299 # compression_key is a DNS string 

300 replacements = data[ck] 

301 # replacements is the list of all tuples (layer, field name) 

302 # where this string was found 

303 replace_pointer = replacements.pop(0)[2] 

304 # replace_pointer is the packed pointer that should replace 

305 # those strings. Note that pop remove it from the list 

306 for rep in replacements: 

307 # setfieldval edits the value of the field in the layer 

308 val = rep[0].getfieldval(rep[1]) 

309 assert val.endswith(ck) 

310 kept_string = dns_encode(val[:-len(ck)], check_built=True)[:-1] 

311 new_val = kept_string + replace_pointer 

312 rep[0].setfieldval(rep[1], new_val) 

313 try: 

314 del rep[0].rdlen 

315 except AttributeError: 

316 pass 

317 # End of the compression algorithm 

318 # Destroy the previous DNS layer if needed 

319 if not isinstance(pkt, DNS) and pkt.getlayer(DNS).underlayer: 

320 pkt.getlayer(DNS).underlayer.remove_payload() 

321 return pkt / dns_pkt 

322 return dns_pkt 

323 

324 

325class DNSCompressedPacket(Packet): 

326 """ 

327 Class to mark that a packet contains DNSStrField and supports compression 

328 """ 

329 @abc.abstractmethod 

330 def get_full(self): 

331 pass 

332 

333 

334class DNSStrField(StrLenField): 

335 """ 

336 Special StrField that handles DNS encoding/decoding. 

337 It will also handle DNS decompression. 

338 (may be StrLenField if a length_from is passed), 

339 """ 

340 def any2i(self, pkt, x): 

341 if x and isinstance(x, list): 

342 return [self.h2i(pkt, y) for y in x] 

343 return super(DNSStrField, self).any2i(pkt, x) 

344 

345 def h2i(self, pkt, x): 

346 # Setting a DNSStrField manually (h2i) means any current compression will break 

347 if ( 

348 pkt and 

349 isinstance(pkt.parent, DNSCompressedPacket) and 

350 pkt.parent.raw_packet_cache 

351 ): 

352 pkt.parent.clear_cache() 

353 if not x: 

354 return b"." 

355 x = bytes_encode(x) 

356 if x[-1:] != b"." and not _is_ptr(x): 

357 return x + b"." 

358 return x 

359 

360 def i2m(self, pkt, x): 

361 return dns_encode(x, check_built=True) 

362 

363 def i2len(self, pkt, x): 

364 return len(self.i2m(pkt, x)) 

365 

366 def get_full(self, pkt): 

367 while pkt and not isinstance(pkt, DNSCompressedPacket): 

368 pkt = pkt.parent or pkt.underlayer 

369 if not pkt: 

370 return None 

371 return pkt.get_full() 

372 

373 def getfield(self, pkt, s): 

374 remain = b"" 

375 if self.length_from: 

376 remain, s = super(DNSStrField, self).getfield(pkt, s) 

377 # Decode the compressed DNS message 

378 decoded, left = dns_get_str(s, full=self.get_full(pkt)) 

379 # returns (remaining, decoded) 

380 return left + remain, decoded 

381 

382 

383class DNSTextField(StrLenField): 

384 """ 

385 Special StrLenField that handles DNS TEXT data (16) 

386 """ 

387 

388 islist = 1 

389 

390 def i2h(self, pkt, x): 

391 if not x: 

392 return [] 

393 return x 

394 

395 def m2i(self, pkt, s): 

396 ret_s = list() 

397 tmp_s = s 

398 # RDATA contains a list of strings, each are prepended with 

399 # a byte containing the size of the following string. 

400 while tmp_s: 

401 tmp_len = tmp_s[0] + 1 

402 if tmp_len > len(tmp_s): 

403 log_runtime.info( 

404 "DNS RR TXT prematured end of character-string " 

405 "(size=%i, remaining bytes=%i)", tmp_len, len(tmp_s) 

406 ) 

407 ret_s.append(tmp_s[1:tmp_len]) 

408 tmp_s = tmp_s[tmp_len:] 

409 return ret_s 

410 

411 def any2i(self, pkt, x): 

412 if isinstance(x, (str, bytes)): 

413 return [x] 

414 return x 

415 

416 def i2len(self, pkt, x): 

417 return len(self.i2m(pkt, x)) 

418 

419 def i2m(self, pkt, s): 

420 ret_s = b"" 

421 for text in s: 

422 if not text: 

423 ret_s += b"\x00" 

424 continue 

425 text = bytes_encode(text) 

426 # The initial string must be split into a list of strings 

427 # prepended with theirs sizes. 

428 while len(text) >= 255: 

429 ret_s += b"\xff" + text[:255] 

430 text = text[255:] 

431 # The remaining string is less than 255 bytes long 

432 if len(text): 

433 ret_s += struct.pack("!B", len(text)) + text 

434 return ret_s 

435 

436 

437# RFC 2671 - Extension Mechanisms for DNS (EDNS0) 

438 

439edns0types = {0: "Reserved", 1: "LLQ", 2: "UL", 3: "NSID", 4: "Owner", 

440 5: "DAU", 6: "DHU", 7: "N3U", 8: "edns-client-subnet", 10: "COOKIE", 

441 12: "Padding", 15: "Extended DNS Error"} 

442 

443 

444class _EDNS0Dummy(Packet): 

445 name = "Dummy class that implements extract_padding()" 

446 

447 def extract_padding(self, p): 

448 # type: (bytes) -> Tuple[bytes, Optional[bytes]] 

449 return "", p 

450 

451 

452class EDNS0TLV(_EDNS0Dummy): 

453 name = "DNS EDNS0 TLV" 

454 fields_desc = [ShortEnumField("optcode", 0, edns0types), 

455 FieldLenField("optlen", None, "optdata", fmt="H"), 

456 StrLenField("optdata", "", 

457 length_from=lambda pkt: pkt.optlen)] 

458 

459 @classmethod 

460 def dispatch_hook(cls, _pkt=None, *args, **kargs): 

461 # type: (Optional[bytes], *Any, **Any) -> Type[Packet] 

462 if _pkt is None: 

463 return EDNS0TLV 

464 if len(_pkt) < 2: 

465 return Raw 

466 edns0type = struct.unpack("!H", _pkt[:2])[0] 

467 return EDNS0OPT_DISPATCHER.get(edns0type, EDNS0TLV) 

468 

469 

470class DNSRROPT(Packet): 

471 name = "DNS OPT Resource Record" 

472 fields_desc = [DNSStrField("rrname", ""), 

473 ShortEnumField("type", 41, dnstypes), 

474 ShortEnumField("rclass", 4096, dnsclasses), 

475 ByteField("extrcode", 0), 

476 ByteField("version", 0), 

477 # version 0 means EDNS0 

478 BitEnumField("z", 32768, 16, {32768: "D0"}), 

479 # D0 means DNSSEC OK from RFC 3225 

480 FieldLenField("rdlen", None, length_of="rdata", fmt="H"), 

481 PacketListField("rdata", [], EDNS0TLV, 

482 length_from=lambda pkt: pkt.rdlen)] 

483 

484 

485# draft-cheshire-edns0-owner-option-01 - EDNS0 OWNER Option 

486 

487class EDNS0OWN(_EDNS0Dummy): 

488 name = "EDNS0 Owner (OWN)" 

489 fields_desc = [ShortEnumField("optcode", 4, edns0types), 

490 FieldLenField("optlen", None, count_of="primary_mac", fmt="H"), 

491 ByteField("v", 0), 

492 ByteField("s", 0), 

493 MACField("primary_mac", "00:00:00:00:00:00"), 

494 ConditionalField( 

495 MACField("wakeup_mac", "00:00:00:00:00:00"), 

496 lambda pkt: (pkt.optlen or 0) >= 14), 

497 ConditionalField( 

498 StrLenField("password", "", 

499 length_from=lambda pkt: pkt.optlen - 14), 

500 lambda pkt: (pkt.optlen or 0) >= 18)] 

501 

502 def post_build(self, pkt, pay): 

503 pkt += pay 

504 if self.optlen is None: 

505 pkt = pkt[:2] + struct.pack("!H", len(pkt) - 4) + pkt[4:] 

506 return pkt 

507 

508 

509# RFC 6975 - Signaling Cryptographic Algorithm Understanding in 

510# DNS Security Extensions (DNSSEC) 

511 

512class EDNS0DAU(_EDNS0Dummy): 

513 name = "DNSSEC Algorithm Understood (DAU)" 

514 fields_desc = [ShortEnumField("optcode", 5, edns0types), 

515 FieldLenField("optlen", None, count_of="alg_code", fmt="H"), 

516 FieldListField("alg_code", None, 

517 ByteEnumField("", 0, dnssecalgotypes), 

518 count_from=lambda pkt:pkt.optlen)] 

519 

520 

521class EDNS0DHU(_EDNS0Dummy): 

522 name = "DS Hash Understood (DHU)" 

523 fields_desc = [ShortEnumField("optcode", 6, edns0types), 

524 FieldLenField("optlen", None, count_of="alg_code", fmt="H"), 

525 FieldListField("alg_code", None, 

526 ByteEnumField("", 0, dnssecdigesttypes), 

527 count_from=lambda pkt:pkt.optlen)] 

528 

529 

530class EDNS0N3U(_EDNS0Dummy): 

531 name = "NSEC3 Hash Understood (N3U)" 

532 fields_desc = [ShortEnumField("optcode", 7, edns0types), 

533 FieldLenField("optlen", None, count_of="alg_code", fmt="H"), 

534 FieldListField("alg_code", None, 

535 ByteEnumField("", 0, dnssecnsec3algotypes), 

536 count_from=lambda pkt:pkt.optlen)] 

537 

538 

539# RFC 7871 - Client Subnet in DNS Queries 

540 

541class ClientSubnetv4(StrLenField): 

542 af_familly = socket.AF_INET 

543 af_length = 32 

544 af_default = b"\xc0" # 192.0.0.0 

545 

546 def getfield(self, pkt, s): 

547 # type: (Packet, bytes) -> Tuple[bytes, I] 

548 sz = operator.floordiv(self.length_from(pkt) + 7, 8) 

549 sz = min(sz, operator.floordiv(self.af_length, 8)) 

550 return s[sz:], self.m2i(pkt, s[:sz]) 

551 

552 def m2i(self, pkt, x): 

553 # type: (Optional[Packet], bytes) -> str 

554 padding = self.af_length - self.length_from(pkt) 

555 if padding: 

556 x += b"\x00" * operator.floordiv(padding, 8) 

557 x = x[: operator.floordiv(self.af_length, 8)] 

558 return inet_ntop(self.af_familly, x) 

559 

560 def _pack_subnet(self, subnet, plen=None): 

561 # type: (bytes, Optional[int]) -> bytes 

562 packed_subnet = inet_pton(self.af_familly, plain_str(subnet)) 

563 if plen is not None: 

564 # RFC 7871: ADDRESS MUST be truncated to the number of bits 

565 # indicated by SOURCE PREFIX-LENGTH, padded with 0 bits to the 

566 # end of the last octet needed. Use ceil(plen / 8) bytes and 

567 # zero out any host bits in the last partial byte. 

568 num_bytes = operator.floordiv(plen + 7, 8) 

569 result = bytearray(packed_subnet[:num_bytes]) 

570 rem = plen % 8 

571 if rem and result: 

572 result[-1] &= (0xff << (8 - rem)) & 0xff 

573 return bytes(result) 

574 # When prefix length is not known, strip trailing zero bytes. 

575 for i in list(range(operator.floordiv(self.af_length, 8)))[::-1]: 

576 if packed_subnet[i] != 0: 

577 i += 1 

578 break 

579 return packed_subnet[:i] 

580 

581 def i2m(self, pkt, x): 

582 # type: (Optional[Packet], Optional[Union[str, Net]]) -> bytes 

583 if x is None: 

584 return self.af_default 

585 plen = getattr(pkt, 'source_plen', None) 

586 try: 

587 return self._pack_subnet(x, plen) 

588 except (OSError, socket.error): 

589 pkt.family = 2 

590 return ClientSubnetv6("", "")._pack_subnet(x, plen) 

591 

592 def i2len(self, pkt, x): 

593 # type: (Packet, Any) -> int 

594 if x is None: 

595 return 1 

596 plen = getattr(pkt, 'source_plen', None) 

597 try: 

598 return len(self._pack_subnet(x, plen)) 

599 except (OSError, socket.error): 

600 pkt.family = 2 

601 return len(ClientSubnetv6("", "")._pack_subnet(x, plen)) 

602 

603 

604class ClientSubnetv6(ClientSubnetv4): 

605 af_familly = socket.AF_INET6 

606 af_length = 128 

607 af_default = b"\x20" # 2000:: 

608 

609 

610class EDNS0ClientSubnet(_EDNS0Dummy): 

611 name = "DNS EDNS0 Client Subnet" 

612 fields_desc = [ShortEnumField("optcode", 8, edns0types), 

613 FieldLenField("optlen", None, "address", fmt="H", 

614 adjust=lambda pkt, x: x + 4), 

615 ShortField("family", 1), 

616 FieldLenField("source_plen", None, 

617 length_of="address", 

618 fmt="B", 

619 adjust=lambda pkt, x: x * 8), 

620 ByteField("scope_plen", 0), 

621 MultipleTypeField( 

622 [(ClientSubnetv4("address", "192.168.0.0", 

623 length_from=lambda p: p.source_plen), 

624 lambda pkt: pkt.family == 1), 

625 (ClientSubnetv6("address", "2001:db8::", 

626 length_from=lambda p: p.source_plen), 

627 lambda pkt: pkt.family == 2)], 

628 ClientSubnetv4("address", "192.168.0.0", 

629 length_from=lambda p: p.source_plen))] 

630 

631 

632class EDNS0COOKIE(_EDNS0Dummy): 

633 name = "DNS EDNS0 COOKIE" 

634 fields_desc = [ShortEnumField("optcode", 10, edns0types), 

635 FieldLenField("optlen", None, length_of="server_cookie", fmt="!H", 

636 adjust=lambda pkt, x: x + 8), 

637 XStrFixedLenField("client_cookie", b"\x00" * 8, length=8), 

638 XStrLenField("server_cookie", "", 

639 length_from=lambda pkt: max(0, pkt.optlen - 8))] 

640 

641 

642# RFC 7830 - EDNS0 Padding Option 

643 

644class EDNS0PADDING(_EDNS0Dummy): 

645 name = "DNS EDNS0 Padding" 

646 fields_desc = [ShortEnumField("optcode", 12, edns0types), 

647 FieldLenField("optlen", None, length_of="padding", fmt="!H"), 

648 StrLenField("padding", "", 

649 length_from=lambda pkt: pkt.optlen)] 

650 

651 

652# RFC 8914 - Extended DNS Errors 

653 

654# https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#extended-dns-error-codes 

655extended_dns_error_codes = { 

656 0: "Other", 

657 1: "Unsupported DNSKEY Algorithm", 

658 2: "Unsupported DS Digest Type", 

659 3: "Stale Answer", 

660 4: "Forged Answer", 

661 5: "DNSSEC Indeterminate", 

662 6: "DNSSEC Bogus", 

663 7: "Signature Expired", 

664 8: "Signature Not Yet Valid", 

665 9: "DNSKEY Missing", 

666 10: "RRSIGs Missing", 

667 11: "No Zone Key Bit Set", 

668 12: "NSEC Missing", 

669 13: "Cached Error", 

670 14: "Not Ready", 

671 15: "Blocked", 

672 16: "Censored", 

673 17: "Filtered", 

674 18: "Prohibited", 

675 19: "Stale NXDOMAIN Answer", 

676 20: "Not Authoritative", 

677 21: "Not Supported", 

678 22: "No Reachable Authority", 

679 23: "Network Error", 

680 24: "Invalid Data", 

681 25: "Signature Expired before Valid", 

682 26: "Too Early", 

683 27: "Unsupported NSEC3 Iterations Value", 

684 28: "Unable to conform to policy", 

685 29: "Synthesized", 

686} 

687 

688 

689# https://www.rfc-editor.org/rfc/rfc8914.html 

690class EDNS0ExtendedDNSError(_EDNS0Dummy): 

691 name = "DNS EDNS0 Extended DNS Error" 

692 fields_desc = [ShortEnumField("optcode", 15, edns0types), 

693 FieldLenField("optlen", None, length_of="extra_text", fmt="!H", 

694 adjust=lambda pkt, x: x + 2), 

695 ShortEnumField("info_code", 0, extended_dns_error_codes), 

696 StrLenField("extra_text", "", 

697 length_from=lambda pkt: pkt.optlen - 2)] 

698 

699 

700EDNS0OPT_DISPATCHER = { 

701 4: EDNS0OWN, 

702 5: EDNS0DAU, 

703 6: EDNS0DHU, 

704 7: EDNS0N3U, 

705 8: EDNS0ClientSubnet, 

706 10: EDNS0COOKIE, 

707 12: EDNS0PADDING, 

708 15: EDNS0ExtendedDNSError, 

709} 

710 

711 

712# RFC 4034 - Resource Records for the DNS Security Extensions 

713 

714def bitmap2RRlist(bitmap): 

715 """ 

716 Decode the 'Type Bit Maps' field of the NSEC Resource Record into an 

717 integer list. 

718 """ 

719 # RFC 4034, 4.1.2. The Type Bit Maps Field 

720 

721 RRlist = [] 

722 

723 while bitmap: 

724 

725 if len(bitmap) < 2: 

726 log_runtime.info("bitmap too short (%i)", len(bitmap)) 

727 return 

728 

729 window_block = bitmap[0] # window number 

730 offset = 256 * window_block # offset of the Resource Record 

731 bitmap_len = bitmap[1] # length of the bitmap in bytes 

732 

733 if bitmap_len <= 0 or bitmap_len > 32: 

734 log_runtime.info("bitmap length is no valid (%i)", bitmap_len) 

735 return 

736 

737 tmp_bitmap = bitmap[2:2 + bitmap_len] 

738 

739 # Let's compare each bit of tmp_bitmap and compute the real RR value 

740 for b in range(len(tmp_bitmap)): 

741 v = 128 

742 for i in range(8): 

743 if tmp_bitmap[b] & v: 

744 # each of the RR is encoded as a bit 

745 RRlist += [offset + b * 8 + i] 

746 v = v >> 1 

747 

748 # Next block if any 

749 bitmap = bitmap[2 + bitmap_len:] 

750 

751 return RRlist 

752 

753 

754def RRlist2bitmap(lst): 

755 """ 

756 Encode a list of integers representing Resource Records to a bitmap field 

757 used in the NSEC Resource Record. 

758 """ 

759 # RFC 4034, 4.1.2. The Type Bit Maps Field 

760 

761 import math 

762 

763 bitmap = b"" 

764 lst = [abs(x) for x in sorted(set(lst)) if x <= 65535] 

765 

766 # number of window blocks 

767 max_window_blocks = int(math.ceil(lst[-1] / 256.)) 

768 min_window_blocks = int(math.floor(lst[0] / 256.)) 

769 if min_window_blocks == max_window_blocks: 

770 max_window_blocks += 1 

771 

772 for wb in range(min_window_blocks, max_window_blocks + 1): 

773 # First, filter out RR not encoded in the current window block 

774 # i.e. keep everything between 256*wb <= 256*(wb+1) 

775 rrlist = sorted(x for x in lst if 256 * wb <= x < 256 * (wb + 1)) 

776 if not rrlist: 

777 continue 

778 

779 # Compute the number of bytes used to store the bitmap 

780 if rrlist[-1] == 0: # only one element in the list 

781 bytes_count = 1 

782 else: 

783 max = rrlist[-1] - 256 * wb 

784 bytes_count = int(math.ceil(max // 8)) + 1 # use at least 1 byte 

785 if bytes_count > 32: # Don't encode more than 256 bits / values 

786 bytes_count = 32 

787 

788 bitmap += struct.pack("BB", wb, bytes_count) 

789 

790 # Generate the bitmap 

791 # The idea is to remove out of range Resource Records with these steps 

792 # 1. rescale to fit into 8 bits 

793 # 2. x gives the bit position ; compute the corresponding value 

794 # 3. sum everything 

795 bitmap += b"".join( 

796 struct.pack( 

797 b"B", 

798 sum(2 ** (7 - (x - 256 * wb) + (tmp * 8)) for x in rrlist 

799 if 256 * wb + 8 * tmp <= x < 256 * wb + 8 * tmp + 8), 

800 ) for tmp in range(bytes_count) 

801 ) 

802 

803 return bitmap 

804 

805 

806class RRlistField(StrField): 

807 islist = 1 

808 

809 def h2i(self, pkt, x): 

810 if x and isinstance(x, list): 

811 return RRlist2bitmap(x) 

812 return x 

813 

814 def i2repr(self, pkt, x): 

815 if not x: 

816 return "[]" 

817 x = self.i2h(pkt, x) 

818 rrlist = bitmap2RRlist(x) 

819 return [dnstypes.get(rr, rr) for rr in rrlist] if rrlist else repr(x) 

820 

821 

822class _DNSRRdummy(Packet): 

823 name = "Dummy class that implements post_build() for Resource Records" 

824 

825 def post_build(self, pkt, pay): 

826 if self.rdlen is not None: 

827 return pkt + pay 

828 

829 lrrname = len(self.fields_desc[0].i2m("", self.getfieldval("rrname"))) 

830 tmp_len = len(pkt) - lrrname - 10 

831 tmp_pkt = pkt[:lrrname + 8] 

832 pkt = struct.pack("!H", tmp_len) + pkt[lrrname + 8 + 2:] 

833 

834 return tmp_pkt + pkt + pay 

835 

836 def default_payload_class(self, payload): 

837 return conf.padding_layer 

838 

839 

840class DNSRRHINFO(_DNSRRdummy): 

841 name = "DNS HINFO Resource Record" 

842 fields_desc = [DNSStrField("rrname", ""), 

843 ShortEnumField("type", 13, dnstypes), 

844 BitField("cacheflush", 0, 1), # mDNS RFC 6762 

845 BitEnumField("rclass", 1, 15, dnsclasses), 

846 IntField("ttl", 0), 

847 ShortField("rdlen", None), 

848 FieldLenField("cpulen", None, fmt="!B", length_of="cpu"), 

849 StrLenField("cpu", "", length_from=lambda x: x.cpulen), 

850 FieldLenField("oslen", None, fmt="!B", length_of="os"), 

851 StrLenField("os", "", length_from=lambda x: x.oslen)] 

852 

853 

854class DNSRRMX(_DNSRRdummy): 

855 name = "DNS MX Resource Record" 

856 fields_desc = [DNSStrField("rrname", ""), 

857 ShortEnumField("type", 15, dnstypes), 

858 BitField("cacheflush", 0, 1), # mDNS RFC 6762 

859 BitEnumField("rclass", 1, 15, dnsclasses), 

860 IntField("ttl", 0), 

861 ShortField("rdlen", None), 

862 ShortField("preference", 0), 

863 DNSStrField("exchange", ""), 

864 ] 

865 

866 

867class DNSRRSOA(_DNSRRdummy): 

868 name = "DNS SOA Resource Record" 

869 fields_desc = [DNSStrField("rrname", ""), 

870 ShortEnumField("type", 6, dnstypes), 

871 ShortEnumField("rclass", 1, dnsclasses), 

872 IntField("ttl", 0), 

873 ShortField("rdlen", None), 

874 DNSStrField("mname", ""), 

875 DNSStrField("rname", ""), 

876 IntField("serial", 0), 

877 IntField("refresh", 0), 

878 IntField("retry", 0), 

879 IntField("expire", 0), 

880 IntField("minimum", 0) 

881 ] 

882 

883 

884class DNSRRRSIG(_DNSRRdummy): 

885 name = "DNS RRSIG Resource Record" 

886 fields_desc = [DNSStrField("rrname", ""), 

887 ShortEnumField("type", 46, dnstypes), 

888 BitField("cacheflush", 0, 1), # mDNS RFC 6762 

889 BitEnumField("rclass", 1, 15, dnsclasses), 

890 IntField("ttl", 0), 

891 ShortField("rdlen", None), 

892 ShortEnumField("typecovered", 1, dnstypes), 

893 ByteEnumField("algorithm", 5, dnssecalgotypes), 

894 ByteField("labels", 0), 

895 IntField("originalttl", 0), 

896 UTCTimeField("expiration", 0), 

897 UTCTimeField("inception", 0), 

898 ShortField("keytag", 0), 

899 DNSStrField("signersname", ""), 

900 StrField("signature", "") 

901 ] 

902 

903 

904class DNSRRNSEC(_DNSRRdummy): 

905 name = "DNS NSEC Resource Record" 

906 fields_desc = [DNSStrField("rrname", ""), 

907 ShortEnumField("type", 47, dnstypes), 

908 BitField("cacheflush", 0, 1), # mDNS RFC 6762 

909 BitEnumField("rclass", 1, 15, dnsclasses), 

910 IntField("ttl", 0), 

911 ShortField("rdlen", None), 

912 DNSStrField("nextname", ""), 

913 RRlistField("typebitmaps", []) 

914 ] 

915 

916 

917class DNSRRDNSKEY(_DNSRRdummy): 

918 name = "DNS DNSKEY Resource Record" 

919 fields_desc = [DNSStrField("rrname", ""), 

920 ShortEnumField("type", 48, dnstypes), 

921 BitField("cacheflush", 0, 1), # mDNS RFC 6762 

922 BitEnumField("rclass", 1, 15, dnsclasses), 

923 IntField("ttl", 0), 

924 ShortField("rdlen", None), 

925 FlagsField("flags", 256, 16, "S???????Z???????"), 

926 # S: Secure Entry Point 

927 # Z: Zone Key 

928 ByteField("protocol", 3), 

929 ByteEnumField("algorithm", 5, dnssecalgotypes), 

930 StrField("publickey", "") 

931 ] 

932 

933 

934class DNSRRDS(_DNSRRdummy): 

935 name = "DNS DS Resource Record" 

936 fields_desc = [DNSStrField("rrname", ""), 

937 ShortEnumField("type", 43, dnstypes), 

938 BitField("cacheflush", 0, 1), # mDNS RFC 6762 

939 BitEnumField("rclass", 1, 15, dnsclasses), 

940 IntField("ttl", 0), 

941 ShortField("rdlen", None), 

942 ShortField("keytag", 0), 

943 ByteEnumField("algorithm", 5, dnssecalgotypes), 

944 ByteEnumField("digesttype", 5, dnssecdigesttypes), 

945 StrField("digest", "") 

946 ] 

947 

948 

949# RFC 5074 - DNSSEC Lookaside Validation (DLV) 

950class DNSRRDLV(DNSRRDS): 

951 name = "DNS DLV Resource Record" 

952 

953 def __init__(self, *args, **kargs): 

954 DNSRRDS.__init__(self, *args, **kargs) 

955 if not kargs.get('type', 0): 

956 self.type = 32769 

957 

958# RFC 5155 - DNS Security (DNSSEC) Hashed Authenticated Denial of Existence 

959 

960 

961class DNSRRNSEC3(_DNSRRdummy): 

962 name = "DNS NSEC3 Resource Record" 

963 fields_desc = [DNSStrField("rrname", ""), 

964 ShortEnumField("type", 50, dnstypes), 

965 BitField("cacheflush", 0, 1), # mDNS RFC 6762 

966 BitEnumField("rclass", 1, 15, dnsclasses), 

967 IntField("ttl", 0), 

968 ShortField("rdlen", None), 

969 ByteField("hashalg", 0), 

970 BitEnumField("flags", 0, 8, {1: "Opt-Out"}), 

971 ShortField("iterations", 0), 

972 FieldLenField("saltlength", 0, fmt="!B", length_of="salt"), 

973 StrLenField("salt", "", length_from=lambda x: x.saltlength), 

974 FieldLenField("hashlength", 0, fmt="!B", length_of="nexthashedownername"), # noqa: E501 

975 StrLenField("nexthashedownername", "", length_from=lambda x: x.hashlength), # noqa: E501 

976 RRlistField("typebitmaps", []) 

977 ] 

978 

979 

980class DNSRRNSEC3PARAM(_DNSRRdummy): 

981 name = "DNS NSEC3PARAM Resource Record" 

982 fields_desc = [DNSStrField("rrname", ""), 

983 ShortEnumField("type", 51, dnstypes), 

984 BitField("cacheflush", 0, 1), # mDNS RFC 6762 

985 BitEnumField("rclass", 1, 15, dnsclasses), 

986 IntField("ttl", 0), 

987 ShortField("rdlen", None), 

988 ByteField("hashalg", 0), 

989 ByteField("flags", 0), 

990 ShortField("iterations", 0), 

991 FieldLenField("saltlength", 0, fmt="!B", length_of="salt"), 

992 StrLenField("salt", "", length_from=lambda pkt: pkt.saltlength) # noqa: E501 

993 ] 

994 

995 

996# RFC 9460 Service Binding and Parameter Specification via the DNS 

997# https://www.rfc-editor.org/rfc/rfc9460.html 

998 

999 

1000# https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml 

1001svc_param_keys = { 

1002 0: "mandatory", 

1003 1: "alpn", 

1004 2: "no-default-alpn", 

1005 3: "port", 

1006 4: "ipv4hint", 

1007 5: "ech", 

1008 6: "ipv6hint", 

1009 7: "dohpath", 

1010 8: "ohttp", 

1011} 

1012 

1013 

1014class SvcParam(Packet): 

1015 name = "SvcParam" 

1016 fields_desc = [ShortEnumField("key", 0, svc_param_keys), 

1017 FieldLenField("len", None, length_of="value", fmt="H"), 

1018 MultipleTypeField( 

1019 [ 

1020 # mandatory 

1021 (FieldListField("value", [], 

1022 ShortEnumField("", 0, svc_param_keys), 

1023 length_from=lambda pkt: pkt.len), 

1024 lambda pkt: pkt.key == 0), 

1025 # alpn, no-default-alpn 

1026 (DNSTextField("value", [], 

1027 length_from=lambda pkt: pkt.len), 

1028 lambda pkt: pkt.key in (1, 2)), 

1029 # port 

1030 (ShortField("value", 0), 

1031 lambda pkt: pkt.key == 3), 

1032 # ipv4hint 

1033 (FieldListField("value", [], 

1034 IPField("", "0.0.0.0"), 

1035 length_from=lambda pkt: pkt.len), 

1036 lambda pkt: pkt.key == 4), 

1037 # ipv6hint 

1038 (FieldListField("value", [], 

1039 IP6Field("", "::"), 

1040 length_from=lambda pkt: pkt.len), 

1041 lambda pkt: pkt.key == 6), 

1042 ], 

1043 StrLenField("value", "", 

1044 length_from=lambda pkt:pkt.len))] 

1045 

1046 def extract_padding(self, p): 

1047 return "", p 

1048 

1049 

1050class DNSRRSVCB(_DNSRRdummy): 

1051 name = "DNS SVCB Resource Record" 

1052 fields_desc = [DNSStrField("rrname", ""), 

1053 ShortEnumField("type", 64, dnstypes), 

1054 BitField("cacheflush", 0, 1), # mDNS RFC 6762 

1055 BitEnumField("rclass", 1, 15, dnsclasses), 

1056 IntField("ttl", 0), 

1057 ShortField("rdlen", None), 

1058 ShortField("svc_priority", 0), 

1059 DNSStrField("target_name", ""), 

1060 PacketListField("svc_params", [], SvcParam)] 

1061 

1062 

1063class DNSRRHTTPS(_DNSRRdummy): 

1064 name = "DNS HTTPS Resource Record" 

1065 fields_desc = [DNSStrField("rrname", ""), 

1066 ShortEnumField("type", 65, dnstypes) 

1067 ] + DNSRRSVCB.fields_desc[2:] 

1068 

1069 

1070# RFC 2782 - A DNS RR for specifying the location of services (DNS SRV) 

1071 

1072 

1073class DNSRRSRV(_DNSRRdummy): 

1074 name = "DNS SRV Resource Record" 

1075 fields_desc = [DNSStrField("rrname", ""), 

1076 ShortEnumField("type", 33, dnstypes), 

1077 BitField("cacheflush", 0, 1), # mDNS RFC 6762 

1078 BitEnumField("rclass", 1, 15, dnsclasses), 

1079 IntField("ttl", 0), 

1080 ShortField("rdlen", None), 

1081 ShortField("priority", 0), 

1082 ShortField("weight", 0), 

1083 ShortField("port", 0), 

1084 DNSStrField("target", ""), ] 

1085 

1086 

1087# RFC 2845 - Secret Key Transaction Authentication for DNS (TSIG) 

1088tsig_algo_sizes = {"HMAC-MD5.SIG-ALG.REG.INT": 16, 

1089 "hmac-sha1": 20} 

1090 

1091 

1092class TimeSignedField(Field[int, bytes]): 

1093 def __init__(self, name, default): 

1094 Field.__init__(self, name, default, fmt="6s") 

1095 

1096 def _convert_seconds(self, packed_seconds): 

1097 """Unpack the internal representation.""" 

1098 seconds = struct.unpack("!H", packed_seconds[:2])[0] 

1099 seconds += struct.unpack("!I", packed_seconds[2:])[0] 

1100 return seconds 

1101 

1102 def i2m(self, pkt, seconds): 

1103 """Convert the number of seconds since 1-Jan-70 UTC to the packed 

1104 representation.""" 

1105 

1106 if seconds is None: 

1107 seconds = 0 

1108 

1109 tmp_short = (seconds >> 32) & 0xFFFF 

1110 tmp_int = seconds & 0xFFFFFFFF 

1111 

1112 return struct.pack("!HI", tmp_short, tmp_int) 

1113 

1114 def m2i(self, pkt, packed_seconds): 

1115 """Convert the internal representation to the number of seconds 

1116 since 1-Jan-70 UTC.""" 

1117 

1118 if packed_seconds is None: 

1119 return None 

1120 

1121 return self._convert_seconds(packed_seconds) 

1122 

1123 def i2repr(self, pkt, packed_seconds): 

1124 """Convert the internal representation to a nice one using the RFC 

1125 format.""" 

1126 time_struct = time.gmtime(packed_seconds) 

1127 return time.strftime("%a %b %d %H:%M:%S %Y", time_struct) 

1128 

1129 

1130class DNSRRTSIG(_DNSRRdummy): 

1131 name = "DNS TSIG Resource Record" 

1132 fields_desc = [DNSStrField("rrname", ""), 

1133 ShortEnumField("type", 250, dnstypes), 

1134 ShortEnumField("rclass", 1, dnsclasses), 

1135 IntField("ttl", 0), 

1136 ShortField("rdlen", None), 

1137 DNSStrField("algo_name", "hmac-sha1"), 

1138 TimeSignedField("time_signed", 0), 

1139 ShortField("fudge", 0), 

1140 FieldLenField("mac_len", None, fmt="!H", length_of="mac_data"), # noqa: E501 

1141 StrLenField("mac_data", "", length_from=lambda pkt: pkt.mac_len), # noqa: E501 

1142 ShortField("original_id", 0), 

1143 ShortField("error", 0), 

1144 FieldLenField("other_len", None, fmt="!H", length_of="other_data"), # noqa: E501 

1145 StrLenField("other_data", "", length_from=lambda pkt: pkt.other_len) # noqa: E501 

1146 ] 

1147 

1148 

1149class DNSRRNAPTR(_DNSRRdummy): 

1150 name = "DNS NAPTR Resource Record" 

1151 fields_desc = [DNSStrField("rrname", ""), 

1152 ShortEnumField("type", 35, dnstypes), 

1153 BitField("cacheflush", 0, 1), # mDNS RFC 6762 

1154 BitEnumField("rclass", 1, 15, dnsclasses), 

1155 IntField("ttl", 0), 

1156 ShortField("rdlen", None), 

1157 ShortField("order", 0), 

1158 ShortField("preference", 0), 

1159 FieldLenField("flags_len", None, fmt="!B", length_of="flags"), 

1160 StrLenField("flags", "", length_from=lambda pkt: pkt.flags_len), 

1161 FieldLenField("services_len", None, fmt="!B", length_of="services"), 

1162 StrLenField("services", "", 

1163 length_from=lambda pkt: pkt.services_len), 

1164 FieldLenField("regexp_len", None, fmt="!B", length_of="regexp"), 

1165 StrLenField("regexp", "", length_from=lambda pkt: pkt.regexp_len), 

1166 DNSStrField("replacement", ""), 

1167 ] 

1168 

1169 

1170DNSRR_DISPATCHER = { 

1171 6: DNSRRSOA, # RFC 1035 

1172 13: DNSRRHINFO, # RFC 1035 

1173 15: DNSRRMX, # RFC 1035 

1174 33: DNSRRSRV, # RFC 2782 

1175 35: DNSRRNAPTR, # RFC 2915 

1176 41: DNSRROPT, # RFC 1671 

1177 43: DNSRRDS, # RFC 4034 

1178 46: DNSRRRSIG, # RFC 4034 

1179 47: DNSRRNSEC, # RFC 4034 

1180 48: DNSRRDNSKEY, # RFC 4034 

1181 50: DNSRRNSEC3, # RFC 5155 

1182 51: DNSRRNSEC3PARAM, # RFC 5155 

1183 64: DNSRRSVCB, # RFC 9460 

1184 65: DNSRRHTTPS, # RFC 9460 

1185 250: DNSRRTSIG, # RFC 2845 

1186 32769: DNSRRDLV, # RFC 4431 

1187} 

1188 

1189 

1190class DNSRR(Packet): 

1191 name = "DNS Resource Record" 

1192 show_indent = 0 

1193 fields_desc = [DNSStrField("rrname", ""), 

1194 ShortEnumField("type", 1, dnstypes), 

1195 BitField("cacheflush", 0, 1), # mDNS RFC 6762 

1196 BitEnumField("rclass", 1, 15, dnsclasses), 

1197 IntField("ttl", 0), 

1198 FieldLenField("rdlen", None, length_of="rdata", fmt="H"), 

1199 MultipleTypeField( 

1200 [ 

1201 # A 

1202 (IPField("rdata", "0.0.0.0"), 

1203 lambda pkt: pkt.type == 1), 

1204 # AAAA 

1205 (IP6Field("rdata", "::"), 

1206 lambda pkt: pkt.type == 28), 

1207 # NS, MD, MF, CNAME, PTR, DNAME 

1208 (DNSStrField("rdata", "", 

1209 length_from=lambda pkt: pkt.rdlen), 

1210 lambda pkt: pkt.type in [2, 3, 4, 5, 12, 39]), 

1211 # TEXT 

1212 (DNSTextField("rdata", [""], 

1213 length_from=lambda pkt: pkt.rdlen), 

1214 lambda pkt: pkt.type == 16), 

1215 ], 

1216 StrLenField("rdata", "", 

1217 length_from=lambda pkt:pkt.rdlen) 

1218 )] 

1219 

1220 def default_payload_class(self, payload): 

1221 return conf.padding_layer 

1222 

1223 

1224def _DNSRR(s, **kwargs): 

1225 """ 

1226 DNSRR dispatcher func 

1227 """ 

1228 if s: 

1229 # Try to find the type of the RR using the dispatcher 

1230 _, remain = dns_get_str(s, _ignore_compression=True) 

1231 cls = DNSRR_DISPATCHER.get( 

1232 struct.unpack("!H", remain[:2])[0], 

1233 DNSRR, 

1234 ) 

1235 rrlen = ( 

1236 len(s) - len(remain) + # rrname len 

1237 10 + 

1238 struct.unpack("!H", remain[8:10])[0] 

1239 ) 

1240 pkt = cls(s[:rrlen], **kwargs) / conf.padding_layer(s[rrlen:]) 

1241 # drop rdlen because if rdata was compressed, it will break everything 

1242 # when rebuilding 

1243 del pkt.fields["rdlen"] 

1244 return pkt 

1245 return None 

1246 

1247 

1248class DNSQR(Packet): 

1249 name = "DNS Question Record" 

1250 show_indent = 0 

1251 fields_desc = [DNSStrField("qname", "www.example.com"), 

1252 ShortEnumField("qtype", 1, dnsqtypes), 

1253 BitField("unicastresponse", 0, 1), # mDNS RFC 6762 

1254 BitEnumField("qclass", 1, 15, dnsclasses)] 

1255 

1256 def default_payload_class(self, payload): 

1257 return conf.padding_layer 

1258 

1259 

1260class _DNSPacketListField(PacketListField): 

1261 # A normal PacketListField with backward-compatible hacks 

1262 def any2i(self, pkt, x): 

1263 # type: (Optional[Packet], List[Any]) -> List[Any] 

1264 if x is None: 

1265 warnings.warn( 

1266 ("The DNS fields 'qd', 'an', 'ns' and 'ar' are now " 

1267 "PacketListField(s) ! " 

1268 "Setting a null default should be [] instead of None"), 

1269 DeprecationWarning 

1270 ) 

1271 x = [] 

1272 return super(_DNSPacketListField, self).any2i(pkt, x) 

1273 

1274 def i2h(self, pkt, x): 

1275 # type: (Optional[Packet], List[Packet]) -> Any 

1276 class _list(list): 

1277 """ 

1278 Fake list object to provide compatibility with older DNS fields 

1279 """ 

1280 def __getattr__(self, attr): 

1281 try: 

1282 ret = getattr(self[0], attr) 

1283 warnings.warn( 

1284 ("The DNS fields 'qd', 'an', 'ns' and 'ar' are now " 

1285 "PacketListField(s) ! " 

1286 "To access the first element, use pkt.an[0] instead of " 

1287 "pkt.an"), 

1288 DeprecationWarning 

1289 ) 

1290 return ret 

1291 except AttributeError: 

1292 raise 

1293 return _list(x) 

1294 

1295 

1296class DNS(DNSCompressedPacket): 

1297 name = "DNS" 

1298 FORCE_TCP = False 

1299 fields_desc = [ 

1300 ConditionalField(ShortField("length", None), 

1301 lambda p: p.FORCE_TCP or isinstance(p.underlayer, TCP)), 

1302 ShortField("id", 0), 

1303 BitField("qr", 0, 1), 

1304 BitEnumField("opcode", 0, 4, {0: "QUERY", 1: "IQUERY", 2: "STATUS"}), 

1305 BitField("aa", 0, 1), 

1306 BitField("tc", 0, 1), 

1307 BitField("rd", 1, 1), 

1308 BitField("ra", 0, 1), 

1309 BitField("z", 0, 1), 

1310 # AD and CD bits are defined in RFC 2535 

1311 BitField("ad", 0, 1), # Authentic Data 

1312 BitField("cd", 0, 1), # Checking Disabled 

1313 BitEnumField("rcode", 0, 4, {0: "ok", 1: "format-error", 

1314 2: "server-failure", 3: "name-error", 

1315 4: "not-implemented", 5: "refused"}), 

1316 FieldLenField("qdcount", None, count_of="qd"), 

1317 FieldLenField("ancount", None, count_of="an"), 

1318 FieldLenField("nscount", None, count_of="ns"), 

1319 FieldLenField("arcount", None, count_of="ar"), 

1320 _DNSPacketListField("qd", [DNSQR()], DNSQR, count_from=lambda pkt: pkt.qdcount), 

1321 _DNSPacketListField("an", [], _DNSRR, count_from=lambda pkt: pkt.ancount), 

1322 _DNSPacketListField("ns", [], _DNSRR, count_from=lambda pkt: pkt.nscount), 

1323 _DNSPacketListField("ar", [], _DNSRR, count_from=lambda pkt: pkt.arcount), 

1324 ] 

1325 

1326 def get_full(self): 

1327 # Required for DNSCompressedPacket 

1328 if isinstance(self.underlayer, TCP) or self.FORCE_TCP: 

1329 return self.original[2:] 

1330 else: 

1331 return self.original 

1332 

1333 def answers(self, other): 

1334 return (isinstance(other, DNS) and 

1335 self.id == other.id and 

1336 self.qr == 1 and 

1337 other.qr == 0) 

1338 

1339 def mysummary(self): 

1340 name = "" 

1341 if self.qr: 

1342 type = "Ans" 

1343 if self.an and isinstance(self.an[0], DNSRR): 

1344 name = ' %s' % self.an[0].rdata 

1345 elif self.rcode != 0: 

1346 name = self.sprintf(' %rcode%') 

1347 else: 

1348 type = "Qry" 

1349 if self.qd and isinstance(self.qd[0], DNSQR): 

1350 name = ' %s' % self.qd[0].qname 

1351 return "%sDNS %s%s" % ( 

1352 "m" 

1353 if isinstance(self.underlayer, UDP) and self.underlayer.dport == 5353 

1354 else "", 

1355 type, 

1356 name, 

1357 ) 

1358 

1359 def post_build(self, pkt, pay): 

1360 if ( 

1361 (isinstance(self.underlayer, TCP) or self.FORCE_TCP) and 

1362 self.length is None 

1363 ): 

1364 pkt = struct.pack("!H", len(pkt) - 2) + pkt[2:] 

1365 return pkt + pay 

1366 

1367 def compress(self): 

1368 """Return the compressed DNS packet (using `dns_compress()`)""" 

1369 return dns_compress(self) 

1370 

1371 def pre_dissect(self, s): 

1372 """ 

1373 Check that a valid DNS over TCP message can be decoded 

1374 """ 

1375 if isinstance(self.underlayer, TCP): 

1376 

1377 # Compute the length of the DNS packet 

1378 if len(s) >= 2: 

1379 dns_len = struct.unpack("!H", s[:2])[0] 

1380 else: 

1381 message = "Malformed DNS message: too small!" 

1382 log_runtime.info(message) 

1383 raise Scapy_Exception(message) 

1384 

1385 # Check if the length is valid 

1386 if dns_len < 14 or len(s) < dns_len: 

1387 message = "Malformed DNS message: invalid length!" 

1388 log_runtime.info(message) 

1389 raise Scapy_Exception(message) 

1390 

1391 return s 

1392 

1393 

1394class DNSTCP(DNS): 

1395 """ 

1396 A DNS packet that is always under TCP 

1397 """ 

1398 FORCE_TCP = True 

1399 match_subclass = True 

1400 

1401 

1402bind_layers(UDP, DNS, dport=5353) 

1403bind_layers(UDP, DNS, sport=5353) 

1404bind_layers(UDP, DNS, dport=53) 

1405bind_layers(UDP, DNS, sport=53) 

1406DestIPField.bind_addr(UDP, "224.0.0.251", dport=5353) 

1407if conf.ipv6_enabled: 

1408 from scapy.layers.inet6 import DestIP6Field 

1409 DestIP6Field.bind_addr(UDP, "ff02::fb", dport=5353) 

1410bind_layers(TCP, DNS, dport=53) 

1411bind_layers(TCP, DNS, sport=53) 

1412 

1413# Nameserver config 

1414conf.nameservers = read_nameservers() 

1415_dns_cache = conf.netcache.new_cache("dns_cache", 300) 

1416 

1417 

1418@conf.commands.register 

1419def dns_resolve(qname, qtype="A", raw=False, tcp=False, verbose=1, timeout=3, **kwargs): 

1420 """ 

1421 Perform a simple DNS resolution using conf.nameservers with caching 

1422 

1423 :param qname: the name to query 

1424 :param qtype: the type to query (default A) 

1425 :param raw: return the whole DNS packet (default False) 

1426 :param tcp: whether to use directly TCP instead of UDP. If truncated is received, 

1427 UDP automatically retries in TCP. (default: False) 

1428 :param verbose: show verbose errors 

1429 :param timeout: seconds until timeout (per server) 

1430 :raise TimeoutError: if no DNS servers were reached in time. 

1431 """ 

1432 # Unify types (for caching) 

1433 qtype = DNSQR.qtype.any2i_one(None, qtype) 

1434 qname = DNSQR.qname.any2i(None, qname) 

1435 # Check cache 

1436 cache_ident = b";".join( 

1437 [qname, struct.pack("!H", qtype)] + 

1438 ([b"raw"] if raw else []) 

1439 ) 

1440 result = _dns_cache.get(cache_ident) 

1441 if result: 

1442 return result 

1443 

1444 kwargs.setdefault("timeout", timeout) 

1445 kwargs.setdefault("verbose", 0) # hide sr1() output 

1446 res = None 

1447 for nameserver in conf.nameservers: 

1448 # Try all nameservers 

1449 try: 

1450 # Spawn a socket, connect to the nameserver on port 53 

1451 if tcp: 

1452 cls = DNSTCP 

1453 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

1454 else: 

1455 cls = DNS 

1456 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 

1457 sock.settimeout(kwargs["timeout"]) 

1458 sock.connect((nameserver, 53)) 

1459 # Connected. Wrap it with DNS 

1460 sock = StreamSocket(sock, cls) 

1461 # I/O 

1462 res = sock.sr1( 

1463 cls(qd=[DNSQR(qname=qname, qtype=qtype)], id=RandShort()), 

1464 **kwargs, 

1465 ) 

1466 except IOError as ex: 

1467 if verbose: 

1468 log_runtime.warning(str(ex)) 

1469 continue 

1470 finally: 

1471 sock.close() 

1472 if res: 

1473 # We have a response ! Check for failure 

1474 if res[DNS].tc == 1: # truncated ! 

1475 if not tcp: 

1476 # Retry using TCP 

1477 return dns_resolve( 

1478 qname=qname, 

1479 qtype=qtype, 

1480 raw=raw, 

1481 tcp=True, 

1482 **kwargs, 

1483 ) 

1484 elif verbose: 

1485 log_runtime.info("DNS answer is truncated !") 

1486 

1487 if res[DNS].rcode == 2: # server failure 

1488 res = None 

1489 if verbose: 

1490 log_runtime.info( 

1491 "DNS: %s answered with failure for %s" % ( 

1492 nameserver, 

1493 qname, 

1494 ) 

1495 ) 

1496 else: 

1497 break 

1498 if res is not None: 

1499 if raw: 

1500 # Raw 

1501 result = res 

1502 else: 

1503 # Find answers 

1504 result = [ 

1505 x 

1506 for x in itertools.chain(res.an, res.ns, res.ar) 

1507 if getattr(x, "type", None) == qtype 

1508 ] 

1509 if result: 

1510 # Cache it 

1511 _dns_cache[cache_ident] = result 

1512 return result 

1513 else: 

1514 raise TimeoutError 

1515 

1516 

1517@conf.commands.register 

1518def dyndns_add(nameserver, name, rdata, type="A", ttl=10): 

1519 """Send a DNS add message to a nameserver for "name" to have a new "rdata" 

1520dyndns_add(nameserver, name, rdata, type="A", ttl=10) -> result code (0=ok) 

1521 

1522example: dyndns_add("ns1.toto.com", "dyn.toto.com", "127.0.0.1") 

1523RFC2136 

1524""" 

1525 zone = name[name.find(".") + 1:] 

1526 r = sr1(IP(dst=nameserver) / UDP() / DNS(opcode=5, 

1527 qd=[DNSQR(qname=zone, qtype="SOA")], # noqa: E501 

1528 ns=[DNSRR(rrname=name, type="A", 

1529 ttl=ttl, rdata=rdata)]), 

1530 verbose=0, timeout=5) 

1531 if r and r.haslayer(DNS): 

1532 return r.getlayer(DNS).rcode 

1533 else: 

1534 return -1 

1535 

1536 

1537@conf.commands.register 

1538def dyndns_del(nameserver, name, type="ALL", ttl=10): 

1539 """Send a DNS delete message to a nameserver for "name" 

1540dyndns_del(nameserver, name, type="ANY", ttl=10) -> result code (0=ok) 

1541 

1542example: dyndns_del("ns1.toto.com", "dyn.toto.com") 

1543RFC2136 

1544""" 

1545 zone = name[name.find(".") + 1:] 

1546 r = sr1(IP(dst=nameserver) / UDP() / DNS(opcode=5, 

1547 qd=[DNSQR(qname=zone, qtype="SOA")], # noqa: E501 

1548 ns=[DNSRR(rrname=name, type=type, 

1549 rclass="ANY", ttl=0, rdata="")]), # noqa: E501 

1550 verbose=0, timeout=5) 

1551 if r and r.haslayer(DNS): 

1552 return r.getlayer(DNS).rcode 

1553 else: 

1554 return -1 

1555 

1556 

1557class DNS_am(AnsweringMachine): 

1558 function_name = "dnsd" 

1559 filter = "udp port 53" 

1560 cls = DNS # We also use this automaton for llmnrd / mdnsd 

1561 

1562 def parse_options(self, joker=None, 

1563 match=None, 

1564 srvmatch=None, 

1565 joker6=False, 

1566 send_error=False, 

1567 relay=False, 

1568 from_ip=True, 

1569 from_ip6=False, 

1570 src_ip=None, 

1571 src_ip6=None, 

1572 ttl=10, 

1573 jokerarpa=False): 

1574 """ 

1575 Simple DNS answering machine. 

1576 

1577 :param joker: default IPv4 for unresolved domains. 

1578 Set to False to disable, None to mirror the interface's IP. 

1579 Defaults to None, unless 'match' is used, then it defaults to 

1580 False. 

1581 :param joker6: default IPv6 for unresolved domains. 

1582 Set to False to disable, None to mirror the interface's IPv6. 

1583 Defaults to False. 

1584 :param match: queries to match. 

1585 This can be a dictionary of {name: val} where name is a string 

1586 representing a domain name (A, AAAA) and val is a tuple of 2 

1587 elements, each representing an IP or a list of IPs. If val is 

1588 a single element, (A, None) is assumed. 

1589 This can also be a list or names, in which case joker(6) are 

1590 used as a response. 

1591 :param jokerarpa: answer for .in-addr.arpa PTR requests. (Default: False) 

1592 :param relay: relay unresolved domains to conf.nameservers (Default: False). 

1593 :param send_error: send an error message when this server can't answer 

1594 (Default: False) 

1595 :param srvmatch: a dictionary of {name: (port, target)} used for SRV 

1596 :param from_ip: an source IP to filter. Can contain a netmask. True for all, 

1597 False for none. Default True 

1598 :param from_ip6: an source IPv6 to filter. Can contain a netmask. True for all, 

1599 False for none. Default False 

1600 :param ttl: the DNS time to live (in seconds) 

1601 :param src_ip: override the source IP 

1602 :param src_ip6: 

1603 

1604 Examples: 

1605 

1606 - Answer all 'A' and 'AAAA' requests:: 

1607 

1608 $ sudo iptables -I OUTPUT -p icmp --icmp-type 3/3 -j DROP 

1609 >>> dnsd(joker="192.168.0.2", joker6="fe80::260:8ff:fe52:f9d8", 

1610 ... iface="eth0") 

1611 

1612 - Answer only 'A' query for google.com with 192.168.0.2:: 

1613 

1614 >>> dnsd(match={"google.com": "192.168.0.2"}, iface="eth0") 

1615 

1616 - Answer DNS for a Windows domain controller ('SRV', 'A' and 'AAAA'):: 

1617 

1618 >>> dnsd( 

1619 ... srvmatch={ 

1620 ... "_ldap._tcp.dc._msdcs.DOMAIN.LOCAL.": (389, 

1621 ... "srv1.domain.local"), 

1622 ... }, 

1623 ... match={"src1.domain.local": ("192.168.0.102", 

1624 ... "fe80::260:8ff:fe52:f9d8")}, 

1625 ... ) 

1626 

1627 - Relay all queries to another DNS server, except some:: 

1628 

1629 >>> conf.nameservers = ["1.1.1.1"] # server to relay to 

1630 >>> dnsd( 

1631 ... match={"test.com": "1.1.1.1"}, 

1632 ... relay=True, 

1633 ... ) 

1634 """ 

1635 from scapy.layers.inet6 import Net6 

1636 

1637 self.mDNS = isinstance(self, mDNS_am) 

1638 self.llmnr = self.cls != DNS 

1639 

1640 # Add some checks (to help) 

1641 if not isinstance(joker, (str, bool)) and joker is not None: 

1642 raise ValueError("Bad 'joker': should be an IPv4 (str) or False !") 

1643 if not isinstance(joker6, (str, bool)) and joker6 is not None: 

1644 raise ValueError("Bad 'joker6': should be an IPv6 (str) or False !") 

1645 if not isinstance(jokerarpa, (str, bool)): 

1646 raise ValueError("Bad 'jokerarpa': should be a hostname or False !") 

1647 if not isinstance(from_ip, (str, Net, bool)): 

1648 raise ValueError("Bad 'from_ip': should be an IPv4 (str), Net or False !") 

1649 if not isinstance(from_ip6, (str, Net6, bool)): 

1650 raise ValueError("Bad 'from_ip6': should be an IPv6 (str), Net or False !") 

1651 if self.mDNS and src_ip: 

1652 raise ValueError("Cannot use 'src_ip' in mDNS !") 

1653 if self.mDNS and src_ip6: 

1654 raise ValueError("Cannot use 'src_ip6' in mDNS !") 

1655 

1656 if joker is None and match is not None: 

1657 joker = False 

1658 self.joker = joker 

1659 self.joker6 = joker6 

1660 self.jokerarpa = jokerarpa 

1661 

1662 def normv(v): 

1663 if isinstance(v, (tuple, list)) and len(v) == 2: 

1664 return tuple(v) 

1665 elif isinstance(v, str): 

1666 return (v, joker6) 

1667 else: 

1668 raise ValueError("Bad match value: '%s'" % repr(v)) 

1669 

1670 def normk(k): 

1671 k = bytes_encode(k).lower() 

1672 if not k.endswith(b"."): 

1673 k += b"." 

1674 return k 

1675 

1676 self.match = {} 

1677 if match: 

1678 if isinstance(match, (list, set)): 

1679 self.match.update({normk(k): (None, None) for k in match}) 

1680 else: 

1681 self.match.update({normk(k): normv(v) for k, v in match.items()}) 

1682 if srvmatch is None: 

1683 self.srvmatch = {} 

1684 else: 

1685 self.srvmatch = {normk(k): normv(v) for k, v in srvmatch.items()} 

1686 

1687 self.send_error = send_error 

1688 self.relay = relay 

1689 if isinstance(from_ip, str): 

1690 self.from_ip = Net(from_ip) 

1691 else: 

1692 self.from_ip = from_ip 

1693 if isinstance(from_ip6, str): 

1694 self.from_ip6 = Net6(from_ip6) 

1695 else: 

1696 self.from_ip6 = from_ip6 

1697 self.src_ip = src_ip 

1698 self.src_ip6 = src_ip6 

1699 self.ttl = ttl 

1700 

1701 def is_request(self, req): 

1702 from scapy.layers.inet6 import IPv6 

1703 query = req.getlayer(self.cls) 

1704 if query is None or query.qr != 0: 

1705 return False 

1706 if self.llmnr: 

1707 # [RFC 4795] sect 2.1: an LLMNR query goes to the link-scope 

1708 # multicast address with a hop limit of 1, so a responder must 

1709 # not answer one that arrived any other way. mDNS is left alone: 

1710 # it permits a direct unicast query and LLMNR does not. 

1711 network = query.underlayer.underlayer 

1712 if isinstance(network, IPv6): 

1713 if network.dst != "ff02::1:3" or network.hlim != 1: 

1714 return False 

1715 elif isinstance(network, IP): 

1716 if network.dst != "224.0.0.252" or network.ttl != 1: 

1717 return False 

1718 else: 

1719 return False 

1720 return ( 

1721 ( 

1722 self.from_ip6 is True or 

1723 (self.from_ip6 and req[IPv6].src in self.from_ip6) 

1724 ) 

1725 if IPv6 in req else 

1726 ( 

1727 self.from_ip is True or 

1728 (self.from_ip and req[IP].src in self.from_ip) 

1729 ) 

1730 ) 

1731 

1732 def make_reply(self, req): 

1733 # Build reply from the request 

1734 resp = req.copy() 

1735 if Ether in req: 

1736 if self.mDNS: 

1737 resp[Ether].src, resp[Ether].dst = None, None 

1738 elif self.llmnr: 

1739 resp[Ether].src, resp[Ether].dst = None, req[Ether].src 

1740 else: 

1741 resp[Ether].src, resp[Ether].dst = ( 

1742 None if req[Ether].dst == "ff:ff:ff:ff:ff:ff" else req[Ether].dst, 

1743 req[Ether].src, 

1744 ) 

1745 from scapy.layers.inet6 import IPv6 

1746 if IPv6 in req: 

1747 resp[IPv6].underlayer.remove_payload() 

1748 if self.mDNS: 

1749 # "All Multicast DNS responses (including responses sent via unicast) 

1750 # SHOULD be sent with IP TTL set to 255." 

1751 resp /= IPv6(dst="ff02::fb", src=self.src_ip6, 

1752 fl=req[IPv6].fl, hlim=255) 

1753 elif self.llmnr: 

1754 resp /= IPv6(dst=req[IPv6].src, src=self.src_ip6, 

1755 fl=req[IPv6].fl, hlim=req[IPv6].hlim) 

1756 else: 

1757 resp /= IPv6(dst=req[IPv6].src, src=self.src_ip6 or req[IPv6].dst, 

1758 fl=req[IPv6].fl, hlim=req[IPv6].hlim) 

1759 elif IP in req: 

1760 resp[IP].underlayer.remove_payload() 

1761 if self.mDNS: 

1762 # "All Multicast DNS responses (including responses sent via unicast) 

1763 # SHOULD be sent with IP TTL set to 255." 

1764 resp /= IP(dst="224.0.0.251", src=self.src_ip, 

1765 id=req[IP].id, ttl=255) 

1766 elif self.llmnr: 

1767 resp /= IP(dst=req[IP].src, src=self.src_ip, 

1768 id=req[IP].id, ttl=req[IP].ttl) 

1769 else: 

1770 resp /= IP(dst=req[IP].src, src=self.src_ip or req[IP].dst, 

1771 id=req[IP].id, ttl=req[IP].ttl) 

1772 else: 

1773 warning("No IP or IPv6 layer in %s", req.command()) 

1774 return 

1775 try: 

1776 resp /= UDP(sport=req[UDP].dport, dport=req[UDP].sport) 

1777 except IndexError: 

1778 warning("No UDP layer in %s", req.command(), exc_info=True) 

1779 return 

1780 try: 

1781 req = req[self.cls] 

1782 except IndexError: 

1783 warning( 

1784 "No %s layer in %s", 

1785 self.cls.__name__, 

1786 req.command(), 

1787 exc_info=True, 

1788 ) 

1789 return 

1790 try: 

1791 queries = req.qd 

1792 except AttributeError: 

1793 warning("No qd attribute in %s", req.command(), exc_info=True) 

1794 return 

1795 # Special case: alias 'ALL' query as 'A' + 'AAAA' 

1796 try: 

1797 allquery = next( 

1798 (x for x in queries if getattr(x, "qtype", None) == 255) 

1799 ) 

1800 queries.remove(allquery) 

1801 queries.extend([ 

1802 DNSQR( 

1803 qtype=x, 

1804 qname=allquery.qname, 

1805 unicastresponse=allquery.unicastresponse, 

1806 qclass=allquery.qclass, 

1807 ) 

1808 for x in [1, 28] 

1809 ]) 

1810 except StopIteration: 

1811 pass 

1812 # Process each query 

1813 ans = [] 

1814 ars = [] 

1815 for rq in queries: 

1816 if isinstance(rq, Raw): 

1817 warning("Cannot parse qd element %s", rq.command(), exc_info=True) 

1818 continue 

1819 rqname = rq.qname.lower() 

1820 if rq.qtype in [1, 28]: 

1821 # A or AAAA 

1822 if rq.qtype == 28: 

1823 # AAAA 

1824 try: 

1825 rdata = self.match[rqname][1] 

1826 except KeyError: 

1827 rdata = self.joker6 

1828 if rdata is None and not self.relay: 

1829 # 'None' resolves to the default IPv6 

1830 iface = resolve_iface(self.optsniff.get("iface", conf.iface)) 

1831 if self.mDNS: 

1832 # All IPs, as per mDNS. 

1833 rdata = iface.ips[6] 

1834 else: 

1835 rdata = get_if_addr6( 

1836 iface 

1837 ) 

1838 if self.mDNS and rdata and IPv6 in resp: 

1839 # For mDNS, we must replace the IPv6 src 

1840 resp[IPv6].src = rdata 

1841 elif rq.qtype == 1: 

1842 # A 

1843 try: 

1844 rdata = self.match[rqname][0] 

1845 except KeyError: 

1846 rdata = self.joker 

1847 if rdata is None and not self.relay: 

1848 # 'None' resolves to the default IPv4 

1849 iface = resolve_iface(self.optsniff.get("iface", conf.iface)) 

1850 if self.mDNS: 

1851 # All IPs, as per mDNS. 

1852 rdata = iface.ips[4] 

1853 else: 

1854 rdata = get_if_addr( 

1855 iface 

1856 ) 

1857 if self.mDNS and rdata and IP in resp: 

1858 # For mDNS, we must replace the IP src 

1859 resp[IP].src = rdata 

1860 if rdata: 

1861 # Common A and AAAA 

1862 if not isinstance(rdata, list): 

1863 rdata = [rdata] 

1864 ans.extend([ 

1865 DNSRR( 

1866 rrname=rq.qname, 

1867 ttl=self.ttl, 

1868 rdata=x, 

1869 type=rq.qtype, 

1870 cacheflush=self.mDNS and rq.qtype == rq.qtype, 

1871 ) 

1872 for x in rdata 

1873 ]) 

1874 continue # next 

1875 elif rq.qtype == 33: 

1876 # SRV 

1877 try: 

1878 port, target = self.srvmatch[rqname] 

1879 ans.append(DNSRRSRV( 

1880 rrname=rq.qname, 

1881 port=port, 

1882 target=target, 

1883 weight=100, 

1884 ttl=self.ttl 

1885 )) 

1886 continue # next 

1887 except KeyError: 

1888 # No result 

1889 pass 

1890 elif rq.qtype == 12: 

1891 # PTR 

1892 if rq.qname[-14:] == b".in-addr.arpa." and self.jokerarpa: 

1893 ans.append(DNSRR( 

1894 rrname=rq.qname, 

1895 type=rq.qtype, 

1896 ttl=self.ttl, 

1897 rdata=self.jokerarpa, 

1898 )) 

1899 continue 

1900 # It it arrives here, there is currently no answer 

1901 if self.relay: 

1902 # Relay mode ? 

1903 try: 

1904 _rslv = dns_resolve(rq.qname, qtype=rq.qtype, raw=True) 

1905 if _rslv: 

1906 ans.extend(_rslv.an) 

1907 ars.extend(_rslv.ar) 

1908 continue # next 

1909 except TimeoutError: 

1910 pass 

1911 # Still no answer. 

1912 if self.mDNS: 

1913 # "Any time a responder receives a query for a name for which it 

1914 # has verified exclusive ownership, for a type for which that name 

1915 # has no records, the responder MUST respond asserting the 

1916 # nonexistence of that record using a DNS NSEC record [RFC4034]." 

1917 ans.append(DNSRRNSEC( 

1918 # RFC6762 sect 6.1 - Negative Response 

1919 ttl=self.ttl, 

1920 rrname=rq.qname, 

1921 nextname=rq.qname, 

1922 typebitmaps=RRlist2bitmap([rq.qtype]), 

1923 )) 

1924 if self.mDNS and all(x.type == 47 for x in ans): 

1925 # If mDNS answers with only NSEC, discard. 

1926 return 

1927 if not ans: 

1928 # No answer is available. 

1929 if self.send_error: 

1930 resp /= self.cls(id=req.id, qr=1, qd=req.qd, rcode=3) 

1931 return resp 

1932 log_runtime.info("No answer could be provided to: %s" % req.summary()) 

1933 return 

1934 # Handle Additional Records 

1935 if self.mDNS: 

1936 # Windows specific extension 

1937 ars.append(DNSRROPT( 

1938 z=0x1194, 

1939 rdata=[ 

1940 EDNS0OWN( 

1941 primary_mac=resp[Ether].src, 

1942 ), 

1943 ], 

1944 )) 

1945 # All rq were answered 

1946 if self.mDNS: 

1947 # in mDNS mode, don't repeat the question, set aa=1, rd=0 

1948 dns = self.cls(id=req.id, aa=1, rd=0, qr=1, qd=[], ar=ars, an=ans) 

1949 else: 

1950 dns = self.cls(id=req.id, qr=1, qd=req.qd, ar=ars, an=ans) 

1951 # Compress DNS and mDNS 

1952 if not self.llmnr: 

1953 resp /= dns_compress(dns) 

1954 else: 

1955 resp /= dns 

1956 return resp 

1957 

1958 

1959class mDNS_am(DNS_am): 

1960 """ 

1961 mDNS answering machine. 

1962 

1963 This has the same arguments as DNS_am. See help(DNS_am) 

1964 

1965 Example:: 

1966 

1967 - Answer for 'TEST.local' with local IPv4:: 

1968 

1969 >>> mdnsd(match=["TEST.local"]) 

1970 

1971 - Answer all requests with other IP:: 

1972 

1973 >>> mdnsd(joker="192.168.0.2", joker6="fe80::260:8ff:fe52:f9d8", 

1974 ... iface="eth0") 

1975 

1976 - Answer for multiple different mDNS names:: 

1977 

1978 >>> mdnsd(match={"TEST.local": "192.168.0.100", 

1979 ... "BOB.local": "192.168.0.101"}) 

1980 

1981 - Answer with both A and AAAA records:: 

1982 

1983 >>> mdnsd(match={"TEST.local": ("192.168.0.100", 

1984 ... "fe80::260:8ff:fe52:f9d8")}) 

1985 """ 

1986 function_name = "mdnsd" 

1987 filter = "udp port 5353" 

1988 

1989 

1990# DNS-SD (RFC 6763) 

1991 

1992 

1993class DNSSDResult(SndRcvList): 

1994 def __init__(self, 

1995 res=None, # type: Optional[Union[_PacketList[QueryAnswer], List[QueryAnswer]]] # noqa: E501 

1996 name="DNS-SD", # type: str 

1997 stats=None # type: Optional[List[Type[Packet]]] 

1998 ): 

1999 SndRcvList.__init__(self, res, name, stats) 

2000 

2001 def show(self, types=['PTR', 'SRV'], alltypes=False): 

2002 # type: (List[str], bool) -> None 

2003 """ 

2004 Print the list of discovered services. 

2005 

2006 :param types: types to show. Default ['PTR', 'SRV'] 

2007 :param alltypes: show all types. Default False 

2008 """ 

2009 if alltypes: 

2010 types = None 

2011 data = list() # type: List[Tuple[str | List[str], ...]] 

2012 

2013 resolve_mac = ( 

2014 self.res and isinstance(self.res[0][1].underlayer, Ether) and 

2015 conf.manufdb 

2016 ) 

2017 

2018 header = ("IP", "Service") 

2019 if resolve_mac: 

2020 header = ("Mac",) + header 

2021 

2022 for _, r in self.res: 

2023 attrs = [] 

2024 for attr in itertools.chain(r[DNS].an, r[DNS].ar): 

2025 if types and dnstypes.get(attr.type) not in types: 

2026 continue 

2027 if isinstance(attr, DNSRRNSEC): 

2028 attrs.append(attr.sprintf("%type%=%nextname%")) 

2029 elif isinstance(attr, DNSRRSRV): 

2030 attrs.append(attr.sprintf("%type%=(%target%,%port%)")) 

2031 else: 

2032 attrs.append(attr.sprintf("%type%=%rdata%")) 

2033 ans = (r.src, attrs) 

2034 if resolve_mac: 

2035 mac = conf.manufdb._resolve_MAC(r.underlayer.src) 

2036 data.append((mac,) + ans) 

2037 else: 

2038 data.append(ans) 

2039 

2040 print( 

2041 pretty_list( 

2042 data, 

2043 [header], 

2044 ) 

2045 ) 

2046 

2047 

2048@conf.commands.register 

2049def dnssd(service="_services._dns-sd._udp.local", 

2050 af=socket.AF_INET, 

2051 qtype="PTR", 

2052 iface=None, 

2053 verbose=2, 

2054 timeout=3): 

2055 """ 

2056 Performs a DNS-SD (RFC6763) request 

2057 

2058 :param service: the service name to query (e.g. _spotify-connect._tcp.local) 

2059 :param af: the transport to use. socket.AF_INET or socket.AF_INET6 

2060 :param qtype: the type to use in the mDNS. Either TXT, PTR or SRV. 

2061 :param iface: the interface to do this discovery on. 

2062 

2063 Examples:: 

2064 

2065 >>> dnssd(service='_companion-link._tcp.local.').show() 

2066 >>> dnssd(service='_spotify-connect._tcp.local.').show() 

2067 """ 

2068 if af == socket.AF_INET: 

2069 pkt = IP(dst=ScopedIP("224.0.0.251", iface), ttl=255) 

2070 elif af == socket.AF_INET6: 

2071 pkt = IPv6(dst=ScopedIP("ff02::fb", iface)) 

2072 else: 

2073 return 

2074 pkt /= UDP(sport=5353, dport=5353) 

2075 pkt /= DNS(rd=0, qd=[DNSQR(qname=service, qtype=qtype)]) 

2076 ans, _ = sr(pkt, multi=True, timeout=timeout, verbose=verbose) 

2077 return DNSSDResult(ans.res)