1# Copyright (C) 2001-2017 Nominum, Inc. 2# 3# Permission to use, copy, modify, and distribute this software and its 4# documentation for any purpose with or without fee is hereby granted, 5# provided that the above copyright notice and this permission notice 6# appear in all copies. 7# 8# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES 9# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR 11# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 14# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 16"""DNS Messages""" 17 18from __future__ import absolute_import 19 20from io import StringIO 21import struct 22import time 23 24# import dns.edns 25import dns.exception 26import dns.flags 27import dns.name 28import dns.opcode 29# import dns.entropy 30# import dns.rcode 31import dns.rdata 32import dns.rdataclass 33import dns.rdatatype 34import dns.rrset 35# import dns.renderer 36import dns.tsig 37import dns.wiredata 38 39from ._compat import long, xrange, string_types 40 41 42class ShortHeader(dns.exception.FormError): 43 """The DNS packet passed to from_wire() is too short.""" 44 45 46class TrailingJunk(dns.exception.FormError): 47 """The DNS packet passed to from_wire() has extra junk at the end of it.""" 48 49 50class UnknownHeaderField(dns.exception.DNSException): 51 """The header field name was not recognized when converting from text 52 into a message.""" 53 54 55class BadEDNS(dns.exception.FormError): 56 """An OPT record occurred somewhere other than the start of 57 the additional data section.""" 58 59 60class BadTSIG(dns.exception.FormError): 61 """A TSIG record occurred somewhere other than the end of 62 the additional data section.""" 63 64 65class UnknownTSIGKey(dns.exception.DNSException): 66 """A TSIG with an unknown key was received.""" 67 68 69#: The question section number 70QUESTION = 0 71 72#: The answer section number 73ANSWER = 1 74 75#: The authority section number 76AUTHORITY = 2 77 78#: The additional section number 79ADDITIONAL = 3 80 81class Message(object): 82 """A DNS message.""" 83 84 def __init__(self, id=None): 85 if id is None: 86 self.id = -1 # dns.entropy.random_16() XXX 87 else: 88 self.id = id 89 self.flags = 0 90 self.question = [] 91 self.answer = [] 92 self.authority = [] 93 self.additional = [] 94 self.edns = -1 95 self.ednsflags = 0 96 self.payload = 0 97 self.options = [] 98 self.request_payload = 0 99 self.keyring = None 100 self.keyname = None 101 self.keyalgorithm = dns.tsig.default_algorithm 102 self.request_mac = b'' 103 self.other_data = b'' 104 self.tsig_error = 0 105 self.fudge = 300 106 self.original_id = self.id 107 self.mac = b'' 108 self.xfr = False 109 self.origin = None 110 self.tsig_ctx = None 111 self.had_tsig = False 112 self.multi = False 113 self.first = True 114 self.index = {} 115 116 def __repr__(self): 117 return '<DNS message, ID ' + repr(self.id) + '>' 118 119 def __str__(self): 120 return self.to_text() 121 122 def to_text(self, origin=None, relativize=True, **kw): 123 """Convert the message to text. 124 125 The *origin*, *relativize*, and any other keyword 126 arguments are passed to the RRset ``to_wire()`` method. 127 128 Returns a ``text``. 129 """ 130 131 # return ";)" 132 133 s = StringIO() 134 s.write(u'id %d\n' % self.id) 135 s.write(u'opcode %s\n' % 136 dns.opcode.to_text(dns.opcode.from_flags(self.flags))) 137 rc = dns.rcode.from_flags(self.flags, self.ednsflags) 138 s.write(u'rcode %s\n' % dns.rcode.to_text(rc)) 139 s.write(u'flags %s\n' % dns.flags.to_text(self.flags)) 140 if self.edns >= 0: 141 s.write(u'edns %s\n' % self.edns) 142 if self.ednsflags != 0: 143 s.write(u'eflags %s\n' % 144 dns.flags.edns_to_text(self.ednsflags)) 145 s.write(u'payload %d\n' % self.payload) 146 for opt in self.options: 147 s.write(u'option %s\n' % opt.to_text()) 148 is_update = dns.opcode.is_update(self.flags) 149 if is_update: 150 s.write(u';ZONE\n') 151 else: 152 s.write(u';QUESTION\n') 153 """ 154 for rrset in self.question: 155 s.write(rrset.to_text(origin, relativize, **kw)) 156 s.write(u'\n') 157 if is_update: 158 s.write(u';PREREQ\n') 159 else: 160 s.write(u';ANSWER\n') 161 for rrset in self.answer: 162 s.write(rrset.to_text(origin, relativize, **kw)) 163 s.write(u'\n') 164 if is_update: 165 s.write(u';UPDATE\n') 166 else: 167 s.write(u';AUTHORITY\n') 168 for rrset in self.authority: 169 s.write(rrset.to_text(origin, relativize, **kw)) 170 s.write(u'\n') 171 s.write(u';ADDITIONAL\n') 172 for rrset in self.additional: 173 s.write(rrset.to_text(origin, relativize, **kw)) 174 s.write(u'\n') 175 """ 176 # 177 # We strip off the final \n so the caller can print the result without 178 # doing weird things to get around eccentricities in Python print 179 # formatting 180 # 181 return s.getvalue()[:-1] 182 183 def __eq__(self, other): 184 """Two messages are equal if they have the same content in the 185 header, question, answer, and authority sections. 186 187 Returns a ``bool``. 188 """ 189 190 if not isinstance(other, Message): 191 return False 192 if self.id != other.id: 193 return False 194 if self.flags != other.flags: 195 return False 196 for n in self.question: 197 if n not in other.question: 198 return False 199 for n in other.question: 200 if n not in self.question: 201 return False 202 for n in self.answer: 203 if n not in other.answer: 204 return False 205 for n in other.answer: 206 if n not in self.answer: 207 return False 208 for n in self.authority: 209 if n not in other.authority: 210 return False 211 for n in other.authority: 212 if n not in self.authority: 213 return False 214 return True 215 216 def __ne__(self, other): 217 return not self.__eq__(other) 218 219 def is_response(self, other): 220 """Is this message a response to *other*? 221 222 Returns a ``bool``. 223 """ 224 225 if other.flags & dns.flags.QR == 0 or \ 226 self.id != other.id or \ 227 dns.opcode.from_flags(self.flags) != \ 228 dns.opcode.from_flags(other.flags): 229 return False 230 if dns.rcode.from_flags(other.flags, other.ednsflags) != \ 231 dns.rcode.NOERROR: 232 return True 233 if dns.opcode.is_update(self.flags): 234 return True 235 for n in self.question: 236 if n not in other.question: 237 return False 238 for n in other.question: 239 if n not in self.question: 240 return False 241 return True 242 243 def section_number(self, section): 244 """Return the "section number" of the specified section for use 245 in indexing. The question section is 0, the answer section is 1, 246 the authority section is 2, and the additional section is 3. 247 248 *section* is one of the section attributes of this message. 249 250 Raises ``ValueError`` if the section isn't known. 251 252 Returns an ``int``. 253 """ 254 255 if section is self.question: 256 return QUESTION 257 elif section is self.answer: 258 return ANSWER 259 elif section is self.authority: 260 return AUTHORITY 261 elif section is self.additional: 262 return ADDITIONAL 263 else: 264 raise ValueError('unknown section') 265 266 def section_from_number(self, number): 267 """Return the "section number" of the specified section for use 268 in indexing. The question section is 0, the answer section is 1, 269 the authority section is 2, and the additional section is 3. 270 271 *section* is one of the section attributes of this message. 272 273 Raises ``ValueError`` if the section isn't known. 274 275 Returns an ``int``. 276 """ 277 278 if number == QUESTION: 279 return self.question 280 elif number == ANSWER: 281 return self.answer 282 elif number == AUTHORITY: 283 return self.authority 284 elif number == ADDITIONAL: 285 return self.additional 286 else: 287 raise ValueError('unknown section') 288 289 def find_rrset(self, section, name, rdclass, rdtype, 290 covers=dns.rdatatype.NONE, deleting=None, create=False, 291 force_unique=False): 292 """Find the RRset with the given attributes in the specified section. 293 294 *section*, an ``int`` section number, or one of the section 295 attributes of this message. This specifies the 296 the section of the message to search. For example:: 297 298 my_message.find_rrset(my_message.answer, name, rdclass, rdtype) 299 my_message.find_rrset(dns.message.ANSWER, name, rdclass, rdtype) 300 301 *name*, a ``dns.name.Name``, the name of the RRset. 302 303 *rdclass*, an ``int``, the class of the RRset. 304 305 *rdtype*, an ``int``, the type of the RRset. 306 307 *covers*, an ``int`` or ``None``, the covers value of the RRset. 308 The default is ``None``. 309 310 *deleting*, an ``int`` or ``None``, the deleting value of the RRset. 311 The default is ``None``. 312 313 *create*, a ``bool``. If ``True``, create the RRset if it is not found. 314 The created RRset is appended to *section*. 315 316 *force_unique*, a ``bool``. If ``True`` and *create* is also ``True``, 317 create a new RRset regardless of whether a matching RRset exists 318 already. The default is ``False``. This is useful when creating 319 DDNS Update messages, as order matters for them. 320 321 Raises ``KeyError`` if the RRset was not found and create was 322 ``False``. 323 324 Returns a ``dns.rrset.RRset object``. 325 """ 326 327 if isinstance(section, int): 328 section_number = section 329 section = self.section_from_number(section_number) 330 else: 331 section_number = self.section_number(section) 332 key = (section_number, name, rdclass, rdtype, covers, deleting) 333 if not force_unique: 334 if self.index is not None: 335 rrset = self.index.get(key) 336 if rrset is not None: 337 return rrset 338 else: 339 for rrset in section: 340 if rrset.match(name, rdclass, rdtype, covers, deleting): 341 return rrset 342 if not create: 343 raise KeyError 344 rrset = dns.rrset.RRset(name, rdclass, rdtype, covers, deleting) 345 section.append(rrset) 346 if self.index is not None: 347 self.index[key] = rrset 348 return rrset 349 350 def get_rrset(self, section, name, rdclass, rdtype, 351 covers=dns.rdatatype.NONE, deleting=None, create=False, 352 force_unique=False): 353 """Get the RRset with the given attributes in the specified section. 354 355 If the RRset is not found, None is returned. 356 357 *section*, an ``int`` section number, or one of the section 358 attributes of this message. This specifies the 359 the section of the message to search. For example:: 360 361 my_message.get_rrset(my_message.answer, name, rdclass, rdtype) 362 my_message.get_rrset(dns.message.ANSWER, name, rdclass, rdtype) 363 364 *name*, a ``dns.name.Name``, the name of the RRset. 365 366 *rdclass*, an ``int``, the class of the RRset. 367 368 *rdtype*, an ``int``, the type of the RRset. 369 370 *covers*, an ``int`` or ``None``, the covers value of the RRset. 371 The default is ``None``. 372 373 *deleting*, an ``int`` or ``None``, the deleting value of the RRset. 374 The default is ``None``. 375 376 *create*, a ``bool``. If ``True``, create the RRset if it is not found. 377 The created RRset is appended to *section*. 378 379 *force_unique*, a ``bool``. If ``True`` and *create* is also ``True``, 380 create a new RRset regardless of whether a matching RRset exists 381 already. The default is ``False``. This is useful when creating 382 DDNS Update messages, as order matters for them. 383 384 Returns a ``dns.rrset.RRset object`` or ``None``. 385 """ 386 387 try: 388 rrset = self.find_rrset(section, name, rdclass, rdtype, covers, 389 deleting, create, force_unique) 390 except KeyError: 391 rrset = None 392 return rrset 393 394 395 def use_tsig(self, keyring, keyname=None, fudge=300, 396 original_id=None, tsig_error=0, other_data=b'', 397 algorithm=dns.tsig.default_algorithm): 398 """When sending, a TSIG signature using the specified keyring 399 and keyname should be added. 400 401 See the documentation of the Message class for a complete 402 description of the keyring dictionary. 403 404 *keyring*, a ``dict``, the TSIG keyring to use. If a 405 *keyring* is specified but a *keyname* is not, then the key 406 used will be the first key in the *keyring*. Note that the 407 order of keys in a dictionary is not defined, so applications 408 should supply a keyname when a keyring is used, unless they 409 know the keyring contains only one key. 410 411 *keyname*, a ``dns.name.Name`` or ``None``, the name of the TSIG key 412 to use; defaults to ``None``. The key must be defined in the keyring. 413 414 *fudge*, an ``int``, the TSIG time fudge. 415 416 *original_id*, an ``int``, the TSIG original id. If ``None``, 417 the message's id is used. 418 419 *tsig_error*, an ``int``, the TSIG error code. 420 421 *other_data*, a ``binary``, the TSIG other data. 422 423 *algorithm*, a ``dns.name.Name``, the TSIG algorithm to use. 424 """ 425 426 self.keyring = keyring 427 if keyname is None: 428 self.keyname = list(self.keyring.keys())[0] 429 else: 430 if isinstance(keyname, string_types): 431 keyname = dns.name.from_text(keyname) 432 self.keyname = keyname 433 self.keyalgorithm = algorithm 434 self.fudge = fudge 435 if original_id is None: 436 self.original_id = self.id 437 else: 438 self.original_id = original_id 439 self.tsig_error = tsig_error 440 self.other_data = other_data 441 442 def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None, 443 options=None): 444 """Configure EDNS behavior. 445 446 *edns*, an ``int``, is the EDNS level to use. Specifying 447 ``None``, ``False``, or ``-1`` means "do not use EDNS", and in this case 448 the other parameters are ignored. Specifying ``True`` is 449 equivalent to specifying 0, i.e. "use EDNS0". 450 451 *ednsflags*, an ``int``, the EDNS flag values. 452 453 *payload*, an ``int``, is the EDNS sender's payload field, which is the 454 maximum size of UDP datagram the sender can handle. I.e. how big 455 a response to this message can be. 456 457 *request_payload*, an ``int``, is the EDNS payload size to use when 458 sending this message. If not specified, defaults to the value of 459 *payload*. 460 461 *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS 462 options. 463 """ 464 465 if edns is None or edns is False: 466 edns = -1 467 if edns is True: 468 edns = 0 469 if request_payload is None: 470 request_payload = payload 471 if edns < 0: 472 ednsflags = 0 473 payload = 0 474 request_payload = 0 475 options = [] 476 else: 477 # make sure the EDNS version in ednsflags agrees with edns 478 ednsflags &= long(0xFF00FFFF) 479 ednsflags |= (edns << 16) 480 if options is None: 481 options = [] 482 self.edns = edns 483 self.ednsflags = ednsflags 484 self.payload = payload 485 self.options = options 486 self.request_payload = request_payload 487 488 def want_dnssec(self, wanted=True): 489 """Enable or disable 'DNSSEC desired' flag in requests. 490 491 *wanted*, a ``bool``. If ``True``, then DNSSEC data is 492 desired in the response, EDNS is enabled if required, and then 493 the DO bit is set. If ``False``, the DO bit is cleared if 494 EDNS is enabled. 495 """ 496 497 if wanted: 498 if self.edns < 0: 499 self.use_edns() 500 self.ednsflags |= dns.flags.DO 501 elif self.edns >= 0: 502 self.ednsflags &= ~dns.flags.DO 503 504 def rcode(self): 505 """Return the rcode. 506 507 Returns an ``int``. 508 """ 509 return dns.rcode.from_flags(self.flags, self.ednsflags) 510 511 def set_rcode(self, rcode): 512 """Set the rcode. 513 514 *rcode*, an ``int``, is the rcode to set. 515 """ 516 (value, evalue) = dns.rcode.to_flags(rcode) 517 self.flags &= 0xFFF0 518 self.flags |= value 519 self.ednsflags &= long(0x00FFFFFF) 520 self.ednsflags |= evalue 521 if self.ednsflags != 0 and self.edns < 0: 522 self.edns = 0 523 524 def opcode(self): 525 """Return the opcode. 526 527 Returns an ``int``. 528 """ 529 return dns.opcode.from_flags(self.flags) 530 531 def set_opcode(self, opcode): 532 """Set the opcode. 533 534 *opcode*, an ``int``, is the opcode to set. 535 """ 536 self.flags &= 0x87FF 537 self.flags |= dns.opcode.to_flags(opcode) 538 539 540class _WireReader(object): 541 542 """Wire format reader. 543 544 wire: a binary, is the wire-format message. 545 message: The message object being built 546 current: When building a message object from wire format, this 547 variable contains the offset from the beginning of wire of the next octet 548 to be read. 549 updating: Is the message a dynamic update? 550 one_rr_per_rrset: Put each RR into its own RRset? 551 ignore_trailing: Ignore trailing junk at end of request? 552 zone_rdclass: The class of the zone in messages which are 553 DNS dynamic updates. 554 """ 555 556 def __init__(self, wire, message, question_only=False, 557 one_rr_per_rrset=False, ignore_trailing=False, pout=False): 558 self.wire = dns.wiredata.maybe_wrap(wire) 559 self.message = message 560 self.current = 0 561 self.updating = False 562 self.zone_rdclass = dns.rdataclass.IN 563 self.question_only = question_only 564 self.one_rr_per_rrset = one_rr_per_rrset 565 self.ignore_trailing = ignore_trailing 566 self.pout = pout 567 568 def _get_question(self, qcount): 569 """Read the next *qcount* records from the wire data and add them to 570 the question section. 571 """ 572 573 if self.updating and qcount > 1: 574 raise dns.exception.FormError 575 576 for i in xrange(0, qcount): 577 (qname, used) = dns.name.from_wire(self.wire, self.current) 578 if self.message.origin is not None: 579 qname = qname.relativize(self.message.origin) 580 self.current = self.current + used 581 (rdtype, rdclass) = \ 582 struct.unpack('!HH', 583 self.wire[self.current:self.current + 4]) 584 self.current = self.current + 4 585 self.message.find_rrset(self.message.question, qname, 586 rdclass, rdtype, create=True, 587 force_unique=True) 588 if self.updating: 589 self.zone_rdclass = rdclass 590 591 def _get_section(self, section, count): 592 """Read the next I{count} records from the wire data and add them to 593 the specified section. 594 595 section: the section of the message to which to add records 596 count: the number of records to read 597 """ 598 599 if self.updating or self.one_rr_per_rrset: 600 force_unique = True 601 else: 602 force_unique = False 603 seen_opt = False 604 for i in xrange(0, count): 605 rr_start = self.current 606 (name, used) = dns.name.from_wire(self.wire, self.current) 607 absolute_name = name 608 if self.message.origin is not None: 609 name = name.relativize(self.message.origin) 610 self.current = self.current + used 611 (rdtype, rdclass, ttl, rdlen) = \ 612 struct.unpack('!HHIH', 613 self.wire[self.current:self.current + 10]) 614 self.current = self.current + 10 615 if rdtype == dns.rdatatype.OPT: 616 if section is not self.message.additional or seen_opt: 617 raise BadEDNS 618 self.message.payload = rdclass 619 self.message.ednsflags = ttl 620 self.message.edns = (ttl & 0xff0000) >> 16 621 self.message.options = [] 622 current = self.current 623 optslen = rdlen 624 while optslen > 0: 625 (otype, olen) = \ 626 struct.unpack('!HH', 627 self.wire[current:current + 4]) 628 current = current + 4 629 opt = dns.edns.option_from_wire( 630 otype, self.wire, current, olen) 631 self.message.options.append(opt) 632 current = current + olen 633 optslen = optslen - 4 - olen 634 seen_opt = True 635 elif rdtype == dns.rdatatype.TSIG: 636 if not (section is self.message.additional and 637 i == (count - 1)): 638 raise BadTSIG 639 if self.message.keyring is None: 640 raise UnknownTSIGKey('got signed message without keyring') 641 secret = self.message.keyring.get(absolute_name) 642 if secret is None: 643 raise UnknownTSIGKey("key '%s' unknown" % name) 644 self.message.keyname = absolute_name 645 (self.message.keyalgorithm, self.message.mac) = \ 646 dns.tsig.get_algorithm_and_mac(self.wire, self.current, 647 rdlen) 648 self.message.tsig_ctx = \ 649 dns.tsig.validate(self.wire, 650 absolute_name, 651 secret, 652 int(time.time()), 653 self.message.request_mac, 654 rr_start, 655 self.current, 656 rdlen, 657 self.message.tsig_ctx, 658 self.message.multi, 659 self.message.first, 660 self.pout) 661 self.message.had_tsig = True 662 else: 663 if ttl < 0: 664 ttl = 0 665 if self.updating and \ 666 (rdclass == dns.rdataclass.ANY or 667 rdclass == dns.rdataclass.NONE): 668 deleting = rdclass 669 rdclass = self.zone_rdclass 670 else: 671 deleting = None 672 if deleting == dns.rdataclass.ANY or \ 673 (deleting == dns.rdataclass.NONE and 674 section is self.message.answer): 675 covers = dns.rdatatype.NONE 676 rd = None 677 else: 678 rd = dns.rdata.from_wire(rdclass, rdtype, self.wire, 679 self.current, rdlen, 680 self.message.origin) 681 covers = rd.covers() 682 if self.message.xfr and rdtype == dns.rdatatype.SOA: 683 force_unique = True 684 rrset = self.message.find_rrset(section, name, 685 rdclass, rdtype, covers, 686 deleting, True, force_unique) 687 if rd is not None: 688 rrset.add(rd, ttl) 689 self.current = self.current + rdlen 690 691 def read(self): 692 """Read a wire format DNS message and build a dns.message.Message 693 object.""" 694 695 l = len(self.wire) 696 if l < 12: 697 raise ShortHeader 698 (self.message.id, self.message.flags, qcount, ancount, 699 aucount, adcount) = struct.unpack('!HHHHHH', self.wire[:12]) 700 self.current = 12 701 if dns.opcode.is_update(self.message.flags): 702 self.updating = True 703 self._get_question(qcount) 704 if self.question_only: 705 return 706 self._get_section(self.message.answer, ancount) 707 self._get_section(self.message.authority, aucount) 708 self._get_section(self.message.additional, adcount) 709 if not self.ignore_trailing and self.current != l: 710 raise TrailingJunk 711 if self.message.multi and self.message.tsig_ctx and \ 712 not self.message.had_tsig: 713 self.message.tsig_ctx.update(self.wire) 714 715 716def from_wire(wire, keyring=None, request_mac=b'', xfr=False, origin=None, 717 tsig_ctx=None, multi=False, first=True, 718 question_only=False, one_rr_per_rrset=False, 719 ignore_trailing=False, pout=False): 720 """Convert a DNS wire format message into a message 721 object. 722 723 *keyring*, a ``dict``, the keyring to use if the message is signed. 724 725 *request_mac*, a ``binary``. If the message is a response to a 726 TSIG-signed request, *request_mac* should be set to the MAC of 727 that request. 728 729 *xfr*, a ``bool``, should be set to ``True`` if this message is part of 730 a zone transfer. 731 732 *origin*, a ``dns.name.Name`` or ``None``. If the message is part 733 of a zone transfer, *origin* should be the origin name of the 734 zone. 735 736 *tsig_ctx*, a ``hmac.HMAC`` objext, the ongoing TSIG context, used 737 when validating zone transfers. 738 739 *multi*, a ``bool``, should be set to ``True`` if this message 740 part of a multiple message sequence. 741 742 *first*, a ``bool``, should be set to ``True`` if this message is 743 stand-alone, or the first message in a multi-message sequence. 744 745 *question_only*, a ``bool``. If ``True``, read only up to 746 the end of the question section. 747 748 *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its 749 own RRset. 750 751 *ignore_trailing*, a ``bool``. If ``True``, ignore trailing 752 junk at end of the message. 753 754 Raises ``dns.message.ShortHeader`` if the message is less than 12 octets 755 long. 756 757 Raises ``dns.messaage.TrailingJunk`` if there were octets in the message 758 past the end of the proper DNS message, and *ignore_trailing* is ``False``. 759 760 Raises ``dns.message.BadEDNS`` if an OPT record was in the 761 wrong section, or occurred more than once. 762 763 Raises ``dns.message.BadTSIG`` if a TSIG record was not the last 764 record of the additional data section. 765 766 Returns a ``dns.message.Message``. 767 """ 768 769 m = Message(id=0) 770 m.keyring = keyring 771 m.request_mac = request_mac 772 m.xfr = xfr 773 m.origin = origin 774 m.tsig_ctx = tsig_ctx 775 m.multi = multi 776 m.first = first 777 778 reader = _WireReader(wire, m, question_only, one_rr_per_rrset, 779 ignore_trailing, pout) 780 reader.read() 781 782 return m 783 784 785class _TextReader(object): 786 787 """Text format reader. 788 789 tok: the tokenizer. 790 message: The message object being built. 791 updating: Is the message a dynamic update? 792 zone_rdclass: The class of the zone in messages which are 793 DNS dynamic updates. 794 last_name: The most recently read name when building a message object. 795 """ 796 797 def __init__(self, text, message): 798 self.message = message 799 self.tok = dns.tokenizer.Tokenizer(text) 800 self.last_name = None 801 self.zone_rdclass = dns.rdataclass.IN 802 self.updating = False 803 804 def _header_line(self, section): 805 """Process one line from the text format header section.""" 806 807 token = self.tok.get() 808 what = token.value 809 if what == 'id': 810 self.message.id = self.tok.get_int() 811 elif what == 'flags': 812 while True: 813 token = self.tok.get() 814 if not token.is_identifier(): 815 self.tok.unget(token) 816 break 817 self.message.flags = self.message.flags | \ 818 dns.flags.from_text(token.value) 819 if dns.opcode.is_update(self.message.flags): 820 self.updating = True 821 elif what == 'edns': 822 self.message.edns = self.tok.get_int() 823 self.message.ednsflags = self.message.ednsflags | \ 824 (self.message.edns << 16) 825 elif what == 'eflags': 826 if self.message.edns < 0: 827 self.message.edns = 0 828 while True: 829 token = self.tok.get() 830 if not token.is_identifier(): 831 self.tok.unget(token) 832 break 833 self.message.ednsflags = self.message.ednsflags | \ 834 dns.flags.edns_from_text(token.value) 835 elif what == 'payload': 836 self.message.payload = self.tok.get_int() 837 if self.message.edns < 0: 838 self.message.edns = 0 839 elif what == 'opcode': 840 text = self.tok.get_string() 841 self.message.flags = self.message.flags | \ 842 dns.opcode.to_flags(dns.opcode.from_text(text)) 843 elif what == 'rcode': 844 text = self.tok.get_string() 845 self.message.set_rcode(dns.rcode.from_text(text)) 846 else: 847 raise UnknownHeaderField 848 self.tok.get_eol() 849 850 def _question_line(self, section): 851 """Process one line from the text format question section.""" 852 853 token = self.tok.get(want_leading=True) 854 if not token.is_whitespace(): 855 self.last_name = dns.name.from_text(token.value, None) 856 name = self.last_name 857 token = self.tok.get() 858 if not token.is_identifier(): 859 raise dns.exception.SyntaxError 860 # Class 861 try: 862 rdclass = dns.rdataclass.from_text(token.value) 863 token = self.tok.get() 864 if not token.is_identifier(): 865 raise dns.exception.SyntaxError 866 except dns.exception.SyntaxError: 867 raise dns.exception.SyntaxError 868 except Exception: 869 rdclass = dns.rdataclass.IN 870 # Type 871 rdtype = dns.rdatatype.from_text(token.value) 872 self.message.find_rrset(self.message.question, name, 873 rdclass, rdtype, create=True, 874 force_unique=True) 875 if self.updating: 876 self.zone_rdclass = rdclass 877 self.tok.get_eol() 878 879 def _rr_line(self, section): 880 """Process one line from the text format answer, authority, or 881 additional data sections. 882 """ 883 884 deleting = None 885 # Name 886 token = self.tok.get(want_leading=True) 887 if not token.is_whitespace(): 888 self.last_name = dns.name.from_text(token.value, None) 889 name = self.last_name 890 token = self.tok.get() 891 if not token.is_identifier(): 892 raise dns.exception.SyntaxError 893 # TTL 894 try: 895 ttl = int(token.value, 0) 896 token = self.tok.get() 897 if not token.is_identifier(): 898 raise dns.exception.SyntaxError 899 except dns.exception.SyntaxError: 900 raise dns.exception.SyntaxError 901 except Exception: 902 ttl = 0 903 # Class 904 try: 905 rdclass = dns.rdataclass.from_text(token.value) 906 token = self.tok.get() 907 if not token.is_identifier(): 908 raise dns.exception.SyntaxError 909 if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE: 910 deleting = rdclass 911 rdclass = self.zone_rdclass 912 except dns.exception.SyntaxError: 913 raise dns.exception.SyntaxError 914 except Exception: 915 rdclass = dns.rdataclass.IN 916 # Type 917 rdtype = dns.rdatatype.from_text(token.value) 918 token = self.tok.get() 919 if not token.is_eol_or_eof(): 920 self.tok.unget(token) 921 rd = dns.rdata.from_text(rdclass, rdtype, self.tok, None) 922 covers = rd.covers() 923 else: 924 rd = None 925 covers = dns.rdatatype.NONE 926 rrset = self.message.find_rrset(section, name, 927 rdclass, rdtype, covers, 928 deleting, True, self.updating) 929 if rd is not None: 930 rrset.add(rd, ttl) 931 932 def read(self): 933 """Read a text format DNS message and build a dns.message.Message 934 object.""" 935 936 line_method = self._header_line 937 section = None 938 while 1: 939 token = self.tok.get(True, True) 940 if token.is_eol_or_eof(): 941 break 942 if token.is_comment(): 943 u = token.value.upper() 944 if u == 'HEADER': 945 line_method = self._header_line 946 elif u == 'QUESTION' or u == 'ZONE': 947 line_method = self._question_line 948 section = self.message.question 949 elif u == 'ANSWER' or u == 'PREREQ': 950 line_method = self._rr_line 951 section = self.message.answer 952 elif u == 'AUTHORITY' or u == 'UPDATE': 953 line_method = self._rr_line 954 section = self.message.authority 955 elif u == 'ADDITIONAL': 956 line_method = self._rr_line 957 section = self.message.additional 958 self.tok.get_eol() 959 continue 960 self.tok.unget(token) 961 line_method(section) 962 963 964def from_text(text): 965 """Convert the text format message into a message object. 966 967 *text*, a ``text``, the text format message. 968 969 Raises ``dns.message.UnknownHeaderField`` if a header is unknown. 970 971 Raises ``dns.exception.SyntaxError`` if the text is badly formed. 972 973 Returns a ``dns.message.Message object`` 974 """ 975 976 # 'text' can also be a file, but we don't publish that fact 977 # since it's an implementation detail. The official file 978 # interface is from_file(). 979 980 m = Message() 981 982 reader = _TextReader(text, m) 983 reader.read() 984 985 return m 986 987 988def from_file(f): 989 """Read the next text format message from the specified file. 990 991 *f*, a ``file`` or ``text``. If *f* is text, it is treated as the 992 pathname of a file to open. 993 994 Raises ``dns.message.UnknownHeaderField`` if a header is unknown. 995 996 Raises ``dns.exception.SyntaxError`` if the text is badly formed. 997 998 Returns a ``dns.message.Message object`` 999 """ 1000 1001 str_type = string_types 1002 opts = 'rU' 1003 1004 if isinstance(f, str_type): 1005 f = open(f, opts) 1006 want_close = True 1007 else: 1008 want_close = False 1009 1010 try: 1011 m = from_text(f) 1012 finally: 1013 if want_close: 1014 f.close() 1015 return m 1016