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

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

831 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 collections 

17import operator 

18import itertools 

19import socket 

20import struct 

21import time 

22import warnings 

23 

24from scapy.arch import ( 

25 get_if_addr, 

26 get_if_addr6, 

27 read_nameservers, 

28) 

29from scapy.ansmachine import AnsweringMachine 

30from scapy.base_classes import Net, ScopedIP 

31from scapy.config import conf 

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

33from scapy.error import log_runtime, warning, Scapy_Exception 

34from scapy.packet import Packet, bind_layers, Raw 

35from scapy.fields import ( 

36 BitEnumField, 

37 BitField, 

38 ByteEnumField, 

39 ByteField, 

40 ConditionalField, 

41 Field, 

42 FieldLenField, 

43 FieldListField, 

44 FlagsField, 

45 I, 

46 IP6Field, 

47 IntField, 

48 MACField, 

49 MultipleTypeField, 

50 PacketListField, 

51 ShortEnumField, 

52 ShortField, 

53 StrField, 

54 StrLenField, 

55 UTCTimeField, 

56 XStrFixedLenField, 

57 XStrLenField, 

58) 

59from scapy.interfaces import resolve_iface 

60from scapy.sendrecv import sr1, sr 

61from scapy.supersocket import StreamSocket 

62from scapy.plist import SndRcvList, _PacketList, QueryAnswer 

63from scapy.pton_ntop import inet_ntop, inet_pton 

64from scapy.utils import pretty_list 

65from scapy.volatile import RandShort 

66 

67from scapy.layers.l2 import Ether 

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

69from scapy.layers.inet6 import IPv6 

70 

71from typing import ( 

72 Any, 

73 List, 

74 Optional, 

75 Tuple, 

76 Type, 

77 Union, 

78) 

79 

80 

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

82dnstypes = { 

83 0: "RESERVED", 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

99} 

100 

101 

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

103dnsqtypes.update(dnstypes) 

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

105 

106 

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

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

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

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

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

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

113 15: "Ed25519", 16: "Ed448", 

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

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

116 

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

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

119 

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

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

122 

123 

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

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

126 from the given pointer. 

127 

128 :param s: the string to decompress 

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

130 

131 :returns: (decoded_string, end_index, left_string) 

132 """ 

133 # _ignore_compression is for internal use only 

134 max_length = len(s) 

135 # The result = the extracted name 

136 name = b"" 

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

138 after_pointer = None 

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

140 bytes_left = None 

141 _fullpacket = False # s = full packet 

142 pointer = 0 

143 while True: 

144 if abs(pointer) >= max_length: 

145 log_runtime.info( 

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

147 ) 

148 break 

149 cur = s[pointer] # get pointer value 

150 pointer += 1 # make pointer go forward 

151 if cur & 0xc0: # Label pointer 

152 if after_pointer is None: 

153 # after_pointer points to where the remaining bytes start, 

154 # as pointer will follow the jump token 

155 after_pointer = pointer + 1 

156 if _ignore_compression: 

157 # skip 

158 pointer += 1 

159 continue 

160 if pointer >= max_length: 

161 log_runtime.info( 

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

163 ) 

164 break 

165 if not full: 

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

167 "at this point!") 

168 # Follow the pointer 

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

170 if pointer in processed_pointers: 

171 warning("DNS decompression loop detected") 

172 break 

173 if len(processed_pointers) >= 20: 

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

175 "Dropping (evil packet)") 

176 break 

177 if not _fullpacket: 

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

179 # the previous context 

180 bytes_left = s[after_pointer:] 

181 s = full 

182 max_length = len(s) 

183 _fullpacket = True 

184 processed_pointers.append(pointer) 

185 continue 

186 elif cur > 0: # Label 

187 # cur = length of the string 

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

189 pointer += cur 

190 else: # End 

191 break 

192 if after_pointer is not None: 

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

194 pointer = after_pointer 

195 if bytes_left is None: 

196 bytes_left = s[pointer:] 

197 # name, remaining 

198 return name or b".", bytes_left 

199 

200 

201def _is_ptr(x): 

202 """ 

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

204 """ 

205 return ( 

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

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

208 ) 

209 

210 

211def dns_encode(x, check_built=False): 

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

213 

214 :param x: the string 

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

216 :returns: the encoded bytes string 

217 """ 

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

219 return b"\x00" 

220 

221 if check_built and _is_ptr(x): 

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

223 return x 

224 

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

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

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

228 x += b"\x00" 

229 return x 

230 

231 

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

233 """Legacy function. Deprecated""" 

234 warnings.warn( 

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

236 DeprecationWarning 

237 ) 

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

239 

240 

241def dns_compress(pkt): 

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

243 """ 

244 if DNS not in pkt: 

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

246 pkt = pkt.copy() 

247 dns_pkt = pkt.getlayer(DNS) 

248 dns_pkt.clear_cache() 

249 build_pkt = raw(dns_pkt) 

250 

251 def field_gen(dns_pkt): 

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

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

254 if not lay: 

255 continue 

256 for current in lay: 

257 for field in current.fields_desc: 

258 if isinstance(field, DNSStrField) or \ 

259 (isinstance(field, MultipleTypeField) and 

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

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

262 dat = current.getfieldval(field.name) 

263 yield current, field.name, dat 

264 

265 def possible_shortens(dat): 

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

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

268 return 

269 yield dat 

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

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

272 data = {} 

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

274 for part in possible_shortens(dat): 

275 # Encode the data 

276 encoded = dns_encode(part, check_built=True) 

277 if part not in data: 

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

279 # possible pointer for future strings. 

280 # We get the index of the encoded data 

281 index = build_pkt.index(encoded) 

282 # The following is used to build correctly the pointer 

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

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

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

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

287 else: 

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

289 # with it, so that it gets compressed 

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

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

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

293 encoded, 

294 b"\0\0", 

295 1 

296 ) 

297 break 

298 # Apply compression rules 

299 for ck in data: 

300 # compression_key is a DNS string 

301 replacements = data[ck] 

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

303 # where this string was found 

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

305 # replace_pointer is the packed pointer that should replace 

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

307 for rep in replacements: 

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

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

310 assert val.endswith(ck) 

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

312 new_val = kept_string + replace_pointer 

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

314 try: 

315 del rep[0].rdlen 

316 except AttributeError: 

317 pass 

318 # End of the compression algorithm 

319 # Destroy the previous DNS layer if needed 

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

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

322 return pkt / dns_pkt 

323 return dns_pkt 

324 

325 

326class DNSCompressedPacket(Packet): 

327 """ 

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

