1# -*- test-case-name: twisted.names.test.test_dns -*- 2# Copyright (c) Twisted Matrix Laboratories. 3# See LICENSE for details. 4 5""" 6DNS protocol implementation. 7 8Future Plans: 9 - Get rid of some toplevels, maybe. 10""" 11 12# System imports 13import inspect 14import random 15import socket 16import struct 17from io import BytesIO 18from itertools import chain 19from typing import Optional, SupportsInt, Union 20 21from zope.interface import Attribute, Interface, implementer 22 23# Twisted imports 24from twisted.internet import defer, protocol 25from twisted.internet.error import CannotListenError 26from twisted.python import failure, log, randbytes, util as tputil 27from twisted.python.compat import cmp, comparable, nativeString 28 29__all__ = [ 30 "IEncodable", 31 "IRecord", 32 "IEncodableRecord", 33 "A", 34 "A6", 35 "AAAA", 36 "AFSDB", 37 "CNAME", 38 "DNAME", 39 "HINFO", 40 "MAILA", 41 "MAILB", 42 "MB", 43 "MD", 44 "MF", 45 "MG", 46 "MINFO", 47 "MR", 48 "MX", 49 "NAPTR", 50 "NS", 51 "NULL", 52 "OPT", 53 "PTR", 54 "RP", 55 "SOA", 56 "SPF", 57 "SRV", 58 "TXT", 59 "SSHFP", 60 "TSIG", 61 "WKS", 62 "ANY", 63 "CH", 64 "CS", 65 "HS", 66 "IN", 67 "ALL_RECORDS", 68 "AXFR", 69 "IXFR", 70 "EFORMAT", 71 "ENAME", 72 "ENOTIMP", 73 "EREFUSED", 74 "ESERVER", 75 "EBADVERSION", 76 "EBADSIG", 77 "EBADKEY", 78 "EBADTIME", 79 "Record_A", 80 "Record_A6", 81 "Record_AAAA", 82 "Record_AFSDB", 83 "Record_CNAME", 84 "Record_DNAME", 85 "Record_HINFO", 86 "Record_MB", 87 "Record_MD", 88 "Record_MF", 89 "Record_MG", 90 "Record_MINFO", 91 "Record_MR", 92 "Record_MX", 93 "Record_NAPTR", 94 "Record_NS", 95 "Record_NULL", 96 "Record_PTR", 97 "Record_RP", 98 "Record_SOA", 99 "Record_SPF", 100 "Record_SRV", 101 "Record_SSHFP", 102 "Record_TSIG", 103 "Record_TXT", 104 "Record_WKS", 105 "UnknownRecord", 106 "QUERY_CLASSES", 107 "QUERY_TYPES", 108 "REV_CLASSES", 109 "REV_TYPES", 110 "EXT_QUERIES", 111 "Charstr", 112 "Message", 113 "Name", 114 "Query", 115 "RRHeader", 116 "SimpleRecord", 117 "DNSDatagramProtocol", 118 "DNSMixin", 119 "DNSProtocol", 120 "OK", 121 "OP_INVERSE", 122 "OP_NOTIFY", 123 "OP_QUERY", 124 "OP_STATUS", 125 "OP_UPDATE", 126 "PORT", 127 "AuthoritativeDomainError", 128 "DNSQueryTimeoutError", 129 "DomainError", 130] 131 132 133AF_INET6 = socket.AF_INET6 134 135 136def _ord2bytes(ordinal): 137 """ 138 Construct a bytes object representing a single byte with the given 139 ordinal value. 140 141 @type ordinal: L{int} 142 @rtype: L{bytes} 143 """ 144 return bytes([ordinal]) 145 146 147def _nicebytes(bytes): 148 """ 149 Represent a mostly textful bytes object in a way suitable for 150 presentation to an end user. 151 152 @param bytes: The bytes to represent. 153 @rtype: L{str} 154 """ 155 return repr(bytes)[1:] 156 157 158def _nicebyteslist(list): 159 """ 160 Represent a list of mostly textful bytes objects in a way suitable for 161 presentation to an end user. 162 163 @param list: The list of bytes to represent. 164 @rtype: L{str} 165 """ 166 return "[{}]".format(", ".join([_nicebytes(b) for b in list])) 167 168 169def randomSource(): 170 """ 171 Wrapper around L{twisted.python.randbytes.RandomFactory.secureRandom} to 172 return 2 random bytes. 173 174 @rtype: L{bytes} 175 """ 176 return struct.unpack("H", randbytes.secureRandom(2, fallback=True))[0] 177 178 179PORT = 53 180 181( 182 A, 183 NS, 184 MD, 185 MF, 186 CNAME, 187 SOA, 188 MB, 189 MG, 190 MR, 191 NULL, 192 WKS, 193 PTR, 194 HINFO, 195 MINFO, 196 MX, 197 TXT, 198 RP, 199 AFSDB, 200) = range(1, 19) 201AAAA = 28 202SRV = 33 203NAPTR = 35 204A6 = 38 205DNAME = 39 206OPT = 41 207SSHFP = 44 208SPF = 99 209 210# These record types do not exist in zones, but are transferred in 211# messages the same way normal RRs are. 212TKEY = 249 213TSIG = 250 214 215QUERY_TYPES = { 216 A: "A", 217 NS: "NS", 218 MD: "MD", 219 MF: "MF", 220 CNAME: "CNAME", 221 SOA: "SOA", 222 MB: "MB", 223 MG: "MG", 224 MR: "MR", 225 NULL: "NULL", 226 WKS: "WKS", 227 PTR: "PTR", 228 HINFO: "HINFO", 229 MINFO: "MINFO", 230 MX: "MX", 231 TXT: "TXT", 232 RP: "RP", 233 AFSDB: "AFSDB", 234 # 19 through 27? Eh, I'll get to 'em. 235 AAAA: "AAAA", 236 SRV: "SRV", 237 NAPTR: "NAPTR", 238 A6: "A6", 239 DNAME: "DNAME", 240 OPT: "OPT", 241 SSHFP: "SSHFP", 242 SPF: "SPF", 243 TKEY: "TKEY", 244 TSIG: "TSIG", 245} 246 247IXFR, AXFR, MAILB, MAILA, ALL_RECORDS = range(251, 256) 248 249# "Extended" queries (Hey, half of these are deprecated, good job) 250EXT_QUERIES = { 251 IXFR: "IXFR", 252 AXFR: "AXFR", 253 MAILB: "MAILB", 254 MAILA: "MAILA", 255 ALL_RECORDS: "ALL_RECORDS", 256} 257 258REV_TYPES = {v: k for (k, v) in chain(QUERY_TYPES.items(), EXT_QUERIES.items())} 259 260IN, CS, CH, HS = range(1, 5) 261ANY = 255 262 263QUERY_CLASSES = {IN: "IN", CS: "CS", CH: "CH", HS: "HS", ANY: "ANY"} 264REV_CLASSES = {v: k for (k, v) in QUERY_CLASSES.items()} 265 266 267# Opcodes 268OP_QUERY, OP_INVERSE, OP_STATUS = range(3) 269OP_NOTIFY = 4 # RFC 1996 270OP_UPDATE = 5 # RFC 2136 271 272 273# Response Codes 274OK, EFORMAT, ESERVER, ENAME, ENOTIMP, EREFUSED = range(6) 275# https://tools.ietf.org/html/rfc6891#section-9 276EBADVERSION = 16 277# RFC 2845 278EBADSIG, EBADKEY, EBADTIME = range(16, 19) 279 280 281class IRecord(Interface): 282 """ 283 A single entry in a zone of authority. 284 """ 285 286 TYPE = Attribute("An indicator of what kind of record this is.") 287 288 289# Backwards compatibility aliases - these should be deprecated or something I 290# suppose. -exarkun 291from twisted.names.error import ( 292 AuthoritativeDomainError, 293 DNSQueryTimeoutError, 294 DomainError, 295) 296 297 298def _nameToLabels(name): 299 """ 300 Split a domain name into its constituent labels. 301 302 @type name: L{bytes} 303 @param name: A fully qualified domain name (with or without a 304 trailing dot). 305 306 @return: A L{list} of labels ending with an empty label 307 representing the DNS root zone. 308 @rtype: L{list} of L{bytes} 309 """ 310 if name in (b"", b"."): 311 return [b""] 312 labels = name.split(b".") 313 if labels[-1] != b"": 314 labels.append(b"") 315 return labels 316 317 318def domainString(domain): 319 """ 320 Coerce a domain name string to bytes. 321 322 L{twisted.names} represents domain names as L{bytes}, but many interfaces 323 accept L{bytes} or a text string (L{unicode} on Python 2, L{str} on Python 324 3). This function coerces text strings using IDNA encoding --- see 325 L{encodings.idna}. 326 327 Note that DNS is I{case insensitive} but I{case preserving}. This function 328 doesn't normalize case, so you'll still need to do that whenever comparing 329 the strings it returns. 330 331 @param domain: A domain name. If passed as a text string it will be 332 C{idna} encoded. 333 @type domain: L{bytes} or L{str} 334 335 @returns: L{bytes} suitable for network transmission. 336 @rtype: L{bytes} 337 338 @since: Twisted 20.3.0 339 """ 340 if isinstance(domain, str): 341 domain = domain.encode("idna") 342 if not isinstance(domain, bytes): 343 raise TypeError( 344 "Expected {} or {} but found {!r} of type {}".format( 345 bytes.__name__, str.__name__, domain, type(domain) 346 ) 347 ) 348 return domain 349 350 351def _isSubdomainOf(descendantName, ancestorName): 352 """ 353 Test whether C{descendantName} is equal to or is a I{subdomain} of 354 C{ancestorName}. 355 356 The names are compared case-insensitively. 357 358 The names are treated as byte strings containing one or more 359 DNS labels separated by B{.}. 360 361 C{descendantName} is considered equal if its sequence of labels 362 exactly matches the labels of C{ancestorName}. 363 364 C{descendantName} is considered a I{subdomain} if its sequence of 365 labels ends with the labels of C{ancestorName}. 366 367 @type descendantName: L{bytes} 368 @param descendantName: The DNS subdomain name. 369 370 @type ancestorName: L{bytes} 371 @param ancestorName: The DNS parent or ancestor domain name. 372 373 @return: C{True} if C{descendantName} is equal to or if it is a 374 subdomain of C{ancestorName}. Otherwise returns C{False}. 375 """ 376 descendantLabels = _nameToLabels(descendantName.lower()) 377 ancestorLabels = _nameToLabels(ancestorName.lower()) 378 return descendantLabels[-len(ancestorLabels) :] == ancestorLabels 379 380 381def str2time(s): 382 """ 383 Parse a string description of an interval into an integer number of seconds. 384 385 @param s: An interval definition constructed as an interval duration 386 followed by an interval unit. An interval duration is a base ten 387 representation of an integer. An interval unit is one of the following 388 letters: S (seconds), M (minutes), H (hours), D (days), W (weeks), or Y 389 (years). For example: C{"3S"} indicates an interval of three seconds; 390 C{"5D"} indicates an interval of five days. Alternatively, C{s} may be 391 any non-string and it will be returned unmodified. 392 @type s: text string (L{bytes} or L{str}) for parsing; anything else 393 for passthrough. 394 395 @return: an L{int} giving the interval represented by the string C{s}, or 396 whatever C{s} is if it is not a string. 397 """ 398 suffixes = ( 399 ("S", 1), 400 ("M", 60), 401 ("H", 60 * 60), 402 ("D", 60 * 60 * 24), 403 ("W", 60 * 60 * 24 * 7), 404 ("Y", 60 * 60 * 24 * 365), 405 ) 406 if isinstance(s, bytes): 407 s = s.decode("ascii") 408 409 if isinstance(s, str): 410 s = s.upper().strip() 411 for (suff, mult) in suffixes: 412 if s.endswith(suff): 413 return int(float(s[:-1]) * mult) 414 try: 415 s = int(s) 416 except ValueError: 417 raise ValueError("Invalid time interval specifier: " + s) 418 return s 419 420 421def readPrecisely(file, l): 422 buff = file.read(l) 423 if len(buff) < l: 424 raise EOFError 425 return buff 426 427 428class IEncodable(Interface): 429 """ 430 Interface for something which can be encoded to and decoded 431 to the DNS wire format. 432 433 A binary-mode file object (such as L{io.BytesIO}) is used as a buffer when 434 encoding or decoding. 435 """ 436 437 def encode(strio, compDict=None): 438 """ 439 Write a representation of this object to the given 440 file object. 441 442 @type strio: File-like object 443 @param strio: The buffer to write to. It must have a C{tell()} method. 444 445 @type compDict: L{dict} of L{bytes} to L{int} r L{None} 446 @param compDict: A mapping of names to byte offsets that have already 447 been written to the buffer, which may be used for compression (see RFC 448 1035 section 4.1.4). When L{None}, encode without compression. 449 """ 450 451 def decode(strio, length=None): 452 """ 453 Reconstruct an object from data read from the given 454 file object. 455 456 @type strio: File-like object 457 @param strio: A seekable buffer from which bytes may be read. 458 459 @type length: L{int} or L{None} 460 @param length: The number of bytes in this RDATA field. Most 461 implementations can ignore this value. Only in the case of 462 records similar to TXT where the total length is in no way 463 encoded in the data is it necessary. 464 """ 465 466 467class IEncodableRecord(IEncodable, IRecord): 468 """ 469 Interface for DNS records that can be encoded and decoded. 470 471 @since: Twisted 21.2.0 472 """ 473 474 475@implementer(IEncodable) 476class Charstr: 477 def __init__(self, string=b""): 478 if not isinstance(string, bytes): 479 raise ValueError(f"{string!r} is not a byte string") 480 self.string = string 481 482 def encode(self, strio, compDict=None): 483 """ 484 Encode this Character string into the appropriate byte format. 485 486 @type strio: file 487 @param strio: The byte representation of this Charstr will be written 488 to this file. 489 """ 490 string = self.string 491 ind = len(string) 492 strio.write(_ord2bytes(ind)) 493 strio.write(string) 494 495 def decode(self, strio, length=None): 496 """ 497 Decode a byte string into this Charstr. 498 499 @type strio: file 500 @param strio: Bytes will be read from this file until the full string 501 is decoded. 502 503 @raise EOFError: Raised when there are not enough bytes available from 504 C{strio}. 505 """ 506 self.string = b"" 507 l = ord(readPrecisely(strio, 1)) 508 self.string = readPrecisely(strio, l) 509 510 def __eq__(self, other: object) -> bool: 511 if isinstance(other, Charstr): 512 return self.string == other.string 513 return NotImplemented 514 515 def __hash__(self): 516 return hash(self.string) 517 518 def __str__(self) -> str: 519 """ 520 Represent this L{Charstr} instance by its string value. 521 """ 522 return nativeString(self.string) 523 524 525@implementer(IEncodable) 526class Name: 527 """ 528 A name in the domain name system, made up of multiple labels. For example, 529 I{twistedmatrix.com}. 530 531 @ivar name: A byte string giving the name. 532 @type name: L{bytes} 533 """ 534 535 def __init__(self, name=b""): 536 """ 537 @param name: A name. 538 @type name: L{bytes} or L{str} 539 """ 540 self.name = domainString(name) 541 542 def encode(self, strio, compDict=None): 543 """ 544 Encode this Name into the appropriate byte format. 545 546 @type strio: file 547 @param strio: The byte representation of this Name will be written to 548 this file. 549 550 @type compDict: dict 551 @param compDict: dictionary of Names that have already been encoded 552 and whose addresses may be backreferenced by this Name (for the purpose 553 of reducing the message size). 554 """ 555 name = self.name 556 while name: 557 if compDict is not None: 558 if name in compDict: 559 strio.write(struct.pack("!H", 0xC000 | compDict[name])) 560 return 561 else: 562 compDict[name] = strio.tell() + Message.headerSize 563 ind = name.find(b".") 564 if ind > 0: 565 label, name = name[:ind], name[ind + 1 :] 566 else: 567 # This is the last label, end the loop after handling it. 568 label = name 569 name = None 570 ind = len(label) 571 strio.write(_ord2bytes(ind)) 572 strio.write(label) 573 strio.write(b"\x00") 574 575 def decode(self, strio, length=None): 576 """ 577 Decode a byte string into this Name. 578 579 @type strio: file 580 @param strio: Bytes will be read from this file until the full Name 581 is decoded. 582 583 @raise EOFError: Raised when there are not enough bytes available 584 from C{strio}. 585 586 @raise ValueError: Raised when the name cannot be decoded (for example, 587 because it contains a loop). 588 """ 589 visited = set() 590 self.name = b"" 591 off = 0 592 while 1: 593 l = ord(readPrecisely(strio, 1)) 594 if l == 0: 595 if off > 0: 596 strio.seek(off) 597 return 598 if (l >> 6) == 3: 599 new_off = (l & 63) << 8 | ord(readPrecisely(strio, 1)) 600 if new_off in visited: 601 raise ValueError("Compression loop in encoded name") 602 visited.add(new_off) 603 if off == 0: 604 off = strio.tell() 605 strio.seek(new_off) 606 continue 607 label = readPrecisely(strio, l) 608 if self.name == b"": 609 self.name = label 610 else: 611 self.name = self.name + b"." + label 612 613 def __eq__(self, other: object) -> bool: 614 if isinstance(other, Name): 615 return self.name.lower() == other.name.lower() 616 return NotImplemented 617 618 def __hash__(self): 619 return hash(self.name) 620 621 def __str__(self) -> str: 622 """ 623 Represent this L{Name} instance by its string name. 624 """ 625 return nativeString(self.name) 626 627 628@comparable 629@implementer(IEncodable) 630class Query: 631 """ 632 Represent a single DNS query. 633 634 @ivar name: The name about which this query is requesting information. 635 @type name: L{Name} 636 637 @ivar type: The query type. 638 @type type: L{int} 639 640 @ivar cls: The query class. 641 @type cls: L{int} 642 """ 643 644 def __init__(self, name: Union[bytes, str] = b"", type: int = A, cls: int = IN): 645 """ 646 @type name: L{bytes} or L{str} 647 @param name: See L{Query.name} 648 649 @type type: L{int} 650 @param type: The query type. 651 652 @type cls: L{int} 653 @param cls: The query class. 654 """ 655 self.name = Name(name) 656 self.type = type 657 self.cls = cls 658 659 def encode(self, strio, compDict=None): 660 self.name.encode(strio, compDict) 661 strio.write(struct.pack("!HH", self.type, self.cls)) 662 663 def decode(self, strio, length=None): 664 self.name.decode(strio) 665 buff = readPrecisely(strio, 4) 666 self.type, self.cls = struct.unpack("!HH", buff) 667 668 def __hash__(self): 669 return hash((self.name.name.lower(), self.type, self.cls)) 670 671 def __cmp__(self, other): 672 if isinstance(other, Query): 673 return cmp( 674 (self.name.name.lower(), self.type, self.cls), 675 (other.name.name.lower(), other.type, other.cls), 676 ) 677 return NotImplemented 678 679 def __str__(self) -> str: 680 t = QUERY_TYPES.get( 681 self.type, EXT_QUERIES.get(self.type, "UNKNOWN (%d)" % self.type) 682 ) 683 c = QUERY_CLASSES.get(self.cls, "UNKNOWN (%d)" % self.cls) 684 return f"<Query {self.name} {t} {c}>" 685 686 def __repr__(self) -> str: 687 return f"Query({self.name.name!r}, {self.type!r}, {self.cls!r})" 688 689 690@implementer(IEncodable) 691class _OPTHeader(tputil.FancyStrMixin, tputil.FancyEqMixin): 692 """ 693 An OPT record header. 694 695 @ivar name: The DNS name associated with this record. Since this 696 is a pseudo record, the name is always an L{Name} instance 697 with value b'', which represents the DNS root zone. This 698 attribute is a readonly property. 699 700 @ivar type: The DNS record type. This is a fixed value of 41 701 C{dns.OPT} for OPT Record. This attribute is a readonly 702 property. 703 704 @see: L{_OPTHeader.__init__} for documentation of other public 705 instance attributes. 706 707 @see: U{https://tools.ietf.org/html/rfc6891#section-6.1.2} 708 709 @since: 13.2 710 """ 711 712 showAttributes = ( 713 ("name", lambda n: nativeString(n.name)), 714 "type", 715 "udpPayloadSize", 716 "extendedRCODE", 717 "version", 718 "dnssecOK", 719 "options", 720 ) 721 722 compareAttributes = ( 723 "name", 724 "type", 725 "udpPayloadSize", 726 "extendedRCODE", 727 "version", 728 "dnssecOK", 729 "options", 730 ) 731 732 def __init__( 733 self, 734 udpPayloadSize=4096, 735 extendedRCODE=0, 736 version=0, 737 dnssecOK=False, 738 options=None, 739 ): 740 """ 741 @type udpPayloadSize: L{int} 742 @param udpPayloadSize: The number of octets of the largest UDP 743 payload that can be reassembled and delivered in the 744 requestor's network stack. 745 746 @type extendedRCODE: L{int} 747 @param extendedRCODE: Forms the upper 8 bits of extended 748 12-bit RCODE (together with the 4 bits defined in 749 [RFC1035]. Note that EXTENDED-RCODE value 0 indicates 750 that an unextended RCODE is in use (values 0 through 15). 751 752 @type version: L{int} 753 @param version: Indicates the implementation level of the 754 setter. Full conformance with this specification is 755 indicated by version C{0}. 756 757 @type dnssecOK: L{bool} 758 @param dnssecOK: DNSSEC OK bit as defined by [RFC3225]. 759 760 @type options: L{list} 761 @param options: A L{list} of 0 or more L{_OPTVariableOption} 762 instances. 763 """ 764 self.udpPayloadSize = udpPayloadSize 765 self.extendedRCODE = extendedRCODE 766 self.version = version 767 self.dnssecOK = dnssecOK 768 769 if options is None: 770 options = [] 771 self.options = options 772 773 @property 774 def name(self): 775 """ 776 A readonly property for accessing the C{name} attribute of 777 this record. 778 779 @return: The DNS name associated with this record. Since this 780 is a pseudo record, the name is always an L{Name} instance 781 with value b'', which represents the DNS root zone. 782 """ 783 return Name(b"") 784 785 @property 786 def type(self): 787 """ 788 A readonly property for accessing the C{type} attribute of 789 this record. 790 791 @return: The DNS record type. This is a fixed value of 41 792 (C{dns.OPT} for OPT Record. 793 """ 794 return OPT 795 796 def encode(self, strio, compDict=None): 797 """ 798 Encode this L{_OPTHeader} instance to bytes. 799 800 @type strio: file 801 @param strio: the byte representation of this L{_OPTHeader} 802 will be written to this file. 803 804 @type compDict: L{dict} or L{None} 805 @param compDict: A dictionary of backreference addresses that 806 have already been written to this stream and that may 807 be used for DNS name compression. 808 """ 809 b = BytesIO() 810 for o in self.options: 811 o.encode(b) 812 optionBytes = b.getvalue() 813 814 RRHeader( 815 name=self.name.name, 816 type=self.type, 817 cls=self.udpPayloadSize, 818 ttl=(self.extendedRCODE << 24 | self.version << 16 | self.dnssecOK << 15), 819 payload=UnknownRecord(optionBytes), 820 ).encode(strio, compDict) 821 822 def decode(self, strio, length=None): 823 """ 824 Decode bytes into an L{_OPTHeader} instance. 825 826 @type strio: file 827 @param strio: Bytes will be read from this file until the full 828 L{_OPTHeader} is decoded. 829 830 @type length: L{int} or L{None} 831 @param length: Not used. 832 """ 833 834 h = RRHeader() 835 h.decode(strio, length) 836 h.payload = UnknownRecord(readPrecisely(strio, h.rdlength)) 837 838 newOptHeader = self.fromRRHeader(h) 839 840 for attrName in self.compareAttributes: 841 if attrName not in ("name", "type"): 842 setattr(self, attrName, getattr(newOptHeader, attrName)) 843 844 @classmethod 845 def fromRRHeader(cls, rrHeader): 846 """ 847 A classmethod for constructing a new L{_OPTHeader} from the 848 attributes and payload of an existing L{RRHeader} instance. 849 850 @type rrHeader: L{RRHeader} 851 @param rrHeader: An L{RRHeader} instance containing an 852 L{UnknownRecord} payload. 853 854 @return: An instance of L{_OPTHeader}. 855 @rtype: L{_OPTHeader} 856 """ 857 options = None 858 if rrHeader.payload is not None: 859 options = [] 860 optionsBytes = BytesIO(rrHeader.payload.data) 861 optionsBytesLength = len(rrHeader.payload.data) 862 while optionsBytes.tell() < optionsBytesLength: 863 o = _OPTVariableOption() 864 o.decode(optionsBytes) 865 options.append(o) 866 867 # Decode variable options if present 868 return cls( 869 udpPayloadSize=rrHeader.cls, 870 extendedRCODE=rrHeader.ttl >> 24, 871 version=rrHeader.ttl >> 16 & 0xFF, 872 dnssecOK=(rrHeader.ttl & 0xFFFF) >> 15, 873 options=options, 874 ) 875 876 877@implementer(IEncodable) 878class _OPTVariableOption(tputil.FancyStrMixin, tputil.FancyEqMixin): 879 """ 880 A class to represent OPT record variable options. 881 882 @see: L{_OPTVariableOption.__init__} for documentation of public 883 instance attributes. 884 885 @see: U{https://tools.ietf.org/html/rfc6891#section-6.1.2} 886 887 @since: 13.2 888 """ 889 890 showAttributes = ("code", ("data", nativeString)) 891 compareAttributes = ("code", "data") 892 893 _fmt = "!HH" 894 895 def __init__(self, code=0, data=b""): 896 """ 897 @type code: L{int} 898 @param code: The option code 899 900 @type data: L{bytes} 901 @param data: The option data 902 """ 903 self.code = code 904 self.data = data 905 906 def encode(self, strio, compDict=None): 907 """ 908 Encode this L{_OPTVariableOption} to bytes. 909 910 @type strio: file 911 @param strio: the byte representation of this 912 L{_OPTVariableOption} will be written to this file. 913 914 @type compDict: L{dict} or L{None} 915 @param compDict: A dictionary of backreference addresses that 916 have already been written to this stream and that may 917 be used for DNS name compression. 918 """ 919 strio.write(struct.pack(self._fmt, self.code, len(self.data)) + self.data) 920 921 def decode(self, strio, length=None): 922 """ 923 Decode bytes into an L{_OPTVariableOption} instance. 924 925 @type strio: file 926 @param strio: Bytes will be read from this file until the full 927 L{_OPTVariableOption} is decoded. 928 929 @type length: L{int} or L{None} 930 @param length: Not used. 931 """ 932 l = struct.calcsize(self._fmt) 933 buff = readPrecisely(strio, l) 934 self.code, length = struct.unpack(self._fmt, buff) 935 self.data = readPrecisely(strio, length) 936 937 938@implementer(IEncodable) 939class RRHeader(tputil.FancyEqMixin): 940 """ 941 A resource record header. 942 943 @cvar fmt: L{str} specifying the byte format of an RR. 944 945 @ivar name: The name about which this reply contains information. 946 @type name: L{Name} 947 948 @ivar type: The query type of the original request. 949 @type type: L{int} 950 951 @ivar cls: The query class of the original request. 952 953 @ivar ttl: The time-to-live for this record. 954 @type ttl: L{int} 955 956 @ivar payload: The record described by this header. 957 @type payload: L{IEncodableRecord} or L{None} 958 959 @ivar auth: A L{bool} indicating whether this C{RRHeader} was parsed from 960 an authoritative message. 961 """ 962 963 compareAttributes = ("name", "type", "cls", "ttl", "payload", "auth") 964 965 fmt = "!HHIH" 966 967 rdlength = None 968 969 cachedResponse = None 970 971 def __init__( 972 self, 973 name: Union[bytes, str] = b"", 974 type: int = A, 975 cls: int = IN, 976 ttl: SupportsInt = 0, 977 payload: Optional[IEncodableRecord] = None, 978 auth: bool = False, 979 ): 980 """ 981 @type name: L{bytes} or L{str} 982 @param name: See L{RRHeader.name} 983 984 @type type: L{int} 985 @param type: The query type. 986 987 @type cls: L{int} 988 @param cls: The query class. 989 990 @type ttl: L{int} 991 @param ttl: Time to live for this record. This will be 992 converted to an L{int}. 993 994 @type payload: L{IEncodableRecord} or L{None} 995 @param payload: An optional Query Type specific data object. 996 997 @raises TypeError: if the ttl cannot be converted to an L{int}. 998 @raises ValueError: if the ttl is negative. 999 @raises ValueError: if the payload type is not equal to the C{type} 1000 argument. 1001 """ 1002 payloadType = None if payload is None else payload.TYPE 1003 if payloadType is not None and payloadType != type: 1004 raise ValueError( 1005 "Payload type (%s) does not match given type (%s)" 1006 % ( 1007 QUERY_TYPES.get(payloadType, payloadType), 1008 QUERY_TYPES.get(type, type), 1009 ) 1010 ) 1011 1012 integralTTL = int(ttl) 1013 1014 if integralTTL < 0: 1015 raise ValueError("TTL cannot be negative") 1016 1017 self.name = Name(name) 1018 self.type = type 1019 self.cls = cls 1020 self.ttl = integralTTL 1021 self.payload = payload 1022 self.auth = auth 1023 1024 def encode(self, strio, compDict=None): 1025 self.name.encode(strio, compDict) 1026 strio.write(struct.pack(self.fmt, self.type, self.cls, self.ttl, 0)) 1027 if self.payload: 1028 prefix = strio.tell() 1029 self.payload.encode(strio, compDict) 1030 aft = strio.tell() 1031 strio.seek(prefix - 2, 0) 1032 strio.write(struct.pack("!H", aft - prefix)) 1033 strio.seek(aft, 0) 1034 1035 def decode(self, strio, length=None): 1036 self.name.decode(strio) 1037 l = struct.calcsize(self.fmt) 1038 buff = readPrecisely(strio, l) 1039 r = struct.unpack(self.fmt, buff) 1040 self.type, self.cls, self.ttl, self.rdlength = r 1041 1042 def isAuthoritative(self): 1043 return self.auth 1044 1045 def __str__(self) -> str: 1046 t = QUERY_TYPES.get( 1047 self.type, EXT_QUERIES.get(self.type, "UNKNOWN (%d)" % self.type) 1048 ) 1049 c = QUERY_CLASSES.get(self.cls, "UNKNOWN (%d)" % self.cls) 1050 return "<RR name=%s type=%s class=%s ttl=%ds auth=%s>" % ( 1051 self.name, 1052 t, 1053 c, 1054 self.ttl, 1055 self.auth and "True" or "False", 1056 ) 1057 1058 __repr__ = __str__ 1059 1060 1061@implementer(IEncodableRecord) 1062class SimpleRecord(tputil.FancyStrMixin, tputil.FancyEqMixin): 1063 """ 1064 A Resource Record which consists of a single RFC 1035 domain-name. 1065 1066 @type name: L{Name} 1067 @ivar name: The name associated with this record. 1068 1069 @type ttl: L{int} 1070 @ivar ttl: The maximum number of seconds which this record should be 1071 cached. 1072 """ 1073 1074 showAttributes = (("name", "name", "%s"), "ttl") 1075 compareAttributes = ("name", "ttl") 1076 1077 TYPE: Optional[int] = None 1078 name = None 1079 1080 def __init__(self, name=b"", ttl=None): 1081 """ 1082 @param name: See L{SimpleRecord.name} 1083 @type name: L{bytes} or L{str} 1084 """ 1085 self.name = Name(name) 1086 self.ttl = str2time(ttl) 1087 1088 def encode(self, strio, compDict=None): 1089 self.name.encode(strio, compDict) 1090 1091 def decode(self, strio, length=None): 1092 self.name = Name() 1093 self.name.decode(strio) 1094 1095 def __hash__(self): 1096 return hash(self.name) 1097 1098 1099# Kinds of RRs - oh my! 1100class Record_NS(SimpleRecord): 1101 """ 1102 An authoritative nameserver. 1103 """ 1104 1105 TYPE = NS 1106 fancybasename = "NS" 1107 1108 1109class Record_MD(SimpleRecord): 1110 """ 1111 A mail destination. 1112 1113 This record type is obsolete. 1114 1115 @see: L{Record_MX} 1116 """ 1117 1118 TYPE = MD 1119 fancybasename = "MD" 1120 1121 1122class Record_MF(SimpleRecord): 1123 """ 1124 A mail forwarder. 1125 1126 This record type is obsolete. 1127 1128 @see: L{Record_MX} 1129 """ 1130 1131 TYPE = MF 1132 fancybasename = "MF" 1133 1134 1135class Record_CNAME(SimpleRecord): 1136 """ 1137 The canonical name for an alias. 1138 """ 1139 1140 TYPE = CNAME 1141 fancybasename = "CNAME" 1142 1143 1144class Record_MB(SimpleRecord): 1145 """ 1146 A mailbox domain name. 1147 1148 This is an experimental record type. 1149 """ 1150 1151 TYPE = MB 1152 fancybasename = "MB" 1153 1154 1155class Record_MG(SimpleRecord): 1156 """ 1157 A mail group member. 1158 1159 This is an experimental record type. 1160 """ 1161 1162 TYPE = MG 1163 fancybasename = "MG" 1164 1165 1166class Record_MR(SimpleRecord): 1167 """ 1168 A mail rename domain name. 1169 1170 This is an experimental record type. 1171 """ 1172 1173 TYPE = MR 1174 fancybasename = "MR" 1175 1176 1177class Record_PTR(SimpleRecord): 1178 """ 1179 A domain name pointer. 1180 """ 1181 1182 TYPE = PTR 1183 fancybasename = "PTR" 1184 1185 1186class Record_DNAME(SimpleRecord): 1187 """ 1188 A non-terminal DNS name redirection. 1189 1190 This record type provides the capability to map an entire subtree of the 1191 DNS name space to another domain. It differs from the CNAME record which 1192 maps a single node of the name space. 1193 1194 @see: U{http://www.faqs.org/rfcs/rfc2672.html} 1195 @see: U{http://www.faqs.org/rfcs/rfc3363.html} 1196 """ 1197 1198 TYPE = DNAME 1199 fancybasename = "DNAME" 1200 1201 1202@implementer(IEncodableRecord) 1203class Record_A(tputil.FancyEqMixin): 1204 """ 1205 An IPv4 host address. 1206 1207 @type address: L{bytes} 1208 @ivar address: The packed network-order representation of the IPv4 address 1209 associated with this record. 1210 1211 @type ttl: L{int} 1212 @ivar ttl: The maximum number of seconds which this record should be 1213 cached. 1214 """ 1215 1216 compareAttributes = ("address", "ttl") 1217 1218 TYPE = A 1219 address = None 1220 1221 def __init__(self, address="0.0.0.0", ttl=None): 1222 """ 1223 @type address: L{bytes} or L{str} 1224 @param address: The IPv4 address associated with this record, in 1225 quad-dotted notation. 1226 """ 1227 if isinstance(address, bytes): 1228 address = address.decode("ascii") 1229 1230 address = socket.inet_aton(address) 1231 self.address = address 1232 self.ttl = str2time(ttl) 1233 1234 def encode(self, strio, compDict=None): 1235 strio.write(self.address) 1236 1237 def decode(self, strio, length=None): 1238 self.address = readPrecisely(strio, 4) 1239 1240 def __hash__(self): 1241 return hash(self.address) 1242 1243 def __str__(self) -> str: 1244 return f"<A address={self.dottedQuad()} ttl={self.ttl}>" 1245 1246 __repr__ = __str__ 1247 1248 def dottedQuad(self): 1249 return socket.inet_ntoa(self.address) 1250 1251 1252@implementer(IEncodableRecord) 1253class Record_SOA(tputil.FancyEqMixin, tputil.FancyStrMixin): 1254 """ 1255 Marks the start of a zone of authority. 1256 1257 This record describes parameters which are shared by all records within a 1258 particular zone. 1259 1260 @type mname: L{Name} 1261 @ivar mname: The domain-name of the name server that was the original or 1262 primary source of data for this zone. 1263 1264 @type rname: L{Name} 1265 @ivar rname: A domain-name which specifies the mailbox of the person 1266 responsible for this zone. 1267 1268 @type serial: L{int} 1269 @ivar serial: The unsigned 32 bit version number of the original copy of 1270 the zone. Zone transfers preserve this value. This value wraps and 1271 should be compared using sequence space arithmetic. 1272 1273 @type refresh: L{int} 1274 @ivar refresh: A 32 bit time interval before the zone should be refreshed. 1275 1276 @type minimum: L{int} 1277 @ivar minimum: The unsigned 32 bit minimum TTL field that should be 1278 exported with any RR from this zone. 1279 1280 @type expire: L{int} 1281 @ivar expire: A 32 bit time value that specifies the upper limit on the 1282 time interval that can elapse before the zone is no longer 1283 authoritative. 1284 1285 @type retry: L{int} 1286 @ivar retry: A 32 bit time interval that should elapse before a failed 1287 refresh should be retried. 1288 1289 @type ttl: L{int} 1290 @ivar ttl: The default TTL to use for records served from this zone. 1291 """ 1292 1293 fancybasename = "SOA" 1294 compareAttributes = ( 1295 "serial", 1296 "mname", 1297 "rname", 1298 "refresh", 1299 "expire", 1300 "retry", 1301 "minimum", 1302 "ttl", 1303 ) 1304 showAttributes = ( 1305 ("mname", "mname", "%s"), 1306 ("rname", "rname", "%s"), 1307 "serial", 1308 "refresh", 1309 "retry", 1310 "expire", 1311 "minimum", 1312 "ttl", 1313 ) 1314 1315 TYPE = SOA 1316 1317 def __init__( 1318 self, 1319 mname=b"", 1320 rname=b"", 1321 serial=0, 1322 refresh=0, 1323 retry=0, 1324 expire=0, 1325 minimum=0, 1326 ttl=None, 1327 ): 1328 """ 1329 @param mname: See L{Record_SOA.mname} 1330 @type mname: L{bytes} or L{str} 1331 1332 @param rname: See L{Record_SOA.rname} 1333 @type rname: L{bytes} or L{str} 1334 """ 1335 self.mname, self.rname = Name(mname), Name(rname) 1336 self.serial, self.refresh = str2time(serial), str2time(refresh) 1337 self.minimum, self.expire = str2time(minimum), str2time(expire) 1338 self.retry = str2time(retry) 1339 self.ttl = str2time(ttl) 1340 1341 def encode(self, strio, compDict=None): 1342 self.mname.encode(strio, compDict) 1343 self.rname.encode(strio, compDict) 1344 strio.write( 1345 struct.pack( 1346 "!LlllL", 1347 self.serial, 1348 self.refresh, 1349 self.retry, 1350 self.expire, 1351 self.minimum, 1352 ) 1353 ) 1354 1355 def decode(self, strio, length=None): 1356 self.mname, self.rname = Name(), Name() 1357 self.mname.decode(strio) 1358 self.rname.decode(strio) 1359 r = struct.unpack("!LlllL", readPrecisely(strio, 20)) 1360 self.serial, self.refresh, self.retry, self.expire, self.minimum = r 1361 1362 def __hash__(self): 1363 return hash( 1364 (self.serial, self.mname, self.rname, self.refresh, self.expire, self.retry) 1365 ) 1366 1367 1368@implementer(IEncodableRecord) 1369class Record_NULL(tputil.FancyStrMixin, tputil.FancyEqMixin): 1370 """ 1371 A null record. 1372 1373 This is an experimental record type. 1374 1375 @type ttl: L{int} 1376 @ivar ttl: The maximum number of seconds which this record should be 1377 cached. 1378 """ 1379 1380 fancybasename = "NULL" 1381 showAttributes = (("payload", _nicebytes), "ttl") 1382 compareAttributes = ("payload", "ttl") 1383 1384 TYPE = NULL 1385 1386 def __init__(self, payload=None, ttl=None): 1387 self.payload = payload 1388 self.ttl = str2time(ttl) 1389 1390 def encode(self, strio, compDict=None): 1391 strio.write(self.payload) 1392 1393 def decode(self, strio, length=None): 1394 self.payload = readPrecisely(strio, length) 1395 1396 def __hash__(self): 1397 return hash(self.payload) 1398 1399 1400@implementer(IEncodableRecord) 1401class Record_WKS(tputil.FancyEqMixin, tputil.FancyStrMixin): 1402 """ 1403 A well known service description. 1404 1405 This record type is obsolete. See L{Record_SRV}. 1406 1407 @type address: L{bytes} 1408 @ivar address: The packed network-order representation of the IPv4 address 1409 associated with this record. 1410 1411 @type protocol: L{int} 1412 @ivar protocol: The 8 bit IP protocol number for which this service map is 1413 relevant. 1414 1415 @type map: L{bytes} 1416 @ivar map: A bitvector indicating the services available at the specified 1417 address. 1418 1419 @type ttl: L{int} 1420 @ivar ttl: The maximum number of seconds which this record should be 1421 cached. 1422 """ 1423 1424 fancybasename = "WKS" 1425 compareAttributes = ("address", "protocol", "map", "ttl") 1426 showAttributes = [("_address", "address", "%s"), "protocol", "ttl"] 1427 1428 TYPE = WKS 1429 1430 @property 1431 def _address(self): 1432 return socket.inet_ntoa(self.address) 1433 1434 def __init__(self, address="0.0.0.0", protocol=0, map=b"", ttl=None): 1435 """ 1436 @type address: L{bytes} or L{str} 1437 @param address: The IPv4 address associated with this record, in 1438 quad-dotted notation. 1439 """ 1440 if isinstance(address, bytes): 1441 address = address.decode("idna") 1442 1443 self.address = socket.inet_aton(address) 1444 self.protocol, self.map = protocol, map 1445 self.ttl = str2time(ttl) 1446 1447 def encode(self, strio, compDict=None): 1448 strio.write(self.address) 1449 strio.write(struct.pack("!B", self.protocol)) 1450 strio.write(self.map) 1451 1452 def decode(self, strio, length=None): 1453 self.address = readPrecisely(strio, 4) 1454 self.protocol = struct.unpack("!B", readPrecisely(strio, 1))[0] 1455 self.map = readPrecisely(strio, length - 5) 1456 1457 def __hash__(self): 1458 return hash((self.address, self.protocol, self.map)) 1459 1460 1461@implementer(IEncodableRecord) 1462class Record_AAAA(tputil.FancyEqMixin, tputil.FancyStrMixin): 1463 """ 1464 An IPv6 host address. 1465 1466 @type address: L{bytes} 1467 @ivar address: The packed network-order representation of the IPv6 address 1468 associated with this record. 1469 1470 @type ttl: L{int} 1471 @ivar ttl: The maximum number of seconds which this record should be 1472 cached. 1473 1474 @see: U{http://www.faqs.org/rfcs/rfc1886.html} 1475 """ 1476 1477 TYPE = AAAA 1478 1479 fancybasename = "AAAA" 1480 showAttributes = (("_address", "address", "%s"), "ttl") 1481 compareAttributes = ("address", "ttl") 1482 1483 @property 1484 def _address(self): 1485 return socket.inet_ntop(AF_INET6, self.address) 1486 1487 def __init__(self, address="::", ttl=None): 1488 """ 1489 @type address: L{bytes} or L{str} 1490 @param address: The IPv6 address for this host, in RFC 2373 format. 1491 """ 1492 if isinstance(address, bytes): 1493 address = address.decode("idna") 1494 1495 self.address = socket.inet_pton(AF_INET6, address) 1496 self.ttl = str2time(ttl) 1497 1498 def encode(self, strio, compDict=None): 1499 strio.write(self.address) 1500 1501 def decode(self, strio, length=None): 1502 self.address = readPrecisely(strio, 16) 1503 1504 def __hash__(self): 1505 return hash(self.address) 1506 1507 1508@implementer(IEncodableRecord) 1509class Record_A6(tputil.FancyStrMixin, tputil.FancyEqMixin): 1510 """ 1511 An IPv6 address. 1512 1513 This is an experimental record type. 1514 1515 @type prefixLen: L{int} 1516 @ivar prefixLen: The length of the suffix. 1517 1518 @type suffix: L{bytes} 1519 @ivar suffix: An IPv6 address suffix in network order. 1520 1521 @type prefix: L{Name} 1522 @ivar prefix: If specified, a name which will be used as a prefix for other 1523 A6 records. 1524 1525 @type bytes: L{int} 1526 @ivar bytes: The length of the prefix. 1527 1528 @type ttl: L{int} 1529 @ivar ttl: The maximum number of seconds which this record should be 1530 cached. 1531 1532 @see: U{http://www.faqs.org/rfcs/rfc2874.html} 1533 @see: U{http://www.faqs.org/rfcs/rfc3363.html} 1534 @see: U{http://www.faqs.org/rfcs/rfc3364.html} 1535 """ 1536 1537 TYPE = A6 1538 1539 fancybasename = "A6" 1540 showAttributes = (("_suffix", "suffix", "%s"), ("prefix", "prefix", "%s"), "ttl") 1541 compareAttributes = ("prefixLen", "prefix", "suffix", "ttl") 1542 1543 @property 1544 def _suffix(self): 1545 return socket.inet_ntop(AF_INET6, self.suffix) 1546 1547 def __init__(self, prefixLen=0, suffix="::", prefix=b"", ttl=None): 1548 """ 1549 @param suffix: An IPv6 address suffix in in RFC 2373 format. 1550 @type suffix: L{bytes} or L{str} 1551 1552 @param prefix: An IPv6 address prefix for other A6 records. 1553 @type prefix: L{bytes} or L{str} 1554 """ 1555 if isinstance(suffix, bytes): 1556 suffix = suffix.decode("idna") 1557 1558 self.prefixLen = prefixLen 1559 self.suffix = socket.inet_pton(AF_INET6, suffix) 1560 self.prefix = Name(prefix) 1561 self.bytes = int((128 - self.prefixLen) / 8.0) 1562 self.ttl = str2time(ttl) 1563 1564 def encode(self, strio, compDict=None): 1565 strio.write(struct.pack("!B", self.prefixLen)) 1566 if self.bytes: 1567 strio.write(self.suffix[-self.bytes :]) 1568 if self.prefixLen: 1569 # This may not be compressed 1570 self.prefix.encode(strio, None) 1571 1572 def decode(self, strio, length=None): 1573 self.prefixLen = struct.unpack("!B", readPrecisely(strio, 1))[0] 1574 self.bytes = int((128 - self.prefixLen) / 8.0) 1575 if self.bytes: 1576 self.suffix = b"\x00" * (16 - self.bytes) + readPrecisely(strio, self.bytes) 1577 if self.prefixLen: 1578 self.prefix.decode(strio) 1579 1580 def __eq__(self, other: object) -> bool: 1581 if isinstance(other, Record_A6): 1582 return ( 1583 self.prefixLen == other.prefixLen 1584 and self.suffix[-self.bytes :] == other.suffix[-self.bytes :] 1585 and self.prefix == other.prefix 1586 and self.ttl == other.ttl 1587 ) 1588 return NotImplemented 1589 1590 def __hash__(self): 1591 return hash((self.prefixLen, self.suffix[-self.bytes :], self.prefix)) 1592 1593 def __str__(self) -> str: 1594 return "<A6 %s %s (%d) ttl=%s>" % ( 1595 self.prefix, 1596 socket.inet_ntop(AF_INET6, self.suffix), 1597 self.prefixLen, 1598 self.ttl, 1599 ) 1600 1601 1602@implementer(IEncodableRecord) 1603class Record_SRV(tputil.FancyEqMixin, tputil.FancyStrMixin): 1604 """ 1605 The location of the server(s) for a specific protocol and domain. 1606 1607 This is an experimental record type. 1608 1609 @type priority: L{int} 1610 @ivar priority: The priority of this target host. A client MUST attempt to 1611 contact the target host with the lowest-numbered priority it can reach; 1612 target hosts with the same priority SHOULD be tried in an order defined 1613 by the weight field. 1614 1615 @type weight: L{int} 1616 @ivar weight: Specifies a relative weight for entries with the same 1617 priority. Larger weights SHOULD be given a proportionately higher 1618 probability of being selected. 1619 1620 @type port: L{int} 1621 @ivar port: The port on this target host of this service. 1622 1623 @type target: L{Name} 1624 @ivar target: The domain name of the target host. There MUST be one or 1625 more address records for this name, the name MUST NOT be an alias (in 1626 the sense of RFC 1034 or RFC 2181). Implementors are urged, but not 1627 required, to return the address record(s) in the Additional Data 1628 section. Unless and until permitted by future standards action, name 1629 compression is not to be used for this field. 1630 1631 @type ttl: L{int} 1632 @ivar ttl: The maximum number of seconds which this record should be 1633 cached. 1634 1635 @see: U{http://www.faqs.org/rfcs/rfc2782.html} 1636 """ 1637 1638 TYPE = SRV 1639 1640 fancybasename = "SRV" 1641 compareAttributes = ("priority", "weight", "target", "port", "ttl") 1642 showAttributes = ("priority", "weight", ("target", "target", "%s"), "port", "ttl") 1643 1644 def __init__(self, priority=0, weight=0, port=0, target=b"", ttl=None): 1645 """ 1646 @param target: See L{Record_SRV.target} 1647 @type target: L{bytes} or L{str} 1648 """ 1649 self.priority = int(priority) 1650 self.weight = int(weight) 1651 self.port = int(port) 1652 self.target = Name(target) 1653 self.ttl = str2time(ttl) 1654 1655 def encode(self, strio, compDict=None): 1656 strio.write(struct.pack("!HHH", self.priority, self.weight, self.port)) 1657 # This can't be compressed 1658 self.target.encode(strio, None) 1659 1660 def decode(self, strio, length=None): 1661 r = struct.unpack("!HHH", readPrecisely(strio, struct.calcsize("!HHH"))) 1662 self.priority, self.weight, self.port = r 1663 self.target = Name() 1664 self.target.decode(strio) 1665 1666 def __hash__(self): 1667 return hash((self.priority, self.weight, self.port, self.target)) 1668 1669 1670@implementer(IEncodableRecord) 1671class Record_NAPTR(tputil.FancyEqMixin, tputil.FancyStrMixin): 1672 """ 1673 The location of the server(s) for a specific protocol and domain. 1674 1675 @type order: L{int} 1676 @ivar order: An integer specifying the order in which the NAPTR records 1677 MUST be processed to ensure the correct ordering of rules. Low numbers 1678 are processed before high numbers. 1679 1680 @type preference: L{int} 1681 @ivar preference: An integer that specifies the order in which NAPTR 1682 records with equal "order" values SHOULD be processed, low numbers 1683 being processed before high numbers. 1684 1685 @type flag: L{Charstr} 1686 @ivar flag: A <character-string> containing flags to control aspects of the 1687 rewriting and interpretation of the fields in the record. Flags 1688 are single characters from the set [A-Z0-9]. The case of the alphabetic 1689 characters is not significant. 1690 1691 At this time only four flags, "S", "A", "U", and "P", are defined. 1692 1693 @type service: L{Charstr} 1694 @ivar service: Specifies the service(s) available down this rewrite path. 1695 It may also specify the particular protocol that is used to talk with a 1696 service. A protocol MUST be specified if the flags field states that 1697 the NAPTR is terminal. 1698 1699 @type regexp: L{Charstr} 1700 @ivar regexp: A STRING containing a substitution expression that is applied 1701 to the original string held by the client in order to construct the 1702 next domain name to lookup. 1703 1704 @type replacement: L{Name} 1705 @ivar replacement: The next NAME to query for NAPTR, SRV, or address 1706 records depending on the value of the flags field. This MUST be a 1707 fully qualified domain-name. 1708 1709 @type ttl: L{int} 1710 @ivar ttl: The maximum number of seconds which this record should be 1711 cached. 1712 1713 @see: U{http://www.faqs.org/rfcs/rfc2915.html} 1714 """ 1715 1716 TYPE = NAPTR 1717 1718 compareAttributes = ( 1719 "order", 1720 "preference", 1721 "flags", 1722 "service", 1723 "regexp", 1724 "replacement", 1725 ) 1726 fancybasename = "NAPTR" 1727 1728 showAttributes = ( 1729 "order", 1730 "preference", 1731 ("flags", "flags", "%s"), 1732 ("service", "service", "%s"), 1733 ("regexp", "regexp", "%s"), 1734 ("replacement", "replacement", "%s"), 1735 "ttl", 1736 ) 1737 1738 def __init__( 1739 self, 1740 order=0, 1741 preference=0, 1742 flags=b"", 1743 service=b"", 1744 regexp=b"", 1745 replacement=b"", 1746 ttl=None, 1747 ): 1748 """ 1749 @param replacement: See L{Record_NAPTR.replacement} 1750 @type replacement: L{bytes} or L{str} 1751 """ 1752 self.order = int(order) 1753 self.preference = int(preference) 1754 self.flags = Charstr(flags) 1755 self.service = Charstr(service) 1756 self.regexp = Charstr(regexp) 1757 self.replacement = Name(replacement) 1758 self.ttl = str2time(ttl) 1759 1760 def encode(self, strio, compDict=None): 1761 strio.write(struct.pack("!HH", self.order, self.preference)) 1762 # This can't be compressed 1763 self.flags.encode(strio, None) 1764 self.service.encode(strio, None) 1765 self.regexp.encode(strio, None) 1766 self.replacement.encode(strio, None) 1767 1768 def decode(self, strio, length=None): 1769 r = struct.unpack("!HH", readPrecisely(strio, struct.calcsize("!HH"))) 1770 self.order, self.preference = r 1771 self.flags = Charstr() 1772 self.service = Charstr() 1773 self.regexp = Charstr() 1774 self.replacement = Name() 1775 self.flags.decode(strio) 1776 self.service.decode(strio) 1777 self.regexp.decode(strio) 1778 self.replacement.decode(strio) 1779 1780 def __hash__(self): 1781 return hash( 1782 ( 1783 self.order, 1784 self.preference, 1785 self.flags, 1786 self.service, 1787 self.regexp, 1788 self.replacement, 1789 ) 1790 ) 1791 1792 1793@implementer(IEncodableRecord) 1794class Record_AFSDB(tputil.FancyStrMixin, tputil.FancyEqMixin): 1795 """ 1796 Map from a domain name to the name of an AFS cell database server. 1797 1798 @type subtype: L{int} 1799 @ivar subtype: In the case of subtype 1, the host has an AFS version 3.0 1800 Volume Location Server for the named AFS cell. In the case of subtype 1801 2, the host has an authenticated name server holding the cell-root 1802 directory node for the named DCE/NCA cell. 1803 1804 @type hostname: L{Name} 1805 @ivar hostname: The domain name of a host that has a server for the cell 1806 named by this record. 1807 1808 @type ttl: L{int} 1809 @ivar ttl: The maximum number of seconds which this record should be 1810 cached. 1811 1812 @see: U{http://www.faqs.org/rfcs/rfc1183.html} 1813 """ 1814 1815 TYPE = AFSDB 1816 1817 fancybasename = "AFSDB" 1818 compareAttributes = ("subtype", "hostname", "ttl") 1819 showAttributes = ("subtype", ("hostname", "hostname", "%s"), "ttl") 1820 1821 def __init__(self, subtype=0, hostname=b"", ttl=None): 1822 """ 1823 @param hostname: See L{Record_AFSDB.hostname} 1824 @type hostname: L{bytes} or L{str} 1825 """ 1826 self.subtype = int(subtype) 1827 self.hostname = Name(hostname) 1828 self.ttl = str2time(ttl) 1829 1830 def encode(self, strio, compDict=None): 1831 strio.write(struct.pack("!H", self.subtype)) 1832 self.hostname.encode(strio, compDict) 1833 1834 def decode(self, strio, length=None): 1835 r = struct.unpack("!H", readPrecisely(strio, struct.calcsize("!H"))) 1836 (self.subtype,) = r 1837 self.hostname.decode(strio) 1838 1839 def __hash__(self): 1840 return hash((self.subtype, self.hostname)) 1841 1842 1843@implementer(IEncodableRecord) 1844class Record_RP(tputil.FancyEqMixin, tputil.FancyStrMixin): 1845 """ 1846 The responsible person for a domain. 1847 1848 @type mbox: L{Name} 1849 @ivar mbox: A domain name that specifies the mailbox for the responsible 1850 person. 1851 1852 @type txt: L{Name} 1853 @ivar txt: A domain name for which TXT RR's exist (indirection through 1854 which allows information sharing about the contents of this RP record). 1855 1856 @type ttl: L{int} 1857 @ivar ttl: The maximum number of seconds which this record should be 1858 cached. 1859 1860 @see: U{http://www.faqs.org/rfcs/rfc1183.html} 1861 """ 1862 1863 TYPE = RP 1864 1865 fancybasename = "RP" 1866 compareAttributes = ("mbox", "txt", "ttl") 1867 showAttributes = (("mbox", "mbox", "%s"), ("txt", "txt", "%s"), "ttl") 1868 1869 def __init__(self, mbox=b"", txt=b"", ttl=None): 1870 """ 1871 @param mbox: See L{Record_RP.mbox}. 1872 @type mbox: L{bytes} or L{str} 1873 1874 @param txt: See L{Record_RP.txt} 1875 @type txt: L{bytes} or L{str} 1876 """ 1877 self.mbox = Name(mbox) 1878 self.txt = Name(txt) 1879 self.ttl = str2time(ttl) 1880 1881 def encode(self, strio, compDict=None): 1882 self.mbox.encode(strio, compDict) 1883 self.txt.encode(strio, compDict) 1884 1885 def decode(self, strio, length=None): 1886 self.mbox = Name() 1887 self.txt = Name() 1888 self.mbox.decode(strio) 1889 self.txt.decode(strio) 1890 1891 def __hash__(self): 1892 return hash((self.mbox, self.txt)) 1893 1894 1895@implementer(IEncodableRecord) 1896class Record_HINFO(tputil.FancyStrMixin, tputil.FancyEqMixin): 1897 """ 1898 Host information. 1899 1900 @type cpu: L{bytes} 1901 @ivar cpu: Specifies the CPU type. 1902 1903 @type os: L{bytes} 1904 @ivar os: Specifies the OS. 1905 1906 @type ttl: L{int} 1907 @ivar ttl: The maximum number of seconds which this record should be 1908 cached. 1909 """ 1910 1911 TYPE = HINFO 1912 1913 fancybasename = "HINFO" 1914 showAttributes = (("cpu", _nicebytes), ("os", _nicebytes), "ttl") 1915 compareAttributes = ("cpu", "os", "ttl") 1916 1917 def __init__(self, cpu=b"", os=b"", ttl=None): 1918 self.cpu, self.os = cpu, os 1919 self.ttl = str2time(ttl) 1920 1921 def encode(self, strio, compDict=None): 1922 strio.write(struct.pack("!B", len(self.cpu)) + self.cpu) 1923 strio.write(struct.pack("!B", len(self.os)) + self.os) 1924 1925 def decode(self, strio, length=None): 1926 cpu = struct.unpack("!B", readPrecisely(strio, 1))[0] 1927 self.cpu = readPrecisely(strio, cpu) 1928 os = struct.unpack("!B", readPrecisely(strio, 1))[0] 1929 self.os = readPrecisely(strio, os) 1930 1931 def __eq__(self, other: object) -> bool: 1932 if isinstance(other, Record_HINFO): 1933 return ( 1934 self.os.lower() == other.os.lower() 1935 and self.cpu.lower() == other.cpu.lower() 1936 and self.ttl == other.ttl 1937 ) 1938 return NotImplemented 1939 1940 def __hash__(self): 1941 return hash((self.os.lower(), self.cpu.lower())) 1942 1943 1944@implementer(IEncodableRecord) 1945class Record_MINFO(tputil.FancyEqMixin, tputil.FancyStrMixin): 1946 """ 1947 Mailbox or mail list information. 1948 1949 This is an experimental record type. 1950 1951 @type rmailbx: L{Name} 1952 @ivar rmailbx: A domain-name which specifies a mailbox which is responsible 1953 for the mailing list or mailbox. If this domain name names the root, 1954 the owner of the MINFO RR is responsible for itself. 1955 1956 @type emailbx: L{Name} 1957 @ivar emailbx: A domain-name which specifies a mailbox which is to receive 1958 error messages related to the mailing list or mailbox specified by the 1959 owner of the MINFO record. If this domain name names the root, errors 1960 should be returned to the sender of the message. 1961 1962 @type ttl: L{int} 1963 @ivar ttl: The maximum number of seconds which this record should be 1964 cached. 1965 """ 1966 1967 TYPE = MINFO 1968 1969 rmailbx = None 1970 emailbx = None 1971 1972 fancybasename = "MINFO" 1973 compareAttributes = ("rmailbx", "emailbx", "ttl") 1974 showAttributes = ( 1975 ("rmailbx", "responsibility", "%s"), 1976 ("emailbx", "errors", "%s"), 1977 "ttl", 1978 ) 1979 1980 def __init__(self, rmailbx=b"", emailbx=b"", ttl=None): 1981 """ 1982 @param rmailbx: See L{Record_MINFO.rmailbx}. 1983 @type rmailbx: L{bytes} or L{str} 1984 1985 @param emailbx: See L{Record_MINFO.rmailbx}. 1986 @type emailbx: L{bytes} or L{str} 1987 """ 1988 self.rmailbx, self.emailbx = Name(rmailbx), Name(emailbx) 1989 self.ttl = str2time(ttl) 1990 1991 def encode(self, strio, compDict=None): 1992 self.rmailbx.encode(strio, compDict) 1993 self.emailbx.encode(strio, compDict) 1994 1995 def decode(self, strio, length=None): 1996 self.rmailbx, self.emailbx = Name(), Name() 1997 self.rmailbx.decode(strio) 1998 self.emailbx.decode(strio) 1999 2000 def __hash__(self): 2001 return hash((self.rmailbx, self.emailbx)) 2002 2003 2004@implementer(IEncodableRecord) 2005class Record_MX(tputil.FancyStrMixin, tputil.FancyEqMixin): 2006 """ 2007 Mail exchange. 2008 2009 @type preference: L{int} 2010 @ivar preference: Specifies the preference given to this RR among others at 2011 the same owner. Lower values are preferred. 2012 2013 @type name: L{Name} 2014 @ivar name: A domain-name which specifies a host willing to act as a mail 2015 exchange. 2016 2017 @type ttl: L{int} 2018 @ivar ttl: The maximum number of seconds which this record should be 2019 cached. 2020 """ 2021 2022 TYPE = MX 2023 2024 fancybasename = "MX" 2025 compareAttributes = ("preference", "name", "ttl") 2026 showAttributes = ("preference", ("name", "name", "%s"), "ttl") 2027 2028 def __init__(self, preference=0, name=b"", ttl=None, **kwargs): 2029 """ 2030 @param name: See L{Record_MX.name}. 2031 @type name: L{bytes} or L{str} 2032 """ 2033 self.preference = int(preference) 2034 self.name = Name(kwargs.get("exchange", name)) 2035 self.ttl = str2time(ttl) 2036 2037 def encode(self, strio, compDict=None): 2038 strio.write(struct.pack("!H", self.preference)) 2039 self.name.encode(strio, compDict) 2040 2041 def decode(self, strio, length=None): 2042 self.preference = struct.unpack("!H", readPrecisely(strio, 2))[0] 2043 self.name = Name() 2044 self.name.decode(strio) 2045 2046 def __hash__(self): 2047 return hash((self.preference, self.name)) 2048 2049 2050@implementer(IEncodableRecord) 2051class Record_SSHFP(tputil.FancyEqMixin, tputil.FancyStrMixin): 2052 """ 2053 A record containing the fingerprint of an SSH key. 2054 2055 @type algorithm: L{int} 2056 @ivar algorithm: The SSH key's algorithm, such as L{ALGORITHM_RSA}. 2057 Note that the numbering used for SSH key algorithms is specific 2058 to the SSHFP record, and is not the same as the numbering 2059 used for KEY or SIG records. 2060 2061 @type fingerprintType: L{int} 2062 @ivar fingerprintType: The fingerprint type, 2063 such as L{FINGERPRINT_TYPE_SHA256}. 2064 2065 @type fingerprint: L{bytes} 2066 @ivar fingerprint: The key's fingerprint, e.g. a 32-byte SHA-256 digest. 2067 2068 @cvar ALGORITHM_RSA: The algorithm value for C{ssh-rsa} keys. 2069 @cvar ALGORITHM_DSS: The algorithm value for C{ssh-dss} keys. 2070 @cvar ALGORITHM_ECDSA: The algorithm value for C{ecdsa-sha2-*} keys. 2071 @cvar ALGORITHM_Ed25519: The algorithm value for C{ed25519} keys. 2072 2073 @cvar FINGERPRINT_TYPE_SHA1: The type for SHA-1 fingerprints. 2074 @cvar FINGERPRINT_TYPE_SHA256: The type for SHA-256 fingerprints. 2075 2076 @see: U{RFC 4255 <https://tools.ietf.org/html/rfc4255>} 2077 and 2078 U{RFC 6594 <https://tools.ietf.org/html/rfc6594>} 2079 """ 2080 2081 fancybasename = "SSHFP" 2082 compareAttributes = ("algorithm", "fingerprintType", "fingerprint", "ttl") 2083 showAttributes = ("algorithm", "fingerprintType", "fingerprint") 2084 2085 TYPE = SSHFP 2086 2087 ALGORITHM_RSA = 1 2088 ALGORITHM_DSS = 2 2089 ALGORITHM_ECDSA = 3 2090 ALGORITHM_Ed25519 = 4 2091 2092 FINGERPRINT_TYPE_SHA1 = 1 2093 FINGERPRINT_TYPE_SHA256 = 2 2094 2095 def __init__(self, algorithm=0, fingerprintType=0, fingerprint=b"", ttl=0): 2096 self.algorithm = algorithm 2097 self.fingerprintType = fingerprintType 2098 self.fingerprint = fingerprint 2099 self.ttl = ttl 2100 2101 def encode(self, strio, compDict=None): 2102 strio.write(struct.pack("!BB", self.algorithm, self.fingerprintType)) 2103 strio.write(self.fingerprint) 2104 2105 def decode(self, strio, length=None): 2106 r = struct.unpack("!BB", readPrecisely(strio, 2)) 2107 (self.algorithm, self.fingerprintType) = r 2108 self.fingerprint = readPrecisely(strio, length - 2) 2109 2110 def __hash__(self): 2111 return hash((self.algorithm, self.fingerprintType, self.fingerprint)) 2112 2113 2114@implementer(IEncodableRecord) 2115class Record_TXT(tputil.FancyEqMixin, tputil.FancyStrMixin): 2116 """ 2117 Freeform text. 2118 2119 @type data: L{list} of L{bytes} 2120 @ivar data: Freeform text which makes up this record. 2121 2122 @type ttl: L{int} 2123 @ivar ttl: The maximum number of seconds which this record should be cached. 2124 """ 2125 2126 TYPE = TXT 2127 2128 fancybasename = "TXT" 2129 showAttributes = (("data", _nicebyteslist), "ttl") 2130 compareAttributes = ("data", "ttl") 2131 2132 def __init__(self, *data, **kw): 2133 self.data = list(data) 2134 # arg man python sucks so bad 2135 self.ttl = str2time(kw.get("ttl", None)) 2136 2137 def encode(self, strio, compDict=None): 2138 for d in self.data: 2139 strio.write(struct.pack("!B", len(d)) + d) 2140 2141 def decode(self, strio, length=None): 2142 soFar = 0 2143 self.data = [] 2144 while soFar < length: 2145 L = struct.unpack("!B", readPrecisely(strio, 1))[0] 2146 self.data.append(readPrecisely(strio, L)) 2147 soFar += L + 1 2148 if soFar != length: 2149 log.msg( 2150 "Decoded %d bytes in %s record, but rdlength is %d" 2151 % (soFar, self.fancybasename, length) 2152 ) 2153 2154 def __hash__(self): 2155 return hash(tuple(self.data)) 2156 2157 2158@implementer(IEncodableRecord) 2159class UnknownRecord(tputil.FancyEqMixin, tputil.FancyStrMixin): 2160 """ 2161 Encapsulate the wire data for unknown record types so that they can 2162 pass through the system unchanged. 2163 2164 @type data: L{bytes} 2165 @ivar data: Wire data which makes up this record. 2166 2167 @type ttl: L{int} 2168 @ivar ttl: The maximum number of seconds which this record should be cached. 2169 2170 @since: 11.1 2171 """ 2172 2173 TYPE = None 2174 2175 fancybasename = "UNKNOWN" 2176 compareAttributes = ("data", "ttl") 2177 showAttributes = (("data", _nicebytes), "ttl") 2178 2179 def __init__(self, data=b"", ttl=None): 2180 self.data = data 2181 self.ttl = str2time(ttl) 2182 2183 def encode(self, strio, compDict=None): 2184 """ 2185 Write the raw bytes corresponding to this record's payload to the 2186 stream. 2187 """ 2188 strio.write(self.data) 2189 2190 def decode(self, strio, length=None): 2191 """ 2192 Load the bytes which are part of this record from the stream and store 2193 them unparsed and unmodified. 2194 """ 2195 if length is None: 2196 raise Exception("must know length for unknown record types") 2197 self.data = readPrecisely(strio, length) 2198 2199 def __hash__(self): 2200 return hash((self.data, self.ttl)) 2201 2202 2203class Record_SPF(Record_TXT): 2204 """ 2205 Structurally, freeform text. Semantically, a policy definition, formatted 2206 as defined in U{rfc 4408<http://www.faqs.org/rfcs/rfc4408.html>}. 2207 2208 @type data: L{list} of L{bytes} 2209 @ivar data: Freeform text which makes up this record. 2210 2211 @type ttl: L{int} 2212 @ivar ttl: The maximum number of seconds 2213 which this record should be cached. 2214 """ 2215 2216 TYPE = SPF 2217 fancybasename = "SPF" 2218 2219 2220@implementer(IEncodableRecord) 2221class Record_TSIG(tputil.FancyEqMixin, tputil.FancyStrMixin): 2222 """ 2223 A transaction signature, encapsulated in a RR, as described 2224 in U{RFC 2845 <https://tools.ietf.org/html/rfc2845>}. 2225 2226 @type algorithm: L{Name} 2227 @ivar algorithm: The name of the signature or MAC algorithm. 2228 2229 @type timeSigned: L{int} 2230 @ivar timeSigned: Signing time, as seconds from the POSIX epoch. 2231 2232 @type fudge: L{int} 2233 @ivar fudge: Allowable time skew, in seconds. 2234 2235 @type MAC: L{bytes} 2236 @ivar MAC: The message digest or signature. 2237 2238 @type originalID: L{int} 2239 @ivar originalID: A message ID. 2240 2241 @type error: L{int} 2242 @ivar error: An error code (extended C{RCODE}) carried 2243 in exceptional cases. 2244 2245 @type otherData: L{bytes} 2246 @ivar otherData: Other data carried in exceptional cases. 2247 2248 """ 2249 2250 fancybasename = "TSIG" 2251 compareAttributes = ( 2252 "algorithm", 2253 "timeSigned", 2254 "fudge", 2255 "MAC", 2256 "originalID", 2257 "error", 2258 "otherData", 2259 "ttl", 2260 ) 2261 showAttributes = ["algorithm", "timeSigned", "MAC", "error", "otherData"] 2262 2263 TYPE = TSIG 2264 2265 def __init__( 2266 self, 2267 algorithm=None, 2268 timeSigned=None, 2269 fudge=5, 2270 MAC=None, 2271 originalID=0, 2272 error=OK, 2273 otherData=b"", 2274 ttl=0, 2275 ): 2276 # All of our init arguments have to have defaults, because of 2277 # the way IEncodable and Message.parseRecords() work, but for 2278 # some of our arguments there is no reasonable default; we use 2279 # invalid values here to prevent a user of this class from 2280 # relying on what's really an internal implementation detail. 2281 self.algorithm = None if algorithm is None else Name(algorithm) 2282 self.timeSigned = timeSigned 2283 self.fudge = str2time(fudge) 2284 self.MAC = MAC 2285 self.originalID = originalID 2286 self.error = error 2287 self.otherData = otherData 2288 self.ttl = ttl 2289 2290 def encode(self, strio, compDict=None): 2291 self.algorithm.encode(strio, compDict) 2292 strio.write(struct.pack("!Q", self.timeSigned)[2:]) # 48-bit number 2293 strio.write(struct.pack("!HH", self.fudge, len(self.MAC))) 2294 strio.write(self.MAC) 2295 strio.write( 2296 struct.pack("!HHH", self.originalID, self.error, len(self.otherData)) 2297 ) 2298 strio.write(self.otherData) 2299 2300 def decode(self, strio, length=None): 2301 algorithm = Name() 2302 algorithm.decode(strio) 2303 self.algorithm = algorithm 2304 fields = struct.unpack("!QHH", b"\x00\x00" + readPrecisely(strio, 10)) 2305 self.timeSigned, self.fudge, macLength = fields 2306 self.MAC = readPrecisely(strio, macLength) 2307 fields = struct.unpack("!HHH", readPrecisely(strio, 6)) 2308 self.originalID, self.error, otherLength = fields 2309 self.otherData = readPrecisely(strio, otherLength) 2310 2311 def __hash__(self): 2312 return hash((self.algorithm, self.timeSigned, self.MAC, self.originalID)) 2313 2314 2315def _responseFromMessage(responseConstructor, message, **kwargs): 2316 """ 2317 Generate a L{Message} like instance suitable for use as the response to 2318 C{message}. 2319 2320 The C{queries}, C{id} attributes will be copied from C{message} and the 2321 C{answer} flag will be set to L{True}. 2322 2323 @param responseConstructor: A response message constructor with an 2324 initializer signature matching L{dns.Message.__init__}. 2325 @type responseConstructor: C{callable} 2326 2327 @param message: A request message. 2328 @type message: L{Message} 2329 2330 @param kwargs: Keyword arguments which will be passed to the initialiser 2331 of the response message. 2332 @type kwargs: L{dict} 2333 2334 @return: A L{Message} like response instance. 2335 @rtype: C{responseConstructor} 2336 """ 2337 response = responseConstructor(id=message.id, answer=True, **kwargs) 2338 response.queries = message.queries[:] 2339 return response 2340 2341 2342def _getDisplayableArguments(obj, alwaysShow, fieldNames): 2343 """ 2344 Inspect the function signature of C{obj}'s constructor, 2345 and get a list of which arguments should be displayed. 2346 This is a helper function for C{_compactRepr}. 2347 2348 @param obj: The instance whose repr is being generated. 2349 @param alwaysShow: A L{list} of field names which should always be shown. 2350 @param fieldNames: A L{list} of field attribute names which should be shown 2351 if they have non-default values. 2352 @return: A L{list} of displayable arguments. 2353 """ 2354 displayableArgs = [] 2355 # Get the argument names and values from the constructor. 2356 signature = inspect.signature(obj.__class__.__init__) 2357 for name in fieldNames: 2358 defaultValue = signature.parameters[name].default 2359 fieldValue = getattr(obj, name, defaultValue) 2360 if (name in alwaysShow) or (fieldValue != defaultValue): 2361 displayableArgs.append(f" {name}={fieldValue!r}") 2362 2363 return displayableArgs 2364 2365 2366def _compactRepr( 2367 obj, alwaysShow=None, flagNames=None, fieldNames=None, sectionNames=None 2368): 2369 """ 2370 Return a L{str} representation of C{obj} which only shows fields with 2371 non-default values, flags which are True and sections which have been 2372 explicitly set. 2373 2374 @param obj: The instance whose repr is being generated. 2375 @param alwaysShow: A L{list} of field names which should always be shown. 2376 @param flagNames: A L{list} of flag attribute names which should be shown if 2377 they are L{True}. 2378 @param fieldNames: A L{list} of field attribute names which should be shown 2379 if they have non-default values. 2380 @param sectionNames: A L{list} of section attribute names which should be 2381 shown if they have been assigned a value. 2382 2383 @return: A L{str} representation of C{obj}. 2384 """ 2385 if alwaysShow is None: 2386 alwaysShow = [] 2387 2388 if flagNames is None: 2389 flagNames = [] 2390 2391 if fieldNames is None: 2392 fieldNames = [] 2393 2394 if sectionNames is None: 2395 sectionNames = [] 2396 2397 setFlags = [] 2398 for name in flagNames: 2399 if name in alwaysShow or getattr(obj, name, False) == True: 2400 setFlags.append(name) 2401 2402 displayableArgs = _getDisplayableArguments(obj, alwaysShow, fieldNames) 2403 out = ["<", obj.__class__.__name__] + displayableArgs 2404 2405 if setFlags: 2406 out.append(" flags={}".format(",".join(setFlags))) 2407 2408 for name in sectionNames: 2409 section = getattr(obj, name, []) 2410 if section: 2411 out.append(f" {name}={section!r}") 2412 2413 out.append(">") 2414 2415 return "".join(out) 2416 2417 2418class Message(tputil.FancyEqMixin): 2419 """ 2420 L{Message} contains all the information represented by a single 2421 DNS request or response. 2422 2423 @ivar id: See L{__init__} 2424 @ivar answer: See L{__init__} 2425 @ivar opCode: See L{__init__} 2426 @ivar recDes: See L{__init__} 2427 @ivar recAv: See L{__init__} 2428 @ivar auth: See L{__init__} 2429 @ivar rCode: See L{__init__} 2430 @ivar trunc: See L{__init__} 2431 @ivar maxSize: See L{__init__} 2432 @ivar authenticData: See L{__init__} 2433 @ivar checkingDisabled: See L{__init__} 2434 2435 @ivar queries: The queries which are being asked of or answered by 2436 DNS server. 2437 @type queries: L{list} of L{Query} 2438 2439 @ivar answers: Records containing the answers to C{queries} if 2440 this is a response message. 2441 @type answers: L{list} of L{RRHeader} 2442 2443 @ivar authority: Records containing information about the 2444 authoritative DNS servers for the names in C{queries}. 2445 @type authority: L{list} of L{RRHeader} 2446 2447 @ivar additional: Records containing IP addresses of host names 2448 in C{answers} and C{authority}. 2449 @type additional: L{list} of L{RRHeader} 2450 2451 @ivar _flagNames: The names of attributes representing the flag header 2452 fields. 2453 @ivar _fieldNames: The names of attributes representing non-flag fixed 2454 header fields. 2455 @ivar _sectionNames: The names of attributes representing the record 2456 sections of this message. 2457 """ 2458 2459 compareAttributes = ( 2460 "id", 2461 "answer", 2462 "opCode", 2463 "recDes", 2464 "recAv", 2465 "auth", 2466 "rCode", 2467 "trunc", 2468 "maxSize", 2469 "authenticData", 2470 "checkingDisabled", 2471 "queries", 2472 "answers", 2473 "authority", 2474 "additional", 2475 ) 2476 2477 headerFmt = "!H2B4H" 2478 headerSize = struct.calcsize(headerFmt) 2479 2480 # Question, answer, additional, and nameserver lists 2481 queries = answers = add = ns = None 2482 2483 def __init__( 2484 self, 2485 id=0, 2486 answer=0, 2487 opCode=0, 2488 recDes=0, 2489 recAv=0, 2490 auth=0, 2491 rCode=OK, 2492 trunc=0, 2493 maxSize=512, 2494 authenticData=0, 2495 checkingDisabled=0, 2496 ): 2497 """ 2498 @param id: A 16 bit identifier assigned by the program that 2499 generates any kind of query. This identifier is copied to 2500 the corresponding reply and can be used by the requester 2501 to match up replies to outstanding queries. 2502 @type id: L{int} 2503 2504 @param answer: A one bit field that specifies whether this 2505 message is a query (0), or a response (1). 2506 @type answer: L{int} 2507 2508 @param opCode: A four bit field that specifies kind of query in 2509 this message. This value is set by the originator of a query 2510 and copied into the response. 2511 @type opCode: L{int} 2512 2513 @param recDes: Recursion Desired - this bit may be set in a 2514 query and is copied into the response. If RD is set, it 2515 directs the name server to pursue the query recursively. 2516 Recursive query support is optional. 2517 @type recDes: L{int} 2518 2519 @param recAv: Recursion Available - this bit is set or cleared 2520 in a response and denotes whether recursive query support 2521 is available in the name server. 2522 @type recAv: L{int} 2523 2524 @param auth: Authoritative Answer - this bit is valid in 2525 responses and specifies that the responding name server 2526 is an authority for the domain name in question section. 2527 @type auth: L{int} 2528 2529 @ivar rCode: A response code, used to indicate success or failure in a 2530 message which is a response from a server to a client request. 2531 @type rCode: C{0 <= int < 16} 2532 2533 @param trunc: A flag indicating that this message was 2534 truncated due to length greater than that permitted on the 2535 transmission channel. 2536 @type trunc: L{int} 2537 2538 @param maxSize: The requestor's UDP payload size is the number 2539 of octets of the largest UDP payload that can be 2540 reassembled and delivered in the requestor's network 2541 stack. 2542 @type maxSize: L{int} 2543 2544 @param authenticData: A flag indicating in a response that all 2545 the data included in the answer and authority portion of 2546 the response has been authenticated by the server 2547 according to the policies of that server. 2548 See U{RFC2535 section-6.1<https://tools.ietf.org/html/rfc2535#section-6.1>}. 2549 @type authenticData: L{int} 2550 2551 @param checkingDisabled: A flag indicating in a query that 2552 pending (non-authenticated) data is acceptable to the 2553 resolver sending the query. 2554 See U{RFC2535 section-6.1<https://tools.ietf.org/html/rfc2535#section-6.1>}. 2555 @type authenticData: L{int} 2556 """ 2557 self.maxSize = maxSize 2558 self.id = id 2559 self.answer = answer 2560 self.opCode = opCode 2561 self.auth = auth 2562 self.trunc = trunc 2563 self.recDes = recDes 2564 self.recAv = recAv 2565 self.rCode = rCode 2566 self.authenticData = authenticData 2567 self.checkingDisabled = checkingDisabled 2568 2569 self.queries = [] 2570 self.answers = [] 2571 self.authority = [] 2572 self.additional = [] 2573 2574 def __repr__(self) -> str: 2575 """ 2576 Generate a repr of this L{Message}. 2577 2578 Only includes the non-default fields and sections and only includes 2579 flags which are set. The C{id} is always shown. 2580 2581 @return: The native string repr. 2582 """ 2583 return _compactRepr( 2584 self, 2585 flagNames=( 2586 "answer", 2587 "auth", 2588 "trunc", 2589 "recDes", 2590 "recAv", 2591 "authenticData", 2592 "checkingDisabled", 2593 ), 2594 fieldNames=("id", "opCode", "rCode", "maxSize"), 2595 sectionNames=("queries", "answers", "authority", "additional"), 2596 alwaysShow=("id",), 2597 ) 2598 2599 def addQuery(self, name, type=ALL_RECORDS, cls=IN): 2600 """ 2601 Add another query to this Message. 2602 2603 @type name: L{bytes} 2604 @param name: The name to query. 2605 2606 @type type: L{int} 2607 @param type: Query type 2608 2609 @type cls: L{int} 2610 @param cls: Query class 2611 """ 2612 self.queries.append(Query(name, type, cls)) 2613 2614 def encode(self, strio): 2615 compDict = {} 2616 body_tmp = BytesIO() 2617 for q in self.queries: 2618 q.encode(body_tmp, compDict) 2619 for q in self.answers: 2620 q.encode(body_tmp, compDict) 2621 for q in self.authority: 2622 q.encode(body_tmp, compDict) 2623 for q in self.additional: 2624 q.encode(body_tmp, compDict) 2625 body = body_tmp.getvalue() 2626 size = len(body) + self.headerSize 2627 if self.maxSize and size > self.maxSize: 2628 self.trunc = 1 2629 body = body[: self.maxSize - self.headerSize] 2630 byte3 = ( 2631 ((self.answer & 1) << 7) 2632 | ((self.opCode & 0xF) << 3) 2633 | ((self.auth & 1) << 2) 2634 | ((self.trunc & 1) << 1) 2635 | (self.recDes & 1) 2636 ) 2637 byte4 = ( 2638 ((self.recAv & 1) << 7) 2639 | ((self.authenticData & 1) << 5) 2640 | ((self.checkingDisabled & 1) << 4) 2641 | (self.rCode & 0xF) 2642 ) 2643 2644 strio.write( 2645 struct.pack( 2646 self.headerFmt, 2647 self.id, 2648 byte3, 2649 byte4, 2650 len(self.queries), 2651 len(self.answers), 2652 len(self.authority), 2653 len(self.additional), 2654 ) 2655 ) 2656 strio.write(body) 2657 2658 def decode(self, strio, length=None): 2659 self.maxSize = 0 2660 header = readPrecisely(strio, self.headerSize) 2661 r = struct.unpack(self.headerFmt, header) 2662 self.id, byte3, byte4, nqueries, nans, nns, nadd = r 2663 self.answer = (byte3 >> 7) & 1 2664 self.opCode = (byte3 >> 3) & 0xF 2665 self.auth = (byte3 >> 2) & 1 2666 self.trunc = (byte3 >> 1) & 1 2667 self.recDes = byte3 & 1 2668 self.recAv = (byte4 >> 7) & 1 2669 self.authenticData = (byte4 >> 5) & 1 2670 self.checkingDisabled = (byte4 >> 4) & 1 2671 self.rCode = byte4 & 0xF 2672 2673 self.queries = [] 2674 for i in range(nqueries): 2675 q = Query() 2676 try: 2677 q.decode(strio) 2678 except EOFError: 2679 return 2680 self.queries.append(q) 2681 2682 items = ((self.answers, nans), (self.authority, nns), (self.additional, nadd)) 2683 2684 for (l, n) in items: 2685 self.parseRecords(l, n, strio) 2686 2687 def parseRecords(self, list, num, strio): 2688 for i in range(num): 2689 header = RRHeader(auth=self.auth) 2690 try: 2691 header.decode(strio) 2692 except EOFError: 2693 return 2694 t = self.lookupRecordType(header.type) 2695 if not t: 2696 continue 2697 header.payload = t(ttl=header.ttl) 2698 try: 2699 header.payload.decode(strio, header.rdlength) 2700 except EOFError: 2701 return 2702 list.append(header) 2703 2704 # Create a mapping from record types to their corresponding Record_* 2705 # classes. This relies on the global state which has been created so 2706 # far in initializing this module (so don't define Record classes after 2707 # this). 2708 _recordTypes = {} 2709 for name in globals(): 2710 if name.startswith("Record_"): 2711 _recordTypes[globals()[name].TYPE] = globals()[name] 2712 2713 # Clear the iteration variable out of the class namespace so it 2714 # doesn't become an attribute. 2715 del name 2716 2717 def lookupRecordType(self, type): 2718 """ 2719 Retrieve the L{IRecord} implementation for the given record type. 2720 2721 @param type: A record type, such as C{A} or L{NS}. 2722 @type type: L{int} 2723 2724 @return: An object which implements L{IRecord} or L{None} if none 2725 can be found for the given type. 2726 @rtype: C{Type[IRecord]} 2727 """ 2728 return self._recordTypes.get(type, UnknownRecord) 2729 2730 def toStr(self): 2731 """ 2732 Encode this L{Message} into a byte string in the format described by RFC 2733 1035. 2734 2735 @rtype: L{bytes} 2736 """ 2737 strio = BytesIO() 2738 self.encode(strio) 2739 return strio.getvalue() 2740 2741 def fromStr(self, str): 2742 """ 2743 Decode a byte string in the format described by RFC 1035 into this 2744 L{Message}. 2745 2746 @param str: L{bytes} 2747 """ 2748 strio = BytesIO(str) 2749 self.decode(strio) 2750 2751 2752class _EDNSMessage(tputil.FancyEqMixin): 2753 """ 2754 An I{EDNS} message. 2755 2756 Designed for compatibility with L{Message} but with a narrower public 2757 interface. 2758 2759 Most importantly, L{_EDNSMessage.fromStr} will interpret and remove I{OPT} 2760 records that are present in the additional records section. 2761 2762 The I{OPT} records are used to populate certain I{EDNS} specific attributes. 2763 2764 L{_EDNSMessage.toStr} will add suitable I{OPT} records to the additional 2765 section to represent the extended EDNS information. 2766 2767 @see: U{https://tools.ietf.org/html/rfc6891} 2768 2769 @ivar id: See L{__init__} 2770 @ivar answer: See L{__init__} 2771 @ivar opCode: See L{__init__} 2772 @ivar auth: See L{__init__} 2773 @ivar trunc: See L{__init__} 2774 @ivar recDes: See L{__init__} 2775 @ivar recAv: See L{__init__} 2776 @ivar rCode: See L{__init__} 2777 @ivar ednsVersion: See L{__init__} 2778 @ivar dnssecOK: See L{__init__} 2779 @ivar authenticData: See L{__init__} 2780 @ivar checkingDisabled: See L{__init__} 2781 @ivar maxSize: See L{__init__} 2782 2783 @ivar queries: See L{__init__} 2784 @ivar answers: See L{__init__} 2785 @ivar authority: See L{__init__} 2786 @ivar additional: See L{__init__} 2787 2788 @ivar _messageFactory: A constructor of L{Message} instances. Called by 2789 C{_toMessage} and C{_fromMessage}. 2790 """ 2791 2792 compareAttributes = ( 2793 "id", 2794 "answer", 2795 "opCode", 2796 "auth", 2797 "trunc", 2798 "recDes", 2799 "recAv", 2800 "rCode", 2801 "ednsVersion", 2802 "dnssecOK", 2803 "authenticData", 2804 "checkingDisabled", 2805 "maxSize", 2806 "queries", 2807 "answers", 2808 "authority", 2809 "additional", 2810 ) 2811 2812 _messageFactory = Message 2813 2814 def __init__( 2815 self, 2816 id=0, 2817 answer=False, 2818 opCode=OP_QUERY, 2819 auth=False, 2820 trunc=False, 2821 recDes=False, 2822 recAv=False, 2823 rCode=0, 2824 ednsVersion=0, 2825 dnssecOK=False, 2826 authenticData=False, 2827 checkingDisabled=False, 2828 maxSize=512, 2829 queries=None, 2830 answers=None, 2831 authority=None, 2832 additional=None, 2833 ): 2834 """ 2835 Construct a new L{_EDNSMessage} 2836 2837 @see: U{RFC1035 section-4.1.1<https://tools.ietf.org/html/rfc1035#section-4.1.1>} 2838 @see: U{RFC2535 section-6.1<https://tools.ietf.org/html/rfc2535#section-6.1>} 2839 @see: U{RFC3225 section-3<https://tools.ietf.org/html/rfc3225#section-3>} 2840 @see: U{RFC6891 section-6.1.3<https://tools.ietf.org/html/rfc6891#section-6.1.3>} 2841 2842 @param id: A 16 bit identifier assigned by the program that generates 2843 any kind of query. This identifier is copied the corresponding 2844 reply and can be used by the requester to match up replies to 2845 outstanding queries. 2846 @type id: L{int} 2847 2848 @param answer: A one bit field that specifies whether this message is a 2849 query (0), or a response (1). 2850 @type answer: L{bool} 2851 2852 @param opCode: A four bit field that specifies kind of query in this 2853 message. This value is set by the originator of a query and copied 2854 into the response. 2855 @type opCode: L{int} 2856 2857 @param auth: Authoritative Answer - this bit is valid in responses, and 2858 specifies that the responding name server is an authority for the 2859 domain name in question section. 2860 @type auth: L{bool} 2861 2862 @param trunc: Truncation - specifies that this message was truncated due 2863 to length greater than that permitted on the transmission channel. 2864 @type trunc: L{bool} 2865 2866 @param recDes: Recursion Desired - this bit may be set in a query and is 2867 copied into the response. If set, it directs the name server to 2868 pursue the query recursively. Recursive query support is optional. 2869 @type recDes: L{bool} 2870 2871 @param recAv: Recursion Available - this bit is set or cleared in a 2872 response, and denotes whether recursive query support is available 2873 in the name server. 2874 @type recAv: L{bool} 2875 2876 @param rCode: Extended 12-bit RCODE. Derived from the 4 bits defined in 2877 U{RFC1035 4.1.1<https://tools.ietf.org/html/rfc1035#section-4.1.1>} 2878 and the upper 8bits defined in U{RFC6891 2879 6.1.3<https://tools.ietf.org/html/rfc6891#section-6.1.3>}. 2880 @type rCode: L{int} 2881 2882 @param ednsVersion: Indicates the EDNS implementation level. Set to 2883 L{None} to prevent any EDNS attributes and options being added to 2884 the encoded byte string. 2885 @type ednsVersion: L{int} or L{None} 2886 2887 @param dnssecOK: DNSSEC OK bit as defined by 2888 U{RFC3225 3<https://tools.ietf.org/html/rfc3225#section-3>}. 2889 @type dnssecOK: L{bool} 2890 2891 @param authenticData: A flag indicating in a response that all the data 2892 included in the answer and authority portion of the response has 2893 been authenticated by the server according to the policies of that 2894 server. 2895 See U{RFC2535 section-6.1<https://tools.ietf.org/html/rfc2535#section-6.1>}. 2896 @type authenticData: L{bool} 2897 2898 @param checkingDisabled: A flag indicating in a query that pending 2899 (non-authenticated) data is acceptable to the resolver sending the 2900 query. 2901 See U{RFC2535 section-6.1<https://tools.ietf.org/html/rfc2535#section-6.1>}. 2902 @type authenticData: L{bool} 2903 2904 @param maxSize: The requestor's UDP payload size is the number of octets 2905 of the largest UDP payload that can be reassembled and delivered in 2906 the requestor's network stack. 2907 @type maxSize: L{int} 2908 2909 @param queries: The L{list} of L{Query} associated with this message. 2910 @type queries: L{list} of L{Query} 2911 2912 @param answers: The L{list} of answers associated with this message. 2913 @type answers: L{list} of L{RRHeader} 2914 2915 @param authority: The L{list} of authority records associated with this 2916 message. 2917 @type authority: L{list} of L{RRHeader} 2918 2919 @param additional: The L{list} of additional records associated with 2920 this message. 2921 @type additional: L{list} of L{RRHeader} 2922 """ 2923 self.id = id 2924 self.answer = answer 2925 self.opCode = opCode 2926 self.auth = auth 2927 self.trunc = trunc 2928 self.recDes = recDes 2929 self.recAv = recAv 2930 self.rCode = rCode 2931 self.ednsVersion = ednsVersion 2932 self.dnssecOK = dnssecOK 2933 self.authenticData = authenticData 2934 self.checkingDisabled = checkingDisabled 2935 self.maxSize = maxSize 2936 2937 if queries is None: 2938 queries = [] 2939 self.queries = queries 2940 2941 if answers is None: 2942 answers = [] 2943 self.answers = answers 2944 2945 if authority is None: 2946 authority = [] 2947 self.authority = authority 2948 2949 if additional is None: 2950 additional = [] 2951 self.additional = additional 2952 2953 def __repr__(self) -> str: 2954 return _compactRepr( 2955 self, 2956 flagNames=( 2957 "answer", 2958 "auth", 2959 "trunc", 2960 "recDes", 2961 "recAv", 2962 "authenticData", 2963 "checkingDisabled", 2964 "dnssecOK", 2965 ), 2966 fieldNames=("id", "opCode", "rCode", "maxSize", "ednsVersion"), 2967 sectionNames=("queries", "answers", "authority", "additional"), 2968 alwaysShow=("id",), 2969 ) 2970 2971 def _toMessage(self): 2972 """ 2973 Convert to a standard L{dns.Message}. 2974 2975 If C{ednsVersion} is not None, an L{_OPTHeader} instance containing all 2976 the I{EDNS} specific attributes and options will be appended to the list 2977 of C{additional} records. 2978 2979 @return: A L{dns.Message} 2980 @rtype: L{dns.Message} 2981 """ 2982 m = self._messageFactory( 2983 id=self.id, 2984 answer=self.answer, 2985 opCode=self.opCode, 2986 auth=self.auth, 2987 trunc=self.trunc, 2988 recDes=self.recDes, 2989 recAv=self.recAv, 2990 # Assign the lower 4 bits to the message 2991 rCode=self.rCode & 0xF, 2992 authenticData=self.authenticData, 2993 checkingDisabled=self.checkingDisabled, 2994 ) 2995 2996 m.queries = self.queries[:] 2997 m.answers = self.answers[:] 2998 m.authority = self.authority[:] 2999 m.additional = self.additional[:] 3000 3001 if self.ednsVersion is not None: 3002 o = _OPTHeader( 3003 version=self.ednsVersion, 3004 dnssecOK=self.dnssecOK, 3005 udpPayloadSize=self.maxSize, 3006 # Assign the upper 8 bits to the OPT record 3007 extendedRCODE=self.rCode >> 4, 3008 ) 3009 m.additional.append(o) 3010 3011 return m 3012 3013 def toStr(self): 3014 """ 3015 Encode to wire format by first converting to a standard L{dns.Message}. 3016 3017 @return: A L{bytes} string. 3018 """ 3019 return self._toMessage().toStr() 3020 3021 @classmethod 3022 def _fromMessage(cls, message): 3023 """ 3024 Construct and return a new L{_EDNSMessage} whose attributes and records 3025 are derived from the attributes and records of C{message} (a L{Message} 3026 instance). 3027 3028 If present, an C{OPT} record will be extracted from the C{additional} 3029 section and its attributes and options will be used to set the EDNS 3030 specific attributes C{extendedRCODE}, C{ednsVersion}, C{dnssecOK}, 3031 C{ednsOptions}. 3032 3033 The C{extendedRCODE} will be combined with C{message.rCode} and assigned 3034 to C{self.rCode}. 3035 3036 @param message: The source L{Message}. 3037 @type message: L{Message} 3038 3039 @return: A new L{_EDNSMessage} 3040 @rtype: L{_EDNSMessage} 3041 """ 3042 additional = [] 3043 optRecords = [] 3044 for r in message.additional: 3045 if r.type == OPT: 3046 optRecords.append(_OPTHeader.fromRRHeader(r)) 3047 else: 3048 additional.append(r) 3049 3050 newMessage = cls( 3051 id=message.id, 3052 answer=message.answer, 3053 opCode=message.opCode, 3054 auth=message.auth, 3055 trunc=message.trunc, 3056 recDes=message.recDes, 3057 recAv=message.recAv, 3058 rCode=message.rCode, 3059 authenticData=message.authenticData, 3060 checkingDisabled=message.checkingDisabled, 3061 # Default to None, it will be updated later when the OPT records are 3062 # parsed. 3063 ednsVersion=None, 3064 dnssecOK=False, 3065 queries=message.queries[:], 3066 answers=message.answers[:], 3067 authority=message.authority[:], 3068 additional=additional, 3069 ) 3070 3071 if len(optRecords) == 1: 3072 # XXX: If multiple OPT records are received, an EDNS server should 3073 # respond with FORMERR. See ticket:5669#comment:1. 3074 opt = optRecords[0] 3075 newMessage.ednsVersion = opt.version 3076 newMessage.dnssecOK = opt.dnssecOK 3077 newMessage.maxSize = opt.udpPayloadSize 3078 newMessage.rCode = opt.extendedRCODE << 4 | message.rCode 3079 3080 return newMessage 3081 3082 def fromStr(self, bytes): 3083 """ 3084 Decode from wire format, saving flags, values and records to this 3085 L{_EDNSMessage} instance in place. 3086 3087 @param bytes: The full byte string to be decoded. 3088 @type bytes: L{bytes} 3089 """ 3090 m = self._messageFactory() 3091 m.fromStr(bytes) 3092 3093 ednsMessage = self._fromMessage(m) 3094 for attrName in self.compareAttributes: 3095 setattr(self, attrName, getattr(ednsMessage, attrName)) 3096 3097 3098class DNSMixin: 3099 """ 3100 DNS protocol mixin shared by UDP and TCP implementations. 3101 3102 @ivar _reactor: A L{IReactorTime} and L{IReactorUDP} provider which will 3103 be used to issue DNS queries and manage request timeouts. 3104 """ 3105 3106 id = None 3107 liveMessages = None 3108 3109 def __init__(self, controller, reactor=None): 3110 self.controller = controller 3111 self.id = random.randrange(2 ** 10, 2 ** 15) 3112 if reactor is None: 3113 from twisted.internet import reactor 3114 self._reactor = reactor 3115 3116 def pickID(self): 3117 """ 3118 Return a unique ID for queries. 3119 """ 3120 while True: 3121 id = randomSource() 3122 if id not in self.liveMessages: 3123 return id 3124 3125 def callLater(self, period, func, *args): 3126 """ 3127 Wrapper around reactor.callLater, mainly for test purpose. 3128 """ 3129 return self._reactor.callLater(period, func, *args) 3130 3131 def _query(self, queries, timeout, id, writeMessage): 3132 """ 3133 Send out a message with the given queries. 3134 3135 @type queries: L{list} of C{Query} instances 3136 @param queries: The queries to transmit 3137 3138 @type timeout: L{int} or C{float} 3139 @param timeout: How long to wait before giving up 3140 3141 @type id: L{int} 3142 @param id: Unique key for this request 3143 3144 @type writeMessage: C{callable} 3145 @param writeMessage: One-parameter callback which writes the message 3146 3147 @rtype: C{Deferred} 3148 @return: a C{Deferred} which will be fired with the result of the 3149 query, or errbacked with any errors that could happen (exceptions 3150 during writing of the query, timeout errors, ...). 3151 """ 3152 m = Message(id, recDes=1) 3153 m.queries = queries 3154 3155 try: 3156 writeMessage(m) 3157 except BaseException: 3158 return defer.fail() 3159 3160 resultDeferred = defer.Deferred() 3161 cancelCall = self.callLater(timeout, self._clearFailed, resultDeferred, id) 3162 self.liveMessages[id] = (resultDeferred, cancelCall) 3163 3164 return resultDeferred 3165 3166 def _clearFailed(self, deferred, id): 3167 """ 3168 Clean the Deferred after a timeout. 3169 """ 3170 try: 3171 del self.liveMessages[id] 3172 except KeyError: 3173 pass 3174 deferred.errback(failure.Failure(DNSQueryTimeoutError(id))) 3175 3176 3177class DNSDatagramProtocol(DNSMixin, protocol.DatagramProtocol): 3178 """ 3179 DNS protocol over UDP. 3180 """ 3181 3182 resends = None 3183 3184 def stopProtocol(self): 3185 """ 3186 Stop protocol: reset state variables. 3187 """ 3188 self.liveMessages = {} 3189 self.resends = {} 3190 self.transport = None 3191 3192 def startProtocol(self): 3193 """ 3194 Upon start, reset internal state. 3195 """ 3196 self.liveMessages = {} 3197 self.resends = {} 3198 3199 def writeMessage(self, message, address): 3200 """ 3201 Send a message holding DNS queries. 3202 3203 @type message: L{Message} 3204 """ 3205 self.transport.write(message.toStr(), address) 3206 3207 def startListening(self): 3208 self._reactor.listenUDP(0, self, maxPacketSize=512) 3209 3210 def datagramReceived(self, data, addr): 3211 """ 3212 Read a datagram, extract the message in it and trigger the associated 3213 Deferred. 3214 """ 3215 m = Message() 3216 try: 3217 m.fromStr(data) 3218 except EOFError: 3219 log.msg("Truncated packet (%d bytes) from %s" % (len(data), addr)) 3220 return 3221 except BaseException: 3222 # Nothing should trigger this, but since we're potentially 3223 # invoking a lot of different decoding methods, we might as well 3224 # be extra cautious. Anything that triggers this is itself 3225 # buggy. 3226 log.err(failure.Failure(), "Unexpected decoding error") 3227 return 3228 3229 if m.id in self.liveMessages: 3230 d, canceller = self.liveMessages[m.id] 3231 del self.liveMessages[m.id] 3232 canceller.cancel() 3233 # XXX we shouldn't need this hack of catching exception on callback() 3234 try: 3235 d.callback(m) 3236 except BaseException: 3237 log.err() 3238 else: 3239 if m.id not in self.resends: 3240 self.controller.messageReceived(m, self, addr) 3241 3242 def removeResend(self, id): 3243 """ 3244 Mark message ID as no longer having duplication suppression. 3245 """ 3246 try: 3247 del self.resends[id] 3248 except KeyError: 3249 pass 3250 3251 def query(self, address, queries, timeout=10, id=None): 3252 """ 3253 Send out a message with the given queries. 3254 3255 @type address: L{tuple} of L{str} and L{int} 3256 @param address: The address to which to send the query 3257 3258 @type queries: L{list} of C{Query} instances 3259 @param queries: The queries to transmit 3260 3261 @rtype: C{Deferred} 3262 """ 3263 if not self.transport: 3264 # XXX transport might not get created automatically, use callLater? 3265 try: 3266 self.startListening() 3267 except CannotListenError: 3268 return defer.fail() 3269 3270 if id is None: 3271 id = self.pickID() 3272 else: 3273 self.resends[id] = 1 3274 3275 def writeMessage(m): 3276 self.writeMessage(m, address) 3277 3278 return self._query(queries, timeout, id, writeMessage) 3279 3280 3281class DNSProtocol(DNSMixin, protocol.Protocol): 3282 """ 3283 DNS protocol over TCP. 3284 """ 3285 3286 length = None 3287 buffer = b"" 3288 3289 def writeMessage(self, message): 3290 """ 3291 Send a message holding DNS queries. 3292 3293 @type message: L{Message} 3294 """ 3295 s = message.toStr() 3296 self.transport.write(struct.pack("!H", len(s)) + s) 3297 3298 def connectionMade(self): 3299 """ 3300 Connection is made: reset internal state, and notify the controller. 3301 """ 3302 self.liveMessages = {} 3303 self.controller.connectionMade(self) 3304 3305 def connectionLost(self, reason): 3306 """ 3307 Notify the controller that this protocol is no longer 3308 connected. 3309 """ 3310 self.controller.connectionLost(self) 3311 3312 def dataReceived(self, data): 3313 self.buffer += data 3314 3315 while self.buffer: 3316 if self.length is None and len(self.buffer) >= 2: 3317 self.length = struct.unpack("!H", self.buffer[:2])[0] 3318 self.buffer = self.buffer[2:] 3319 3320 if len(self.buffer) >= self.length: 3321 myChunk = self.buffer[: self.length] 3322 m = Message() 3323 m.fromStr(myChunk) 3324 3325 try: 3326 d, canceller = self.liveMessages[m.id] 3327 except KeyError: 3328 self.controller.messageReceived(m, self) 3329 else: 3330 del self.liveMessages[m.id] 3331 canceller.cancel() 3332 # XXX we shouldn't need this hack 3333 try: 3334 d.callback(m) 3335 except BaseException: 3336 log.err() 3337 3338 self.buffer = self.buffer[self.length :] 3339 self.length = None 3340 else: 3341 break 3342 3343 def query(self, queries, timeout=60): 3344 """ 3345 Send out a message with the given queries. 3346 3347 @type queries: L{list} of C{Query} instances 3348 @param queries: The queries to transmit 3349 3350 @rtype: C{Deferred} 3351 """ 3352 id = self.pickID() 3353 return self._query(queries, timeout, id, self.writeMessage) 3354