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