329 """ 

330 @abc.abstractmethod 

331 def get_full(self): 

332 pass 

333 

334 

335class DNSStrField(StrLenField): 

336 """ 

337 Special StrField that handles DNS encoding/decoding. 

338 It will also handle DNS decompression. 

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

340 """ 

341 def any2i(self, pkt, x): 

342 if x and isinstance(x, list): 

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

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

345 

346 def h2i(self, pkt, x): 

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

348 if ( 

349 pkt and 

350 isinstance(pkt.parent, DNSCompressedPacket) and 

351 pkt.parent.raw_packet_cache 

352 ): 

353 pkt.parent.clear_cache() 

354 if not x: 

355 return b"." 

356 x = bytes_encode(x) 

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

358 return x + b"." 

359 return x 

360 

361 def i2m(self, pkt, x): 

362 return dns_encode(x, check_built=True) 

363 

364 def i2len(self, pkt, x): 

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

366 

367 def get_full(self, pkt): 

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

369 pkt = pkt.parent or pkt.underlayer 

370 if not pkt: 

371 return None 

372 return pkt.get_full() 

373 

374 def getfield(self, pkt, s): 

375 remain = b"" 

376 if self.length_from: 

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

378 # Decode the compressed DNS message 

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

380 # returns (remaining, decoded) 

381 return left + remain, decoded 

382 

383 

384class DNSTextField(StrLenField): 

385 """ 

386 Special StrLenField that handles DNS TEXT data (16) 

