1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """DNS Messages"""
17
18 import io
19 import random
20 import struct
21 import sys
22 import time
23
24 import dns.edns
25 import dns.exception
26 import dns.flags
27 import dns.name
28 import dns.opcode
29 import dns.entropy
30 import dns.rcode
31 import dns.rdata
32 import dns.rdataclass
33 import dns.rdatatype
34 import dns.rrset
35 import dns.renderer
36 import dns.tsig
37 import dns.wiredata
38
40 """Raised if the DNS packet passed to from_wire() is too short."""
41 pass
42
44 """Raised if the DNS packet passed to from_wire() has extra junk
45 at the end of it."""
46 pass
47
49 """Raised if a header field name is not recognized when converting from
50 text into a message."""
51 pass
52
53 -class BadEDNS(dns.exception.FormError):
54 """Raised if an OPT record occurs somewhere other than the start of
55 the additional data section."""
56 pass
57
58 -class BadTSIG(dns.exception.FormError):
59 """Raised if a TSIG record occurs somewhere other than the end of
60 the additional data section."""
61 pass
62
64 """Raised if we got a TSIG but don't know the key."""
65 pass
66
68 """A DNS message.
69
70 @ivar id: The query id; the default is a randomly chosen id.
71 @type id: int
72 @ivar flags: The DNS flags of the message. @see: RFC 1035 for an
73 explanation of these flags.
74 @type flags: int
75 @ivar question: The question section.
76 @type question: list of dns.rrset.RRset objects
77 @ivar answer: The answer section.
78 @type answer: list of dns.rrset.RRset objects
79 @ivar authority: The authority section.
80 @type authority: list of dns.rrset.RRset objects
81 @ivar additional: The additional data section.
82 @type additional: list of dns.rrset.RRset objects
83 @ivar edns: The EDNS level to use. The default is -1, no Edns.
84 @type edns: int
85 @ivar ednsflags: The EDNS flags
86 @type ednsflags: long
87 @ivar payload: The EDNS payload size. The default is 0.
88 @type payload: int
89 @ivar options: The EDNS options
90 @type options: list of dns.edns.Option objects
91 @ivar request_payload: The associated request's EDNS payload size.
92 @type request_payload: int
93 @ivar keyring: The TSIG keyring to use. The default is None.
94 @type keyring: dict
95 @ivar keyname: The TSIG keyname to use. The default is None.
96 @type keyname: dns.name.Name object
97 @ivar keyalgorithm: The TSIG algorithm to use; defaults to
98 dns.tsig.default_algorithm. Constants for TSIG algorithms are defined
99 in dns.tsig, and the currently implemented algorithms are
100 HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and
101 HMAC_SHA512.
102 @type keyalgorithm: string
103 @ivar request_mac: The TSIG MAC of the request message associated with
104 this message; used when validating TSIG signatures. @see: RFC 2845 for
105 more information on TSIG fields.
106 @type request_mac: string
107 @ivar fudge: TSIG time fudge; default is 300 seconds.
108 @type fudge: int
109 @ivar original_id: TSIG original id; defaults to the message's id
110 @type original_id: int
111 @ivar tsig_error: TSIG error code; default is 0.
112 @type tsig_error: int
113 @ivar other_data: TSIG other data.
114 @type other_data: bytes
115 @ivar mac: The TSIG MAC for this message.
116 @type mac: bytes
117 @ivar xfr: Is the message being used to contain the results of a DNS
118 zone transfer? The default is False.
119 @type xfr: bool
120 @ivar origin: The origin of the zone in messages which are used for
121 zone transfers or for DNS dynamic updates. The default is None.
122 @type origin: dns.name.Name object
123 @ivar tsig_ctx: The TSIG signature context associated with this
124 message. The default is None.
125 @type tsig_ctx: hmac.HMAC object
126 @ivar had_tsig: Did the message decoded from wire format have a TSIG
127 signature?
128 @type had_tsig: bool
129 @ivar multi: Is this message part of a multi-message sequence? The
130 default is false. This variable is used when validating TSIG signatures
131 on messages which are part of a zone transfer.
132 @type multi: bool
133 @ivar first: Is this message standalone, or the first of a multi
134 message sequence? This variable is used when validating TSIG signatures
135 on messages which are part of a zone transfer.
136 @type first: bool
137 @ivar index: An index of rrsets in the message. The index key is
138 (section, name, rdclass, rdtype, covers, deleting). Indexing can be
139 disabled by setting the index to None.
140 @type index: dict
141 """
142
144 if id is None:
145 self.id = dns.entropy.random_16()
146 else:
147 self.id = id
148 self.flags = 0
149 self.question = []
150 self.answer = []
151 self.authority = []
152 self.additional = []
153 self.edns = -1
154 self.ednsflags = 0
155 self.payload = 0
156 self.options = []
157 self.request_payload = 0
158 self.keyring = None
159 self.keyname = None
160 self.keyalgorithm = dns.tsig.default_algorithm
161 self.request_mac = b''
162 self.other_data = b''
163 self.tsig_error = 0
164 self.fudge = 300
165 self.original_id = self.id
166 self.mac = b''
167 self.xfr = False
168 self.origin = None
169 self.tsig_ctx = None
170 self.had_tsig = False
171 self.multi = False
172 self.first = True
173 self.index = {}
174
176 return '<DNS message, ID ' + str(self.id) + '>'
177
180
181 - def to_text(self, origin=None, relativize=True, **kw):
182 """Convert the message to text.
183
184 The I{origin}, I{relativize}, and any other keyword
185 arguments are passed to the rrset to_wire() method.
186
187 @rtype: string
188 """
189
190 s = io.StringIO()
191 print('id %d' % self.id, file=s)
192 print('opcode %s' % \
193 dns.opcode.to_text(dns.opcode.from_flags(self.flags)),
194 file=s)
195 rc = dns.rcode.from_flags(self.flags, self.ednsflags)
196 print('rcode %s' % dns.rcode.to_text(rc), file=s)
197 print('flags %s' % dns.flags.to_text(self.flags), file=s)
198 if self.edns >= 0:
199 print('edns %s' % self.edns, file=s)
200 if self.ednsflags != 0:
201 print('eflags %s' % \
202 dns.flags.edns_to_text(self.ednsflags), file=s)
203 print('payload', self.payload, file=s)
204 is_update = dns.opcode.is_update(self.flags)
205 if is_update:
206 print(';ZONE', file=s)
207 else:
208 print(';QUESTION', file=s)
209 for rrset in self.question:
210 print(rrset.to_text(origin, relativize, **kw), file=s)
211 if is_update:
212 print(';PREREQ', file=s)
213 else:
214 print(';ANSWER', file=s)
215 for rrset in self.answer:
216 print(rrset.to_text(origin, relativize, **kw), file=s)
217 if is_update:
218 print(';UPDATE', file=s)
219 else:
220 print(';AUTHORITY', file=s)
221 for rrset in self.authority:
222 print(rrset.to_text(origin, relativize, **kw), file=s)
223 print(';ADDITIONAL', file=s)
224 for rrset in self.additional:
225 print(rrset.to_text(origin, relativize, **kw), file=s)
226
227
228
229
230
231 return s.getvalue()[:-1]
232
234 """Two messages are equal if they have the same content in the
235 header, question, answer, and authority sections.
236 @rtype: bool"""
237 if not isinstance(other, Message):
238 return False
239 if self.id != other.id:
240 return False
241 if self.flags != other.flags:
242 return False
243 for n in self.question:
244 if n not in other.question:
245 return False
246 for n in other.question:
247 if n not in self.question:
248 return False
249 for n in self.answer:
250 if n not in other.answer:
251 return False
252 for n in other.answer:
253 if n not in self.answer:
254 return False
255 for n in self.authority:
256 if n not in other.authority:
257 return False
258 for n in other.authority:
259 if n not in self.authority:
260 return False
261 return True
262
264 """Are two messages not equal?
265 @rtype: bool"""
266 return not self.__eq__(other)
267
288
290 if section is self.question:
291 return 0
292 elif section is self.answer:
293 return 1
294 elif section is self.authority:
295 return 2
296 elif section is self.additional:
297 return 3
298 else:
299 raise ValueError('unknown section')
300
301 - def find_rrset(self, section, name, rdclass, rdtype,
302 covers=dns.rdatatype.NONE, deleting=None, create=False,
303 force_unique=False):
304 """Find the RRset with the given attributes in the specified section.
305
306 @param section: the section of the message to look in, e.g.
307 self.answer.
308 @type section: list of dns.rrset.RRset objects
309 @param name: the name of the RRset
310 @type name: dns.name.Name object
311 @param rdclass: the class of the RRset
312 @type rdclass: int
313 @param rdtype: the type of the RRset
314 @type rdtype: int
315 @param covers: the covers value of the RRset
316 @type covers: int
317 @param deleting: the deleting value of the RRset
318 @type deleting: int
319 @param create: If True, create the RRset if it is not found.
320 The created RRset is appended to I{section}.
321 @type create: bool
322 @param force_unique: If True and create is also True, create a
323 new RRset regardless of whether a matching RRset exists already.
324 @type force_unique: bool
325 @raises KeyError: the RRset was not found and create was False
326 @rtype: dns.rrset.RRset object"""
327
328 key = (self.section_number(section),
329 name, rdclass, rdtype, covers, deleting)
330 if not force_unique:
331 if not self.index is None:
332 rrset = self.index.get(key)
333 if not rrset is None:
334 return rrset
335 else:
336 for rrset in section:
337 if rrset.match(name, rdclass, rdtype, covers, deleting):
338 return rrset
339 if not create:
340 raise KeyError
341 rrset = dns.rrset.RRset(name, rdclass, rdtype, covers, deleting)
342 section.append(rrset)
343 if not self.index is None:
344 self.index[key] = rrset
345 return rrset
346
347 - def get_rrset(self, section, name, rdclass, rdtype,
348 covers=dns.rdatatype.NONE, deleting=None, create=False,
349 force_unique=False):
350 """Get the RRset with the given attributes in the specified section.
351
352 If the RRset is not found, None is returned.
353
354 @param section: the section of the message to look in, e.g.
355 self.answer.
356 @type section: list of dns.rrset.RRset objects
357 @param name: the name of the RRset
358 @type name: dns.name.Name object
359 @param rdclass: the class of the RRset
360 @type rdclass: int
361 @param rdtype: the type of the RRset
362 @type rdtype: int
363 @param covers: the covers value of the RRset
364 @type covers: int
365 @param deleting: the deleting value of the RRset
366 @type deleting: int
367 @param create: If True, create the RRset if it is not found.
368 The created RRset is appended to I{section}.
369 @type create: bool
370 @param force_unique: If True and create is also True, create a
371 new RRset regardless of whether a matching RRset exists already.
372 @type force_unique: bool
373 @rtype: dns.rrset.RRset object or None"""
374
375 try:
376 rrset = self.find_rrset(section, name, rdclass, rdtype, covers,
377 deleting, create, force_unique)
378 except KeyError:
379 rrset = None
380 return rrset
381
382 - def to_wire(self, origin=None, max_size=0, **kw):
383 """Return a string containing the message in DNS compressed wire
384 format.
385
386 Additional keyword arguments are passed to the rrset to_wire()
387 method.
388
389 @param origin: The origin to be appended to any relative names.
390 @type origin: dns.name.Name object
391 @param max_size: The maximum size of the wire format output; default
392 is 0, which means 'the message's request payload, if nonzero, or
393 65536'.
394 @type max_size: int
395 @raises dns.exception.TooBig: max_size was exceeded
396 @rtype: string
397 """
398
399 if max_size == 0:
400 if self.request_payload != 0:
401 max_size = self.request_payload
402 else:
403 max_size = 65535
404 if max_size < 512:
405 max_size = 512
406 elif max_size > 65535:
407 max_size = 65535
408 r = dns.renderer.Renderer(self.id, self.flags, max_size, origin)
409 for rrset in self.question:
410 r.add_question(rrset.name, rrset.rdtype, rrset.rdclass)
411 for rrset in self.answer:
412 r.add_rrset(dns.renderer.ANSWER, rrset, **kw)
413 for rrset in self.authority:
414 r.add_rrset(dns.renderer.AUTHORITY, rrset, **kw)
415 if self.edns >= 0:
416 r.add_edns(self.edns, self.ednsflags, self.payload, self.options)
417 for rrset in self.additional:
418 r.add_rrset(dns.renderer.ADDITIONAL, rrset, **kw)
419 r.write_header()
420 if not self.keyname is None:
421 r.add_tsig(self.keyname, self.keyring[self.keyname],
422 self.fudge, self.original_id, self.tsig_error,
423 self.other_data, self.request_mac,
424 self.keyalgorithm)
425 self.mac = r.mac
426 return r.get_wire()
427
431 """When sending, a TSIG signature using the specified keyring
432 and keyname should be added.
433
434 @param keyring: The TSIG keyring to use; defaults to None.
435 @type keyring: dict
436 @param keyname: The name of the TSIG key to use; defaults to None.
437 The key must be defined in the keyring. If a keyring is specified
438 but a keyname is not, then the key used will be the first key in the
439 keyring. Note that the order of keys in a dictionary is not defined,
440 so applications should supply a keyname when a keyring is used, unless
441 they know the keyring contains only one key.
442 @type keyname: dns.name.Name or string
443 @param fudge: TSIG time fudge; default is 300 seconds.
444 @type fudge: int
445 @param original_id: TSIG original id; defaults to the message's id
446 @type original_id: int
447 @param tsig_error: TSIG error code; default is 0.
448 @type tsig_error: int
449 @param other_data: TSIG other data.
450 @type other_data: bytes
451 @param algorithm: The TSIG algorithm to use; defaults to
452 dns.tsig.default_algorithm
453 """
454
455 self.keyring = keyring
456 if keyname is None:
457 self.keyname = next(iter(self.keyring.keys()))
458 else:
459 if isinstance(keyname, str):
460 keyname = dns.name.from_text(keyname)
461 self.keyname = keyname
462 self.keyalgorithm = algorithm
463 self.fudge = fudge
464 if original_id is None:
465 self.original_id = self.id
466 else:
467 self.original_id = original_id
468 self.tsig_error = tsig_error
469 self.other_data = other_data
470
471 - def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None, options=None):
472 """Configure EDNS behavior.
473 @param edns: The EDNS level to use. Specifying None, False, or -1
474 means 'do not use EDNS', and in this case the other parameters are
475 ignored. Specifying True is equivalent to specifying 0, i.e. 'use
476 EDNS0'.
477 @type edns: int or bool or None
478 @param ednsflags: EDNS flag values.
479 @type ednsflags: int
480 @param payload: The EDNS sender's payload field, which is the maximum
481 size of UDP datagram the sender can handle.
482 @type payload: int
483 @param request_payload: The EDNS payload size to use when sending
484 this message. If not specified, defaults to the value of payload.
485 @type request_payload: int or None
486 @param options: The EDNS options
487 @type options: None or list of dns.edns.Option objects
488 @see: RFC 2671
489 """
490 if edns is None or edns is False:
491 edns = -1
492 if edns is True:
493 edns = 0
494 if request_payload is None:
495 request_payload = payload
496 if edns < 0:
497 ednsflags = 0
498 payload = 0
499 request_payload = 0
500 options = []
501 else:
502
503 ednsflags &= 0xFF00FFFF
504 ednsflags |= (edns << 16)
505 if options is None:
506 options = []
507 self.edns = edns
508 self.ednsflags = ednsflags
509 self.payload = payload
510 self.options = options
511 self.request_payload = request_payload
512
514 """Enable or disable 'DNSSEC desired' flag in requests.
515 @param wanted: Is DNSSEC desired? If True, EDNS is enabled if
516 required, and then the DO bit is set. If False, the DO bit is
517 cleared if EDNS is enabled.
518 @type wanted: bool
519 """
520 if wanted:
521 if self.edns < 0:
522 self.use_edns()
523 self.ednsflags |= dns.flags.DO
524 elif self.edns >= 0:
525 self.ednsflags &= ~dns.flags.DO
526
532
534 """Set the rcode.
535 @param rcode: the rcode
536 @type rcode: int
537 """
538 (value, evalue) = dns.rcode.to_flags(rcode)
539 self.flags &= 0xFFF0
540 self.flags |= value
541 self.ednsflags &= 0x00FFFFFF
542 self.ednsflags |= evalue
543 if self.ednsflags != 0 and self.edns < 0:
544 self.edns = 0
545
551
559
561 """Wire format reader.
562
563 @ivar wire: the wire-format message.
564 @type wire: string
565 @ivar message: The message object being built
566 @type message: dns.message.Message object
567 @ivar current: When building a message object from wire format, this
568 variable contains the offset from the beginning of wire of the next octet
569 to be read.
570 @type current: int
571 @ivar updating: Is the message a dynamic update?
572 @type updating: bool
573 @ivar one_rr_per_rrset: Put each RR into its own RRset?
574 @type one_rr_per_rrset: bool
575 @ivar ignore_trailing: Ignore trailing junk at end of request?
576 @type ignore_trailing: bool
577 @ivar zone_rdclass: The class of the zone in messages which are
578 DNS dynamic updates.
579 @type zone_rdclass: int
580 """
581
582 - def __init__(self, wire, message, question_only=False,
583 one_rr_per_rrset=False, ignore_trailing=False):
584 self.wire = dns.wiredata.maybe_wrap(wire)
585 self.message = message
586 self.current = 0
587 self.updating = False
588 self.zone_rdclass = dns.rdataclass.IN
589 self.question_only = question_only
590 self.one_rr_per_rrset = one_rr_per_rrset
591 self.ignore_trailing = ignore_trailing
592
594 """Read the next I{qcount} records from the wire data and add them to
595 the question section.
596 @param qcount: the number of questions in the message
597 @type qcount: int"""
598
599 if self.updating and qcount > 1:
600 raise dns.exception.FormError
601
602 for i in range(0, qcount):
603 (qname, used) = dns.name.from_wire(self.wire, self.current)
604 if not self.message.origin is None:
605 qname = qname.relativize(self.message.origin)
606 self.current = self.current + used
607 (rdtype, rdclass) = \
608 struct.unpack('!HH',
609 self.wire[self.current:self.current + 4])
610 self.current = self.current + 4
611 self.message.find_rrset(self.message.question, qname,
612 rdclass, rdtype, create=True,
613 force_unique=True)
614 if self.updating:
615 self.zone_rdclass = rdclass
616
618 """Read the next I{count} records from the wire data and add them to
619 the specified section.
620 @param section: the section of the message to which to add records
621 @type section: list of dns.rrset.RRset objects
622 @param count: the number of records to read
623 @type count: int"""
624
625 if self.updating or self.one_rr_per_rrset:
626 force_unique = True
627 else:
628 force_unique = False
629 seen_opt = False
630 for i in range(0, count):
631 rr_start = self.current
632 (name, used) = dns.name.from_wire(self.wire, self.current)
633 absolute_name = name
634 if not self.message.origin is None:
635 name = name.relativize(self.message.origin)
636 self.current = self.current + used
637 (rdtype, rdclass, ttl, rdlen) = \
638 struct.unpack('!HHIH',
639 self.wire[self.current:self.current + 10])
640 self.current = self.current + 10
641 if rdtype == dns.rdatatype.OPT:
642 if not section is self.message.additional or seen_opt:
643 raise BadEDNS
644 self.message.payload = rdclass
645 self.message.ednsflags = ttl
646 self.message.edns = (ttl & 0xff0000) >> 16
647 self.message.options = []
648 current = self.current
649 optslen = rdlen
650 while optslen > 0:
651 (otype, olen) = \
652 struct.unpack('!HH',
653 self.wire[current:current + 4])
654 current = current + 4
655 opt = dns.edns.option_from_wire(otype, self.wire, current, olen)
656 self.message.options.append(opt)
657 current = current + olen
658 optslen = optslen - 4 - olen
659 seen_opt = True
660 elif rdtype == dns.rdatatype.TSIG:
661 if not (section is self.message.additional and
662 i == (count - 1)):
663 raise BadTSIG
664 if self.message.keyring is None:
665 raise UnknownTSIGKey('got signed message without keyring')
666 secret = self.message.keyring.get(absolute_name)
667 if secret is None:
668 raise UnknownTSIGKey("key '%s' unknown" % name)
669 self.message.tsig_ctx = \
670 dns.tsig.validate(self.wire,
671 absolute_name,
672 secret,
673 int(time.time()),
674 self.message.request_mac,
675 rr_start,
676 self.current,
677 rdlen,
678 self.message.tsig_ctx,
679 self.message.multi,
680 self.message.first)
681 self.message.had_tsig = True
682 else:
683 if ttl < 0:
684 ttl = 0
685 if self.updating and \
686 (rdclass == dns.rdataclass.ANY or
687 rdclass == dns.rdataclass.NONE):
688 deleting = rdclass
689 rdclass = self.zone_rdclass
690 else:
691 deleting = None
692 if deleting == dns.rdataclass.ANY or \
693 (deleting == dns.rdataclass.NONE and \
694 section is self.message.answer):
695 covers = dns.rdatatype.NONE
696 rd = None
697 else:
698 rd = dns.rdata.from_wire(rdclass, rdtype, self.wire,
699 self.current, rdlen,
700 self.message.origin)
701 covers = rd.covers()
702 if self.message.xfr and rdtype == dns.rdatatype.SOA:
703 force_unique = True
704 rrset = self.message.find_rrset(section, name,
705 rdclass, rdtype, covers,
706 deleting, True, force_unique)
707 if not rd is None:
708 rrset.add(rd, ttl)
709 self.current = self.current + rdlen
710
734
735
736 -def from_wire(wire, keyring=None, request_mac=b'', xfr=False, origin=None,
737 tsig_ctx = None, multi = False, first = True,
738 question_only = False, one_rr_per_rrset = False,
739 ignore_trailing = False):
740 """Convert a DNS wire format message into a message
741 object.
742
743 @param keyring: The keyring to use if the message is signed.
744 @type keyring: dict
745 @param request_mac: If the message is a response to a TSIG-signed request,
746 I{request_mac} should be set to the MAC of that request.
747 @type request_mac: bytes
748 @param xfr: Is this message part of a zone transfer?
749 @type xfr: bool
750 @param origin: If the message is part of a zone transfer, I{origin}
751 should be the origin name of the zone.
752 @type origin: dns.name.Name object
753 @param tsig_ctx: The ongoing TSIG context, used when validating zone
754 transfers.
755 @type tsig_ctx: hmac.HMAC object
756 @param multi: Is this message part of a multiple message sequence?
757 @type multi: bool
758 @param first: Is this message standalone, or the first of a multi
759 message sequence?
760 @type first: bool
761 @param question_only: Read only up to the end of the question section?
762 @type question_only: bool
763 @param one_rr_per_rrset: Put each RR into its own RRset
764 @type one_rr_per_rrset: bool
765 @param ignore_trailing: Ignore trailing junk at end of request?
766 @type ignore_trailing: bool
767 @raises ShortHeader: The message is less than 12 octets long.
768 @raises TrailingJunk: There were octets in the message past the end
769 of the proper DNS message.
770 @raises BadEDNS: An OPT record was in the wrong section, or occurred more
771 than once.
772 @raises BadTSIG: A TSIG record was not the last record of the additional
773 data section.
774 @rtype: dns.message.Message object"""
775
776 m = Message(id=0)
777 m.keyring = keyring
778 m.request_mac = request_mac
779 m.xfr = xfr
780 m.origin = origin
781 m.tsig_ctx = tsig_ctx
782 m.multi = multi
783 m.first = first
784
785 reader = _WireReader(wire, m, question_only, one_rr_per_rrset,
786 ignore_trailing)
787 reader.read()
788
789 return m
790
791
792 -class _TextReader(object):
793 """Text format reader.
794
795 @ivar tok: the tokenizer
796 @type tok: dns.tokenizer.Tokenizer object
797 @ivar message: The message object being built
798 @type message: dns.message.Message object
799 @ivar updating: Is the message a dynamic update?
800 @type updating: bool
801 @ivar zone_rdclass: The class of the zone in messages which are
802 DNS dynamic updates.
803 @type zone_rdclass: int
804 @ivar last_name: The most recently read name when building a message object
805 from text format.
806 @type last_name: dns.name.Name object
807 """
808
809 - def __init__(self, text, message):
810 self.message = message
811 self.tok = dns.tokenizer.Tokenizer(text)
812 self.last_name = None
813 self.zone_rdclass = dns.rdataclass.IN
814 self.updating = False
815
817 """Process one line from the text format header section."""
818
819 token = self.tok.get()
820 what = token.value
821 if what == 'id':
822 self.message.id = self.tok.get_int()
823 elif what == 'flags':
824 while True:
825 token = self.tok.get()
826 if not token.is_identifier():
827 self.tok.unget(token)
828 break
829 self.message.flags = self.message.flags | \
830 dns.flags.from_text(token.value)
831 if dns.opcode.is_update(self.message.flags):
832 self.updating = True
833 elif what == 'edns':
834 self.message.edns = self.tok.get_int()
835 self.message.ednsflags = self.message.ednsflags | \
836 (self.message.edns << 16)
837 elif what == 'eflags':
838 if self.message.edns < 0:
839 self.message.edns = 0
840 while True:
841 token = self.tok.get()
842 if not token.is_identifier():
843 self.tok.unget(token)
844 break
845 self.message.ednsflags = self.message.ednsflags | \
846 dns.flags.edns_from_text(token.value)
847 elif what == 'payload':
848 self.message.payload = self.tok.get_int()
849 if self.message.edns < 0:
850 self.message.edns = 0
851 elif what == 'opcode':
852 text = self.tok.get_string()
853 self.message.flags = self.message.flags | \
854 dns.opcode.to_flags(dns.opcode.from_text(text))
855 elif what == 'rcode':
856 text = self.tok.get_string()
857 self.message.set_rcode(dns.rcode.from_text(text))
858 else:
859 raise UnknownHeaderField
860 self.tok.get_eol()
861
862 - def _question_line(self, section):
863 """Process one line from the text format question section."""
864
865 token = self.tok.get(want_leading = True)
866 if not token.is_whitespace():
867 self.last_name = dns.name.from_text(token.value, None)
868 name = self.last_name
869 token = self.tok.get()
870 if not token.is_identifier():
871 raise dns.exception.SyntaxError
872
873 try:
874 rdclass = dns.rdataclass.from_text(token.value)
875 token = self.tok.get()
876 if not token.is_identifier():
877 raise dns.exception.SyntaxError
878 except dns.exception.SyntaxError:
879 raise dns.exception.SyntaxError
880 except:
881 rdclass = dns.rdataclass.IN
882
883 rdtype = dns.rdatatype.from_text(token.value)
884 self.message.find_rrset(self.message.question, name,
885 rdclass, rdtype, create=True,
886 force_unique=True)
887 if self.updating:
888 self.zone_rdclass = rdclass
889 self.tok.get_eol()
890
891 - def _rr_line(self, section):
892 """Process one line from the text format answer, authority, or
893 additional data sections.
894 """
895
896 deleting = None
897
898 token = self.tok.get(want_leading = True)
899 if not token.is_whitespace():
900 self.last_name = dns.name.from_text(token.value, None)
901 name = self.last_name
902 token = self.tok.get()
903 if not token.is_identifier():
904 raise dns.exception.SyntaxError
905
906 try:
907 ttl = int(token.value, 0)
908 token = self.tok.get()
909 if not token.is_identifier():
910 raise dns.exception.SyntaxError
911 except dns.exception.SyntaxError:
912 raise dns.exception.SyntaxError
913 except:
914 ttl = 0
915
916 try:
917 rdclass = dns.rdataclass.from_text(token.value)
918 token = self.tok.get()
919 if not token.is_identifier():
920 raise dns.exception.SyntaxError
921 if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE:
922 deleting = rdclass
923 rdclass = self.zone_rdclass
924 except dns.exception.SyntaxError:
925 raise dns.exception.SyntaxError
926 except:
927 rdclass = dns.rdataclass.IN
928
929 rdtype = dns.rdatatype.from_text(token.value)
930 token = self.tok.get()
931 if not token.is_eol_or_eof():
932 self.tok.unget(token)
933 rd = dns.rdata.from_text(rdclass, rdtype, self.tok, None)
934 covers = rd.covers()
935 else:
936 rd = None
937 covers = dns.rdatatype.NONE
938 rrset = self.message.find_rrset(section, name,
939 rdclass, rdtype, covers,
940 deleting, True, self.updating)
941 if not rd is None:
942 rrset.add(rd, ttl)
943
945 """Read a text format DNS message and build a dns.message.Message
946 object."""
947
948 line_method = self._header_line
949 section = None
950 while 1:
951 token = self.tok.get(True, True)
952 if token.is_eol_or_eof():
953 break
954 if token.is_comment():
955 u = token.value.upper()
956 if u == 'HEADER':
957 line_method = self._header_line
958 elif u == 'QUESTION' or u == 'ZONE':
959 line_method = self._question_line
960 section = self.message.question
961 elif u == 'ANSWER' or u == 'PREREQ':
962 line_method = self._rr_line
963 section = self.message.answer
964 elif u == 'AUTHORITY' or u == 'UPDATE':
965 line_method = self._rr_line
966 section = self.message.authority
967 elif u == 'ADDITIONAL':
968 line_method = self._rr_line
969 section = self.message.additional
970 self.tok.get_eol()
971 continue
972 self.tok.unget(token)
973 line_method(section)
974
975
976 -def from_text(text):
977 """Convert the text format message into a message object.
978
979 @param text: The text format message.
980 @type text: string
981 @raises UnknownHeaderField:
982 @raises dns.exception.SyntaxError:
983 @rtype: dns.message.Message object"""
984
985
986
987
988
989 m = Message()
990
991 reader = _TextReader(text, m)
992 reader.read()
993
994 return m
995
997 """Read the next text format message from the specified file.
998
999 @param f: file or string. If I{f} is a string, it is treated
1000 as the name of a file to open.
1001 @raises UnknownHeaderField:
1002 @raises dns.exception.SyntaxError:
1003 @rtype: dns.message.Message object"""
1004
1005 if isinstance(f, str):
1006 f = open(f, 'rU')
1007 want_close = True
1008 else:
1009 want_close = False
1010
1011 try:
1012 m = from_text(f)
1013 finally:
1014 if want_close:
1015 f.close()
1016 return m
1017
1018 -def make_query(qname, rdtype, rdclass = dns.rdataclass.IN, use_edns=None,
1019 want_dnssec=False, ednsflags=0, payload=1280,
1020 request_payload=None, options=None):
1021 """Make a query message.
1022
1023 The query name, type, and class may all be specified either
1024 as objects of the appropriate type, or as strings.
1025
1026 The query will have a randomly choosen query id, and its DNS flags
1027 will be set to dns.flags.RD.
1028
1029 @param qname: The query name.
1030 @type qname: dns.name.Name object or string
1031 @param rdtype: The desired rdata type.
1032 @type rdtype: int
1033 @param rdclass: The desired rdata class; the default is class IN.
1034 @type rdclass: int
1035 @param use_edns: The EDNS level to use; the default is None (no EDNS).
1036 See the description of dns.message.Message.use_edns() for the possible
1037 values for use_edns and their meanings.
1038 @type use_edns: int or bool or None
1039 @param want_dnssec: Should the query indicate that DNSSEC is desired?
1040 @type want_dnssec: bool
1041 @param ednsflags: EDNS flag values.
1042 @type ednsflags: int
1043 @param payload: The EDNS sender's payload field, which is the maximum
1044 size of UDP datagram the sender can handle.
1045 @type payload: int
1046 @param request_payload: The EDNS payload size to use when sending
1047 this message. If not specified, defaults to the value of payload.
1048 @type request_payload: int or None
1049 @param options: The EDNS options
1050 @type options: None or list of dns.edns.Option objects
1051 @see: RFC 2671
1052 @rtype: dns.message.Message object"""
1053
1054 if isinstance(qname, str):
1055 qname = dns.name.from_text(qname)
1056 if isinstance(rdtype, str):
1057 rdtype = dns.rdatatype.from_text(rdtype)
1058 if isinstance(rdclass, str):
1059 rdclass = dns.rdataclass.from_text(rdclass)
1060 m = Message()
1061 m.flags |= dns.flags.RD
1062 m.find_rrset(m.question, qname, rdclass, rdtype, create=True,
1063 force_unique=True)
1064 m.use_edns(use_edns, ednsflags, payload, request_payload, options)
1065 m.want_dnssec(want_dnssec)
1066 return m
1067
1068 -def make_response(query, recursion_available=False, our_payload=8192):
1069 """Make a message which is a response for the specified query.
1070 The message returned is really a response skeleton; it has all
1071 of the infrastructure required of a response, but none of the
1072 content.
1073
1074 The response's question section is a shallow copy of the query's
1075 question section, so the query's question RRsets should not be
1076 changed.
1077
1078 @param query: the query to respond to
1079 @type query: dns.message.Message object
1080 @param recursion_available: should RA be set in the response?
1081 @type recursion_available: bool
1082 @param our_payload: payload size to advertise in EDNS responses; default
1083 is 8192.
1084 @type our_payload: int
1085 @rtype: dns.message.Message object"""
1086
1087 if query.flags & dns.flags.QR:
1088 raise dns.exception.FormError('specified query message is not a query')
1089 response = dns.message.Message(query.id)
1090 response.flags = dns.flags.QR | (query.flags & dns.flags.RD)
1091 if recursion_available:
1092 response.flags |= dns.flags.RA
1093 response.set_opcode(query.opcode())
1094 response.question = list(query.question)
1095 if query.edns >= 0:
1096 response.use_edns(0, 0, our_payload, query.payload)
1097 if not query.keyname is None:
1098 response.keyname = query.keyname
1099 response.keyring = query.keyring
1100 response.request_mac = query.mac
1101 return response
1102