Package dns :: Module message
[hide private]
[frames] | no frames]

Source Code for Module dns.message

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