387 """ 

388 

389 islist = 1 

390 

391 def i2h(self, pkt, x): 

392 if not x: 

393 return [] 

394 return x 

395 

396 def m2i(self, pkt, s): 

397 ret_s = list() 

398 tmp_s = s 

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

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

401 while tmp_s: 

402 tmp_len = tmp_s[0] + 1 

403 if tmp_len > len(tmp_s): 

404 log_runtime.info( 

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

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

407 ) 

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

409 tmp_s = tmp_s[tmp_len:] 

410 return ret_s 

411 

412 def any2i(self, pkt, x): 

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

414 return [x] 

415 return x 

416 

417 def i2len(self, pkt, x): 

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

419 

420 def i2m(self, pkt, s): 

421 ret_s = b"" 

422 for text in s: 

423 if not text: 

424 ret_s += b"\x00" 

425 continue 

426 text = bytes_encode(text) 

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

428 # prepended with theirs sizes. 

429 while len(text) >= 255: 

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

431 text = text[255:] 

432 # The remaining string is less than 255 bytes long 

433 if len(text): 

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

435 return ret_s 

436 

437 

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

439 

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

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

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

443 

444 

445class _EDNS0Dummy(Packet): 

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

447 

448 def extract_padding(self, p): 

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

450 return "", p 

451 

452 

453class EDNS0TLV(_EDNS0Dummy): 

454 name = "DNS EDNS0 TLV" 

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

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

457 StrLenField("optdata", "", 

458 length_from=lambda pkt: pkt.optlen)] 

459 

460 @classmethod 

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

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

463 if _pkt is None: 

464 return EDNS0TLV 

465 if len(_pkt) < 2: 

466 return Raw 

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

468 return EDNS0OPT_DISPATCHER.get(edns0type, EDNS0TLV) 

469 

470 

471class DNSRROPT(Packet): 

472 name = "DNS OPT Resource Record" 

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

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

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

476 ByteField("extrcode", 0), 

477 ByteField("version", 0), 

478 # version 0 means EDNS0 

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

480 # D0 means DNSSEC OK from RFC 3225 

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

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

483 length_from=lambda pkt: pkt.rdlen)] 

484 

485 

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

487 

488class EDNS0OWN(_EDNS0Dummy): 

489 name = "EDNS0 Owner (OWN)" 

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

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

492 ByteField("v", 0), 

493 ByteField("s", 0), 

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

495 ConditionalField( 

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

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

498 ConditionalField( 

499 StrLenField("password", "", 

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

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

502 

503 def post_build(self, pkt, pay): 

504 pkt += pay 

505 if self.optlen is None: 

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

507 return pkt 

508 

509 

510# RFC 6975 - Signaling Cryptographic Algorithm Understanding in 

511# DNS Security Extensions (DNSSEC) 

512 

513class EDNS0DAU(_EDNS0Dummy): 

514 name = "DNSSEC Algorithm Understood (DAU)" 

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

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

517 FieldListField("alg_code", None, 

518 ByteEnumField("", 0, dnssecalgotypes), 

519 count_from=lambda pkt:pkt.optlen)] 

520 

521 

522class EDNS0DHU(_EDNS0Dummy): 

523 name = "DS Hash Understood (DHU)" 

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

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

526 FieldListField("alg_code", None, 

527 ByteEnumField("", 0, dnssecdigesttypes), 

528 count_from=lambda pkt:pkt.optlen)] 

529 

530 

531class EDNS0N3U(_EDNS0Dummy): 

532 name = "NSEC3 Hash Understood (N3U)" 

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

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

535 FieldListField("alg_code", None, 

536 ByteEnumField("", 0, dnssecnsec3algotypes), 

537 count_from=lambda pkt:pkt.optlen)] 

538 

539 

540# RFC 7871 - Client Subnet in DNS Queries 

541 

542class ClientSubnetv4(StrLenField): 

543 af_familly = socket.AF_INET 

544 af_length = 32 

545 af_default = b"\xc0" # 192.0.0.0 

546 

547 def getfield(self, pkt, s): 

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

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

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

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

552 

553 def m2i(self, pkt, x): 

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

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

556 if padding: 

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

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

559 return inet_ntop(self.af_familly, x) 

560 

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

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

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

564 if plen is not None: 

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

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

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

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

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

570 result = bytearray(packed_subnet[:num_bytes]) 

571 rem = plen % 8 

572 if rem and result: 

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

574 return bytes(result) 

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

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

577 if packed_subnet[i] != 0: 

578 i += 1 

579 break 

580 return packed_subnet[:i] 

581 

582 def i2m(self, pkt, x): 

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

584 if x is None: 

585 return self.af_default 

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

587 try: 

588 return self._pack_subnet(x, plen) 

589 except (OSError, socket.error): 

590 pkt.family = 2 

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

592 

593 def i2len(self, pkt, x): 

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

595 if x is None: 

596 return 1 

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

598 try: 

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

600 except (OSError, socket.error): 

601 pkt.family = 2 

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

603 

604 

605class ClientSubnetv6(ClientSubnetv4): 

606 af_familly = socket.AF_INET6 

607 af_length = 128 

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

609 

610 

611class EDNS0ClientSubnet(_EDNS0Dummy): 

612 name = "DNS EDNS0 Client Subnet" 

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

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

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

616 ShortField("family", 1), 

617 FieldLenField("source_plen", None, 

618 length_of="address", 

619 fmt="B", 

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

621 ByteField("scope_plen", 0), 

622 MultipleTypeField( 

623 [(ClientSubnetv4("address", "192.168.0.0", 

624 length_from=lambda p: p.source_plen), 

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

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

627 length_from=lambda p: p.source_plen), 

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

629 ClientSubnetv4("address", "192.168.0.0", 

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

631 

632 

633class EDNS0COOKIE(_EDNS0Dummy): 

634 name = "DNS EDNS0 COOKIE" 

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

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

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

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

639 XStrLenField("server_cookie", "", 

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

641 

642 

643# RFC 7830 - EDNS0 Padding Option 

644 

645class EDNS0PADDING(_EDNS0Dummy): 

646 name = "DNS EDNS0 Padding" 

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

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

649 StrLenField("padding", "", 

650 length_from=lambda pkt: pkt.optlen)] 

651 

652 

653# RFC 8914 - Extended DNS Errors 

654 

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

656extended_dns_error_codes = { 

657 0: "Other", 

658 1: "Unsupported DNSKEY Algorithm", 

659 2: "Unsupported DS Digest Type", 

660 3: "Stale Answer", 

661 4: "Forged Answer", 

662 5: "DNSSEC Indeterminate", 

663 6: "DNSSEC Bogus", 

664 7: "Signature Expired", 

665 8: "Signature Not Yet Valid", 

666 9: "DNSKEY Missing", 

667 10: "RRSIGs Missing", 

668 11: "No Zone Key Bit Set", 

669 12: "NSEC Missing", 

670 13: "Cached Error", 

671 14: "Not Ready", 

672 15: "Blocked", 

673 16: "Censored", 

674 17: "Filtered", 

675 18: "Prohibited", 

676 19: "Stale NXDOMAIN Answer", 

677 20: "Not Authoritative", 

678 21: "Not Supported", 

679 22: "No Reachable Authority", 

680 23: "Network Error", 

681 24: "Invalid Data", 

682 25: "Signature Expired before Valid", 

683 26: "Too Early", 

684 27: "Unsupported NSEC3 Iterations Value", 

685 28: "Unable to conform to policy", 

686 29: "Synthesized", 

687} 

688 

689 

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

691class EDNS0ExtendedDNSError(_EDNS0Dummy): 

692 name = "DNS EDNS0 Extended DNS Error" 

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

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

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

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

697 StrLenField("extra_text", "", 

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

699 

700 

701EDNS0OPT_DISPATCHER = { 

702 4: EDNS0OWN, 

703 5: EDNS0DAU, 

704 6: EDNS0DHU, 

705 7: EDNS0N3U, 

706 8: EDNS0ClientSubnet, 

707 10: EDNS0COOKIE, 

708 12: EDNS0PADDING, 

709 15: EDNS0ExtendedDNSError, 

710} 

711 

712 

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

714 

715def bitmap2RRlist(bitmap): 

716 """ 

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

718 integer list. 

719 """ 

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

721 

722 RRlist = [] 

723 

724 while bitmap: 

725 

726 if len(bitmap) < 2: 

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

728 return 

729 

730 window_block = bitmap[0] # window number 

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

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

733 

734 if bitmap_len <= 0 or bitmap_len > 32: 

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

736 return 

737 

738 tmp_bitmap = bitmap[2:2 + bitmap_len] 

739 

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

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

742 v = 128 

743 for i in range(8): 

744 if tmp_bitmap[b] & v: 

745 # each of the RR is encoded as a bit 

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

747 v = v >> 1 

748 

749 # Next block if any 

750 bitmap = bitmap[2 + bitmap_len:] 

751 

752 return RRlist 

753 

754 

755def RRlist2bitmap(lst): 

756 """ 

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

758 used in the NSEC Resource Record. 

759 """ 

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

761 

762 import math 

763 

764 bitmap = b"" 

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

766 

767 # number of window blocks 

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

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

770 if min_window_blocks == max_window_blocks: 

771 max_window_blocks += 1 

772 

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

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

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

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

777 if not rrlist: 

778 continue 

779 

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

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

782 bytes_count = 1 

783 else: 

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

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

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

787 bytes_count = 32 

788 

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

790 

791 # Generate the bitmap 

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

793 # 1. rescale to fit into 8 bits 

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

795 # 3. sum everything 

796 bitmap += b"".join( 

797 struct.pack( 

798 b"B", 

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

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

801 ) for tmp in range(bytes_count) 

802 ) 

803 

804 return bitmap 

805 

806 

807class RRlistField(StrField): 

808 islist = 1 

809 

810 def h2i(self, pkt, x): 

811 if x and isinstance(x, list): 

812 return RRlist2bitmap(x) 

813 return x 

814 

815 def i2repr(self, pkt, x): 

816 if not x: 

817 return "[]" 

818 x = self.i2h(pkt, x) 

819 rrlist = bitmap2RRlist(x) 

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

821 

822 

823class _DNSRRdummy(Packet): 

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

825 

826 def post_build(self, pkt, pay): 

827 if self.rdlen is not None: 

828 return pkt + pay 

829 

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

831 tmp_len = len(pkt) - lrrname - 10 

832 tmp_pkt = pkt[:lrrname + 8] 

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

834 

835 return tmp_pkt + pkt + pay 

836 

837 def default_payload_class(self, payload): 

838 return conf.padding_layer 

839 

840 

841class DNSRRHINFO(_DNSRRdummy): 

842 name = "DNS HINFO Resource Record" 

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

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

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

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

847 IntField("ttl", 0), 

848 ShortField("rdlen", None), 

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

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

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

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

853 

854 

855class DNSRRMX(_DNSRRdummy): 

856 name = "DNS MX Resource Record" 

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

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

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

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

861 IntField("ttl", 0), 

862 ShortField("rdlen", None), 

863 ShortField("preference", 0), 

864 DNSStrField("exchange", ""), 

865 ] 

866 

867 

868class DNSRRSOA(_DNSRRdummy): 

869 name = "DNS SOA Resource Record" 

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

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

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

873 IntField("ttl", 0), 

874 ShortField("rdlen", None), 

875 DNSStrField("mname", ""), 

876 DNSStrField("rname", ""), 

877 IntField("serial", 0), 

878 IntField("refresh", 0), 

879 IntField("retry", 0), 

880 IntField("expire", 0), 

881 IntField("minimum", 0) 

882 ] 

883 

884 

885class DNSRRRSIG(_DNSRRdummy): 

886 name = "DNS RRSIG Resource Record" 

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

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

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

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

891 IntField("ttl", 0), 

892 ShortField("rdlen", None), 

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

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

895 ByteField("labels", 0), 

896 IntField("originalttl", 0), 

897 UTCTimeField("expiration", 0), 

898 UTCTimeField("inception", 0), 

899 ShortField("keytag", 0), 

900 DNSStrField("signersname", ""), 

901 StrField("signature", "") 

902 ] 

903 

904 

905class DNSRRNSEC(_DNSRRdummy): 

906 name = "DNS NSEC Resource Record" 

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

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

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

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

911 IntField("ttl", 0), 

912 ShortField("rdlen", None), 

913 DNSStrField("nextname", ""), 

914 RRlistField("typebitmaps", []) 

915 ] 

916 

917 

918class DNSRRDNSKEY(_DNSRRdummy): 

919 name = "DNS DNSKEY Resource Record" 

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

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

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

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

924 IntField("ttl", 0), 

925 ShortField("rdlen", None), 

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

927 # S: Secure Entry Point 

928 # Z: Zone Key 

929 ByteField("protocol", 3), 

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

931 StrField("publickey", "") 

932 ] 

933 

934 

935class DNSRRDS(_DNSRRdummy): 

936 name = "DNS DS Resource Record" 

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

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

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

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

941 IntField("ttl", 0), 

942 ShortField("rdlen", None), 

943 ShortField("keytag", 0), 

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

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

946 StrField("digest", "") 

947 ] 

948 

949 

950# RFC 5074 - DNSSEC Lookaside Validation (DLV) 

951class DNSRRDLV(DNSRRDS): 

952 name = "DNS DLV Resource Record" 

953 

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

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

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

957 self.type = 32769 

958 

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

960 

961 

962class DNSRRNSEC3(_DNSRRdummy): 

963 name = "DNS NSEC3 Resource Record" 

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

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

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

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

968 IntField("ttl", 0), 

969 ShortField("rdlen", None), 

970 ByteField("hashalg", 0), 

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

972 ShortField("iterations", 0), 

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

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

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

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

977 RRlistField("typebitmaps", []) 

978 ] 

979 

980 

981class DNSRRNSEC3PARAM(_DNSRRdummy): 

982 name = "DNS NSEC3PARAM Resource Record" 

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

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

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

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

987 IntField("ttl", 0), 

988 ShortField("rdlen", None), 

989 ByteField("hashalg", 0), 

990 ByteField("flags", 0), 

991 ShortField("iterations", 0), 

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

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

994 ] 

995 

996 

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

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

999 

1000 

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

1002svc_param_keys = { 

1003 0: "mandatory", 

1004 1: "alpn", 

1005 2: "no-default-alpn", 

1006 3: "port", 

1007 4: "ipv4hint", 

1008 5: "ech", 

1009 6: "ipv6hint", 

1010 7: "dohpath", 

1011 8: "ohttp", 

1012} 

1013 

1014 

1015class SvcParam(Packet): 

1016 name = "SvcParam" 

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

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

1019 MultipleTypeField( 

1020 [ 

1021 # mandatory 

1022 (FieldListField("value", [], 

1023 ShortEnumField("", 0, svc_param_keys), 

1024 length_from=lambda pkt: pkt.len), 

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

1026 # alpn, no-default-alpn 

1027 (DNSTextField("value", [], 

1028 length_from=lambda pkt: pkt.len), 

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

1030 # port 

1031 (ShortField("value", 0), 

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

1033 # ipv4hint 

1034 (FieldListField("value", [], 

1035 IPField("", "0.0.0.0"), 

1036 length_from=lambda pkt: pkt.len), 

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

1038 # ipv6hint 

1039 (FieldListField("value", [], 

1040 IP6Field("", "::"), 

1041 length_from=lambda pkt: pkt.len), 

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

1043 ], 

1044 StrLenField("value", "", 

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

1046 

1047 def extract_padding(self, p): 

1048 return "", p 

1049 

1050 

1051class DNSRRSVCB(_DNSRRdummy): 

1052 name = "DNS SVCB Resource Record" 

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

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

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

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

1057 IntField("ttl", 0), 

1058 ShortField("rdlen", None), 

1059 ShortField("svc_priority", 0), 

1060 DNSStrField("target_name", ""), 

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

1062 

1063 

1064class DNSRRHTTPS(_DNSRRdummy): 

1065 name = "DNS HTTPS Resource Record" 

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

1067 ShortEnumField("type", 65, dnstypes) 

1068 ] + DNSRRSVCB.fields_desc[2:] 

1069 

1070 

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

1072 

1073 

1074class DNSRRSRV(_DNSRRdummy): 

1075 name = "DNS SRV Resource Record" 

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

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

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

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

1080 IntField("ttl", 0), 

1081 ShortField("rdlen", None), 

1082 ShortField("priority", 0), 

1083 ShortField("weight", 0), 

1084 ShortField("port", 0), 

1085 DNSStrField("target", ""), ] 

1086 

1087 

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

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

1090 "hmac-sha1": 20} 

1091 

1092 

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

1094 def __init__(self, name, default): 

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

1096 

1097 def _convert_seconds(self, packed_seconds): 

1098 """Unpack the internal representation.""" 

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

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

1101 return seconds 

1102 

1103 def i2m(self, pkt, seconds): 

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

1105 representation.""" 

1106 

1107 if seconds is None: 

1108 seconds = 0 

1109 

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

1111 tmp_int = seconds & 0xFFFFFFFF 

1112 

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

1114 

1115 def m2i(self, pkt, packed_seconds): 

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

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

1118 

1119 if packed_seconds is None: 

1120 return None 

1121 

1122 return self._convert_seconds(packed_seconds) 

1123 

1124 def i2repr(self, pkt, packed_seconds): 

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

1126 format.""" 

1127 time_struct = time.gmtime(packed_seconds) 

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

1129 

1130 

1131class DNSRRTSIG(_DNSRRdummy): 

1132 name = "DNS TSIG Resource Record" 

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

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

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

1136 IntField("ttl", 0), 

1137 ShortField("rdlen", None), 

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

1139 TimeSignedField("time_signed", 0), 

1140 ShortField("fudge", 0), 

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

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

1143 ShortField("original_id", 0), 

1144 ShortField("error", 0), 

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

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

1147 ] 

1148 

1149 

1150class DNSRRNAPTR(_DNSRRdummy): 

1151 name = "DNS NAPTR Resource Record" 

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

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

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

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

1156 IntField("ttl", 0), 

1157 ShortField("rdlen", None), 

1158 ShortField("order", 0), 

1159 ShortField("preference", 0), 

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

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

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

1163 StrLenField("services", "", 

1164 length_from=lambda pkt: pkt.services_len), 

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

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

1167 DNSStrField("replacement", ""), 

1168 ] 

1169 

1170 

1171DNSRR_DISPATCHER = { 

1172 6: DNSRRSOA, # RFC 1035 

1173 13: DNSRRHINFO, # RFC 1035 

1174 15: DNSRRMX, # RFC 1035 

1175 33: DNSRRSRV, # RFC 2782 

1176 35: DNSRRNAPTR, # RFC 2915 

1177 41: DNSRROPT, # RFC 1671 

1178 43: DNSRRDS, # RFC 4034 

1179 46: DNSRRRSIG, # RFC 4034 

1180 47: DNSRRNSEC, # RFC 4034 

1181 48: DNSRRDNSKEY, # RFC 4034 

1182 50: DNSRRNSEC3, # RFC 5155 

1183 51: DNSRRNSEC3PARAM, # RFC 5155 

1184 64: DNSRRSVCB, # RFC 9460 

1185 65: DNSRRHTTPS, # RFC 9460 

1186 250: DNSRRTSIG, # RFC 2845 

1187 32769: DNSRRDLV, # RFC 4431 

1188} 

1189 

1190 

1191class DNSRR(Packet): 

1192 name = "DNS Resource Record" 

1193 show_indent = 0 

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

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

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

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

1198 IntField("ttl", 0), 

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

1200 MultipleTypeField( 

1201 [ 

1202 # A 

1203 (IPField("rdata", "0.0.0.0"), 

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

1205 # AAAA 

1206 (IP6Field("rdata", "::"), 

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

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

1209 (DNSStrField("rdata", "", 

1210 length_from=lambda pkt: pkt.rdlen), 

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

1212 # TEXT 

1213 (DNSTextField("rdata", [""], 

1214 length_from=lambda pkt: pkt.rdlen), 

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

1216 ], 

1217 StrLenField("rdata", "", 

1218 length_from=lambda pkt:pkt.rdlen) 

1219 )] 

1220 

1221 def default_payload_class(self, payload): 

1222 return conf.padding_layer 

1223 

1224 

1225def _DNSRR(s, **kwargs): 

1226 """ 

1227 DNSRR dispatcher func 

1228 """ 

1229 if s: 

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

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

1232 cls = DNSRR_DISPATCHER.get( 

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

1234 DNSRR, 

1235 ) 

1236 rrlen = ( 

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

1238 10 + 

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

1240 ) 

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

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

1243 # when rebuilding 

1244 del pkt.fields["rdlen"] 

1245 return pkt 

1246 return None 

1247 

1248 

1249class DNSQR(Packet): 

1250 name = "DNS Question Record" 

1251 show_indent = 0 

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

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

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

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

1256 

1257 def default_payload_class(self, payload): 

1258 return conf.padding_layer 

1259 

1260 

1261class _DNSPacketListField(PacketListField): 

1262 # A normal PacketListField with backward-compatible hacks 

1263 def any2i(self, pkt, x): 

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

1265 if x is None: 

1266 warnings.warn( 

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

1268 "PacketListField(s) ! " 

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

1270 DeprecationWarning 

1271 ) 

1272 x = [] 

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

1274 

1275 def i2h(self, pkt, x): 

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

1277 class _list(list): 

1278 """ 

1279 Fake list object to provide compatibility with older DNS fields 

1280 """ 

1281 def __getattr__(self, attr): 

1282 try: 

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

1284 warnings.warn( 

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

1286 "PacketListField(s) ! " 

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

1288 "pkt.an"), 

1289 DeprecationWarning 

1290 ) 

1291 return ret 

1292 except AttributeError: 

1293 raise 

1294 return _list(x) 

1295 

1296 

1297class DNS(DNSCompressedPacket): 

1298 name = "DNS" 

1299 FORCE_TCP = False 

1300 fields_desc = [ 

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

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

1303 ShortField("id", 0), 

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

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

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

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

1308 BitField("rd", 1, 1), 

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

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

1311 # AD and CD bits are defined in RFC 2535 

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

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

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

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

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

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

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

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

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

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

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

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

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

1325 ] 

1326 

1327 def get_full(self): 

1328 # Required for DNSCompressedPacket 

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

1330 return self.original[2:] 

1331 else: 

1332 return self.original 

1333 

1334 def answers(self, other): 

1335 return (isinstance(other, DNS) and 

1336 self.id == other.id and 

1337 self.qr == 1 and 

1338 other.qr == 0) 

1339 

1340 def mysummary(self): 

1341 name = "" 

1342 if self.qr: 

1343 type = "Ans" 

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

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

1346 elif self.rcode != 0: 

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

1348 else: 

1349 type = "Qry" 

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

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

1352 return "%sDNS %s%s" % ( 

1353 "m" 

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

1355 else "", 

1356 type, 

1357 name, 

1358 ) 

1359 

1360 def post_build(self, pkt, pay): 

1361 if ( 

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

1363 self.length is None 

1364 ): 

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

1366 return pkt + pay 

1367 

1368 def compress(self): 

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

1370 return dns_compress(self) 

1371 

1372 def pre_dissect(self, s): 

1373 """ 

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

1375 """ 

1376 if isinstance(self.underlayer, TCP): 

1377 

1378 # Compute the length of the DNS packet 

1379 if len(s) >= 2: 

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

1381 else: 

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

1383 log_runtime.info(message) 

1384 raise Scapy_Exception(message) 

1385 

1386 # Check if the length is valid 

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

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

1389 log_runtime.info(message) 

1390 raise Scapy_Exception(message) 

1391 

1392 return s 

1393 

1394 

1395class DNSTCP(DNS): 

1396 """ 

1397 A DNS packet that is always under TCP 

1398 """ 

1399 FORCE_TCP = True 

1400 match_subclass = True 

1401 

1402 

1403bind_layers(UDP, DNS, dport=5353) 

1404bind_layers(UDP, DNS, sport=5353) 

1405bind_layers(UDP, DNS, dport=53) 

1406bind_layers(UDP, DNS, sport=53) 

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

1408if conf.ipv6_enabled: 

1409 from scapy.layers.inet6 import DestIP6Field 

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

1411bind_layers(TCP, DNS, dport=53) 

1412bind_layers(TCP, DNS, sport=53) 

1413 

1414# Nameserver config 

1415conf.nameservers = read_nameservers() 

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

1417 

1418 

1419@conf.commands.register 

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

1421 """ 

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

1423 

1424 :param qname: the name to query 

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

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

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

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

1429 :param verbose: show verbose errors 

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

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

1432 """ 

1433 # Unify types (for caching) 

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

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

1436 # Check cache 

1437 cache_ident = b";".join( 

1438 [qname, struct.pack("!B", qtype)] + 

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

1440 ) 

1441 result = _dns_cache.get(cache_ident) 

1442 if result: 

1443 return result 

1444 

1445 kwargs.setdefault("timeout", timeout) 

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

1447 res = None 

1448 for nameserver in conf.nameservers: 

1449 # Try all nameservers 

1450 try: 

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

1452 if tcp: 

1453 cls = DNSTCP 

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

1455 else: 

1456 cls = DNS 

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

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

1459 sock.connect((nameserver, 53)) 

1460 # Connected. Wrap it with DNS 

1461 sock = StreamSocket(sock, cls) 

1462 # I/O 

1463 res = sock.sr1( 

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

1465 **kwargs, 

1466 ) 

1467 except IOError as ex: 

1468 if verbose: 

1469 log_runtime.warning(str(ex)) 

1470 continue 

1471 finally: 

1472 sock.close() 

1473 if res: 

1474 # We have a response ! Check for failure 

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

1476 if not tcp: 

1477 # Retry using TCP 

1478 return dns_resolve( 

1479 qname=qname, 

1480 qtype=qtype, 

1481 raw=raw, 

1482 tcp=True, 

1483 **kwargs, 

1484 ) 

1485 elif verbose: 

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

1487 

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

1489 res = None 

1490 if verbose: 

1491 log_runtime.info( 

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

1493 nameserver, 

1494 qname, 

1495 ) 

1496 ) 

1497 else: 

1498 break 

1499 if res is not None: 

1500 if raw: 

1501 # Raw 

1502 result = res 

1503 else: 

1504 # Find answers 

1505 result = [ 

1506 x 

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

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

1509 ] 

1510 if result: 

1511 # Cache it 

1512 _dns_cache[cache_ident] = result 

1513 return result 

1514 else: 

1515 raise TimeoutError 

1516 

1517 

1518@conf.commands.register 

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

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

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

1522 

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

1524RFC2136 

1525""" 

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

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

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

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

1530 ttl=ttl, rdata=rdata)]), 

1531 verbose=0, timeout=5) 

1532 if r and r.haslayer(DNS): 

1533 return r.getlayer(DNS).rcode 

1534 else: 

1535 return -1 

1536 

1537 

1538@conf.commands.register 

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

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

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

1542 

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

1544RFC2136 

1545""" 

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

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

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

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

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

1551 verbose=0, timeout=5) 

1552 if r and r.haslayer(DNS): 

1553 return r.getlayer(DNS).rcode 

1554 else: 

1555 return -1 

1556 

1557 

1558class DNS_am(AnsweringMachine): 

1559 function_name = "dnsd" 

1560 filter = "udp port 53" 

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

1562 

1563 def parse_options(self, joker=None, 

1564 match=None, 

1565 srvmatch=None, 

1566 joker6=False, 

1567 send_error=False, 

1568 relay=False, 

1569 from_ip=True, 

1570 from_ip6=False, 

1571 src_ip=None, 

1572 src_ip6=None, 

1573 ttl=10, 

1574 jokerarpa=False): 

1575 """ 

1576 Simple DNS answering machine. 

1577 

1578 :param joker: default IPv4 for unresolved domains. 

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

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

1581 False. 

1582 :param joker6: default IPv6 for unresolved domains. 

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

1584 Defaults to False. 

1585 :param match: queries to match. 

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

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

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

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

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

1591 used as a response. 

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

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

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

1595 (Default: False) 

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

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

1598 False for none. Default True 

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

1600 False for none. Default False 

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

1602 :param src_ip: override the source IP 

1603 :param src_ip6: 

1604 

1605 Examples: 

1606 

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

1608 

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

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

1611 ... iface="eth0") 

1612 

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

1614 

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

1616 

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

1618 

1619 >>> dnsd( 

1620 ... srvmatch={ 

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

1622 ... "srv1.domain.local"), 

1623 ... }, 

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

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

1626 ... ) 

1627 

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

1629 

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

1631 >>> dnsd( 

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

1633 ... relay=True, 

1634 ... ) 

1635 """ 

1636 from scapy.layers.inet6 import Net6 

1637 

1638 self.mDNS = isinstance(self, mDNS_am) 

1639 self.llmnr = self.cls != DNS 

1640 

1641 # Add some checks (to help) 

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

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

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

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

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

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

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

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

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

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

1652 if self.mDNS and src_ip: 

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

1654 if self.mDNS and src_ip6: 

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

1656 

1657 if joker is None and match is not None: 

1658 joker = False 

1659 self.joker = joker 

1660 self.joker6 = joker6 

1661 self.jokerarpa = jokerarpa 

1662 

1663 def normv(v): 

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

1665 return tuple(v) 

1666 elif isinstance(v, str): 

1667 return (v, joker6) 

1668 else: 

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

1670 

1671 def normk(k): 

1672 k = bytes_encode(k).lower() 

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

1674 k += b"." 

1675 return k 

1676 

1677 self.match = collections.defaultdict(lambda: (joker, joker6)) 

1678 if match: 

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

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

1681 else: 

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

1683 if srvmatch is None: 

1684 self.srvmatch = {} 

1685 else: 

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

1687 

1688 self.send_error = send_error 

1689 self.relay = relay 

1690 if isinstance(from_ip, str): 

1691 self.from_ip = Net(from_ip) 

1692 else: 

1693 self.from_ip = from_ip 

1694 if isinstance(from_ip6, str): 

1695 self.from_ip6 = Net6(from_ip6) 

1696 else: 

1697 self.from_ip6 = from_ip6 

1698 self.src_ip = src_ip 

1699 self.src_ip6 = src_ip6 

1700 self.ttl = ttl 

1701 

1702 def is_request(self, req): 

1703 from scapy.layers.inet6 import IPv6 

1704 return ( 

1705 req.haslayer(self.cls) and 

1706 req.getlayer(self.cls).qr == 0 and ( 

1707 ( 

1708 self.from_ip6 is True or 

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

1710 ) 

1711 if IPv6 in req else 

1712 ( 

1713 self.from_ip is True or 

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

1715 ) 

1716 ) 

1717 ) 

1718 

1719 def make_reply(self, req): 

1720 # Build reply from the request 

1721 resp = req.copy() 

1722 if Ether in req: 

1723 if self.mDNS: 

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

1725 elif self.llmnr: 

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

1727 else: 

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

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

1730 req[Ether].src, 

1731 ) 

1732 from scapy.layers.inet6 import IPv6 

1733 if IPv6 in req: 

1734 resp[IPv6].underlayer.remove_payload() 

1735 if self.mDNS: 

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

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

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

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

1740 elif self.llmnr: 

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

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

1743 else: 

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

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

1746 elif IP in req: 

1747 resp[IP].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 /= IP(dst="224.0.0.251", src=self.src_ip, 

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

1753 elif self.llmnr: 

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

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

1756 else: 

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

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

1759 else: 

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

1761 return 

1762 try: 

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

1764 except IndexError: 

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

1766 return 

1767 try: 

1768 req = req[self.cls] 

1769 except IndexError: 

1770 warning( 

1771 "No %s layer in %s", 

1772 self.cls.__name__, 

1773 req.command(), 

1774 exc_info=True, 

1775 ) 

1776 return 

1777 try: 

1778 queries = req.qd 

1779 except AttributeError: 

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

1781 return 

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

1783 try: 

1784 allquery = next( 

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

1786 ) 

1787 queries.remove(allquery) 

1788 queries.extend([ 

1789 DNSQR( 

1790 qtype=x, 

1791 qname=allquery.qname, 

1792 unicastresponse=allquery.unicastresponse, 

1793 qclass=allquery.qclass, 

1794 ) 

1795 for x in [1, 28] 

1796 ]) 

1797 except StopIteration: 

1798 pass 

1799 # Process each query 

1800 ans = [] 

1801 ars = [] 

1802 for rq in queries: 

1803 if isinstance(rq, Raw): 

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

1805 continue 

1806 rqname = rq.qname.lower() 

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

1808 # A or AAAA 

1809 if rq.qtype == 28: 

1810 # AAAA 

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

1812 if rdata is None and not self.relay: 

1813 # 'None' resolves to the default IPv6 

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

1815 if self.mDNS: 

1816 # All IPs, as per mDNS. 

1817 rdata = iface.ips[6] 

1818 else: 

1819 rdata = get_if_addr6( 

1820 iface 

1821 ) 

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

1823 # For mDNS, we must replace the IPv6 src 

1824 resp[IPv6].src = rdata 

1825 elif rq.qtype == 1: 

1826 # A 

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

1828 if rdata is None and not self.relay: 

1829 # 'None' resolves to the default IPv4 

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

1831 if self.mDNS: 

1832 # All IPs, as per mDNS. 

1833 rdata = iface.ips[4] 

1834 else: 

1835 rdata = get_if_addr( 

1836 iface 

1837 ) 

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

1839 # For mDNS, we must replace the IP src 

1840 resp[IP].src = rdata 

1841 if rdata: 

1842 # Common A and AAAA 

1843 if not isinstance(rdata, list): 

1844 rdata = [rdata] 

1845 ans.extend([ 

1846 DNSRR( 

1847 rrname=rq.qname, 

1848 ttl=self.ttl, 

1849 rdata=x, 

1850 type=rq.qtype, 

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

1852 ) 

1853 for x in rdata 

1854 ]) 

1855 continue # next 

1856 elif rq.qtype == 33: 

1857 # SRV 

1858 try: 

1859 port, target = self.srvmatch[rqname] 

1860 ans.append(DNSRRSRV( 

1861 rrname=rq.qname, 

1862 port=port, 

1863 target=target, 

1864 weight=100, 

1865 ttl=self.ttl 

1866 )) 

1867 continue # next 

1868 except KeyError: 

1869 # No result 

1870 pass 

1871 elif rq.qtype == 12: 

1872 # PTR 

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

1874 ans.append(DNSRR( 

1875 rrname=rq.qname, 

1876 type=rq.qtype, 

1877 ttl=self.ttl, 

1878 rdata=self.jokerarpa, 

1879 )) 

1880 continue 

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

1882 if self.relay: 

1883 # Relay mode ? 

1884 try: 

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

1886 if _rslv: 

1887 ans.extend(_rslv.an) 

1888 ars.extend(_rslv.ar) 

1889 continue # next 

1890 except TimeoutError: 

1891 pass 

1892 # Still no answer. 

1893 if self.mDNS: 

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

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

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

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

1898 ans.append(DNSRRNSEC( 

1899 # RFC6762 sect 6.1 - Negative Response 

1900 ttl=self.ttl, 

1901 rrname=rq.qname, 

1902 nextname=rq.qname, 

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

1904 )) 

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

1906 # If mDNS answers with only NSEC, discard. 

1907 return 

1908 if not ans: 

1909 # No answer is available. 

1910 if self.send_error: 

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

1912 return resp 

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

1914 return 

1915 # Handle Additional Records 

1916 if self.mDNS: 

1917 # Windows specific extension 

1918 ars.append(DNSRROPT( 

1919 z=0x1194, 

1920 rdata=[ 

1921 EDNS0OWN( 

1922 primary_mac=resp[Ether].src, 

1923 ), 

1924 ], 

1925 )) 

1926 # All rq were answered 

1927 if self.mDNS: 

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

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

1930 else: 

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

1932 # Compress DNS and mDNS 

1933 if not self.llmnr: 

1934 resp /= dns_compress(dns) 

1935 else: 

1936 resp /= dns 

1937 return resp 

1938 

1939 

1940class mDNS_am(DNS_am): 

1941 """ 

1942 mDNS answering machine. 

1943 

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

1945 

1946 Example:: 

1947 

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

1949 

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

1951 

1952 - Answer all requests with other IP:: 

1953 

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

1955 ... iface="eth0") 

1956 

1957 - Answer for multiple different mDNS names:: 

1958 

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

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

1961 

1962 - Answer with both A and AAAA records:: 

1963 

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

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

1966 """ 

1967 function_name = "mdnsd" 

1968 filter = "udp port 5353" 

1969 

1970 

1971# DNS-SD (RFC 6763) 

1972 

1973 

1974class DNSSDResult(SndRcvList): 

1975 def __init__(self, 

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

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

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

1979 ): 

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

1981 

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

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

1984 """ 

1985 Print the list of discovered services. 

1986 

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

1988 :param alltypes: show all types. Default False 

1989 """ 

1990 if alltypes: 

1991 types = None 

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

1993 

1994 resolve_mac = ( 

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

1996 conf.manufdb 

1997 ) 

1998 

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

2000 if resolve_mac: 

2001 header = ("Mac",) + header 

2002 

2003 for _, r in self.res: 

2004 attrs = [] 

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

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

2007 continue 

2008 if isinstance(attr, DNSRRNSEC): 

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

2010 elif isinstance(attr, DNSRRSRV): 

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

2012 else: 

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

2014 ans = (r.src, attrs) 

2015 if resolve_mac: 

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

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

2018 else: 

2019 data.append(ans) 

2020 

2021 print( 

2022 pretty_list( 

2023 data, 

2024 [header], 

2025 ) 

2026 ) 

2027 

2028 

2029@conf.commands.register 

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

2031 af=socket.AF_INET, 

2032 qtype="PTR", 

2033 iface=None, 

2034 verbose=2, 

2035 timeout=3): 

2036 """ 

2037 Performs a DNS-SD (RFC6763) request 

2038 

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

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

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

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

2043 

2044 Examples:: 

2045 

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

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

2048 """ 

2049 if af == socket.AF_INET: 

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

2051 elif af == socket.AF_INET6: 

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

2053 else: 

2054 return 

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

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

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

2058 return DNSSDResult(ans.res)