1 # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
3 # Permission to use, copy, modify, and distribute this software and its
4 # documentation for any purpose with or without fee is hereby granted,
5 # provided that the above copyright notice and this permission notice
6 # appear in all copies.
8 # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
9 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
11 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
14 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
39 class ShortHeader(dns
.exception
.FormError
):
40 """Raised if the DNS packet passed to from_wire() is too short."""
43 class TrailingJunk(dns
.exception
.FormError
):
44 """Raised if the DNS packet passed to from_wire() has extra junk
48 class UnknownHeaderField(dns
.exception
.DNSException
):
49 """Raised if a header field name is not recognized when converting from
50 text into a message."""
53 class BadEDNS(dns
.exception
.FormError
):
54 """Raised if an OPT record occurs somewhere other than the start of
55 the additional data section."""
58 class BadTSIG(dns
.exception
.FormError
):
59 """Raised if a TSIG record occurs somewhere other than the end of
60 the additional data section."""
63 class UnknownTSIGKey(dns
.exception
.DNSException
):
64 """Raised if we got a TSIG but don't know the key."""
67 class Message(object):
70 @ivar id: The query id; the default is a randomly chosen id.
72 @ivar flags: The DNS flags of the message. @see: RFC 1035 for an
73 explanation of these flags.
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.
85 @ivar ednsflags: The EDNS flags
87 @ivar payload: The EDNS payload size. The default is 0.
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.
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
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.
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.
117 @ivar xfr: Is the message being used to contain the results of a DNS
118 zone transfer? The default is False.
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
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.
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.
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.
143 def __init__(self
, id=None):
145 self
.id = dns
.entropy
.random_16()
157 self
.request_payload
= 0
160 self
.keyalgorithm
= dns
.tsig
.default_algorithm
161 self
.request_mac
= ''
165 self
.original_id
= self
.id
170 self
.had_tsig
= False
176 return '<DNS message, ID ' + `self
.id`
+ '>'
179 return self
.to_text()
181 def to_text(self
, origin
=None, relativize
=True, **kw
):
182 """Convert the message to text.
184 The I{origin}, I{relativize}, and any other keyword
185 arguments are passed to the rrset to_wire() method.
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
)
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
)
207 print >> s
, ';QUESTION'
208 for rrset
in self
.question
:
209 print >> s
, rrset
.to_text(origin
, relativize
, **kw
)
211 print >> s
, ';PREREQ'
213 print >> s
, ';ANSWER'
214 for rrset
in self
.answer
:
215 print >> s
, rrset
.to_text(origin
, relativize
, **kw
)
217 print >> s
, ';UPDATE'
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
)
226 # We strip off the final \n so the caller can print the result without
227 # doing weird things to get around eccentricities in Python print
230 return s
.getvalue()[:-1]
232 def __eq__(self
, other
):
233 """Two messages are equal if they have the same content in the
234 header, question, answer, and authority sections.
236 if not isinstance(other
, Message
):
238 if self
.id != other
.id:
240 if self
.flags
!= other
.flags
:
242 for n
in self
.question
:
243 if n
not in other
.question
:
245 for n
in other
.question
:
246 if n
not in self
.question
:
248 for n
in self
.answer
:
249 if n
not in other
.answer
:
251 for n
in other
.answer
:
252 if n
not in self
.answer
:
254 for n
in self
.authority
:
255 if n
not in other
.authority
:
257 for n
in other
.authority
:
258 if n
not in self
.authority
:
262 def __ne__(self
, other
):
263 """Are two messages not equal?
265 return not self
.__eq
__(other
)
267 def is_response(self
, other
):
268 """Is other a response to self?
270 if other
.flags
& dns
.flags
.QR
== 0 or \
271 self
.id != other
.id or \
272 dns
.opcode
.from_flags(self
.flags
) != \
273 dns
.opcode
.from_flags(other
.flags
):
275 if dns
.rcode
.from_flags(other
.flags
, other
.ednsflags
) != \
278 if dns
.opcode
.is_update(self
.flags
):
280 for n
in self
.question
:
281 if n
not in other
.question
:
283 for n
in other
.question
:
284 if n
not in self
.question
:
288 def section_number(self
, section
):
289 if section
is self
.question
:
291 elif section
is self
.answer
:
293 elif section
is self
.authority
:
295 elif section
is self
.additional
:
298 raise ValueError('unknown section')
300 def find_rrset(self
, section
, name
, rdclass
, rdtype
,
301 covers
=dns
.rdatatype
.NONE
, deleting
=None, create
=False,
303 """Find the RRset with the given attributes in the specified section.
305 @param section: the section of the message to look in, e.g.
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
312 @param rdtype: the type of the RRset
314 @param covers: the covers value of the RRset
316 @param deleting: the deleting value of the RRset
318 @param create: If True, create the RRset if it is not found.
319 The created RRset is appended to I{section}.
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"""
327 key
= (self
.section_number(section
),
328 name
, rdclass
, rdtype
, covers
, deleting
)
330 if not self
.index
is None:
331 rrset
= self
.index
.get(key
)
332 if not rrset
is None:
335 for rrset
in section
:
336 if rrset
.match(name
, rdclass
, rdtype
, covers
, deleting
):
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
346 def get_rrset(self
, section
, name
, rdclass
, rdtype
,
347 covers
=dns
.rdatatype
.NONE
, deleting
=None, create
=False,
349 """Get the RRset with the given attributes in the specified section.
351 If the RRset is not found, None is returned.
353 @param section: the section of the message to look in, e.g.
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
360 @param rdtype: the type of the RRset
362 @param covers: the covers value of the RRset
364 @param deleting: the deleting value of the RRset
366 @param create: If True, create the RRset if it is not found.
367 The created RRset is appended to I{section}.
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"""
375 rrset
= self
.find_rrset(section
, name
, rdclass
, rdtype
, covers
,
376 deleting
, create
, force_unique
)
381 def to_wire(self
, origin
=None, max_size
=0, **kw
):
382 """Return a string containing the message in DNS compressed wire
385 Additional keyword arguments are passed to the rrset to_wire()
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
394 @raises dns.exception.TooBig: max_size was exceeded
399 if self
.request_payload
!= 0:
400 max_size
= self
.request_payload
405 elif 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
)
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
)
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
,
427 def use_tsig(self
, keyring
, keyname
=None, fudge
=300,
428 original_id
=None, tsig_error
=0, other_data
='',
429 algorithm
=dns
.tsig
.default_algorithm
):
430 """When sending, a TSIG signature using the specified keyring
431 and keyname should be added.
433 @param keyring: The TSIG keyring to use; defaults to None.
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.
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
454 self
.keyring
= keyring
456 self
.keyname
= self
.keyring
.keys()[0]
458 if isinstance(keyname
, (str, unicode)):
459 keyname
= dns
.name
.from_text(keyname
)
460 self
.keyname
= keyname
461 self
.keyalgorithm
= algorithm
463 if original_id
is None:
464 self
.original_id
= self
.id
466 self
.original_id
= original_id
467 self
.tsig_error
= tsig_error
468 self
.other_data
= other_data
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
476 @type edns: int or bool or None
477 @param ednsflags: EDNS flag values.
479 @param payload: The EDNS sender's payload field, which is the maximum
480 size of UDP datagram the sender can handle.
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
489 if edns
is None or edns
is False:
493 if request_payload
is None:
494 request_payload
= payload
501 # make sure the EDNS version in ednsflags agrees with edns
502 ednsflags
&= 0xFF00FFFFL
503 ednsflags |
= (edns
<< 16)
507 self
.ednsflags
= ednsflags
508 self
.payload
= payload
509 self
.options
= options
510 self
.request_payload
= request_payload
512 def want_dnssec(self
, wanted
=True):
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.
522 self
.ednsflags |
= dns
.flags
.DO
524 self
.ednsflags
&= ~dns
.flags
.DO
530 return dns
.rcode
.from_flags(self
.flags
, self
.ednsflags
)
532 def set_rcode(self
, rcode
):
534 @param rcode: the rcode
537 (value
, evalue
) = dns
.rcode
.to_flags(rcode
)
540 self
.ednsflags
&= 0x00FFFFFFL
541 self
.ednsflags |
= evalue
542 if self
.ednsflags
!= 0 and self
.edns
< 0:
546 """Return the opcode.
549 return dns
.opcode
.from_flags(self
.flags
)
551 def set_opcode(self
, opcode
):
553 @param opcode: the opcode
557 self
.flags |
= dns
.opcode
.to_flags(opcode
)
559 class _WireReader(object):
560 """Wire format reader.
562 @ivar wire: the wire-format message.
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
570 @ivar updating: Is the message a dynamic update?
572 @ivar one_rr_per_rrset: Put each RR into its own RRset?
573 @type one_rr_per_rrset: bool
574 @ivar zone_rdclass: The class of the zone in messages which are
576 @type zone_rdclass: int
579 def __init__(self
, wire
, message
, question_only
=False,
580 one_rr_per_rrset
=False):
581 self
.wire
= dns
.wiredata
.maybe_wrap(wire
)
582 self
.message
= message
584 self
.updating
= False
585 self
.zone_rdclass
= dns
.rdataclass
.IN
586 self
.question_only
= question_only
587 self
.one_rr_per_rrset
= one_rr_per_rrset
589 def _get_question(self
, qcount
):
590 """Read the next I{qcount} records from the wire data and add them to
591 the question section.
592 @param qcount: the number of questions in the message
595 if self
.updating
and qcount
> 1:
596 raise dns
.exception
.FormError
598 for i
in xrange(0, qcount
):
599 (qname
, used
) = dns
.name
.from_wire(self
.wire
, self
.current
)
600 if not self
.message
.origin
is None:
601 qname
= qname
.relativize(self
.message
.origin
)
602 self
.current
= self
.current
+ used
603 (rdtype
, rdclass
) = \
605 self
.wire
[self
.current
:self
.current
+ 4])
606 self
.current
= self
.current
+ 4
607 self
.message
.find_rrset(self
.message
.question
, qname
,
608 rdclass
, rdtype
, create
=True,
611 self
.zone_rdclass
= rdclass
613 def _get_section(self
, section
, count
):
614 """Read the next I{count} records from the wire data and add them to
615 the specified section.
616 @param section: the section of the message to which to add records
617 @type section: list of dns.rrset.RRset objects
618 @param count: the number of records to read
621 if self
.updating
or self
.one_rr_per_rrset
:
626 for i
in xrange(0, count
):
627 rr_start
= self
.current
628 (name
, used
) = dns
.name
.from_wire(self
.wire
, self
.current
)
630 if not self
.message
.origin
is None:
631 name
= name
.relativize(self
.message
.origin
)
632 self
.current
= self
.current
+ used
633 (rdtype
, rdclass
, ttl
, rdlen
) = \
634 struct
.unpack('!HHIH',
635 self
.wire
[self
.current
:self
.current
+ 10])
636 self
.current
= self
.current
+ 10
637 if rdtype
== dns
.rdatatype
.OPT
:
638 if not section
is self
.message
.additional
or seen_opt
:
640 self
.message
.payload
= rdclass
641 self
.message
.ednsflags
= ttl
642 self
.message
.edns
= (ttl
& 0xff0000) >> 16
643 self
.message
.options
= []
644 current
= self
.current
649 self
.wire
[current
:current
+ 4])
650 current
= current
+ 4
651 opt
= dns
.edns
.option_from_wire(otype
, self
.wire
, current
, olen
)
652 self
.message
.options
.append(opt
)
653 current
= current
+ olen
654 optslen
= optslen
- 4 - olen
656 elif rdtype
== dns
.rdatatype
.TSIG
:
657 if not (section
is self
.message
.additional
and
660 if self
.message
.keyring
is None:
661 raise UnknownTSIGKey('got signed message without keyring')
662 secret
= self
.message
.keyring
.get(absolute_name
)
664 raise UnknownTSIGKey("key '%s' unknown" % name
)
665 self
.message
.tsig_ctx
= \
666 dns
.tsig
.validate(self
.wire
,
670 self
.message
.request_mac
,
674 self
.message
.tsig_ctx
,
677 self
.message
.had_tsig
= True
681 if self
.updating
and \
682 (rdclass
== dns
.rdataclass
.ANY
or
683 rdclass
== dns
.rdataclass
.NONE
):
685 rdclass
= self
.zone_rdclass
688 if deleting
== dns
.rdataclass
.ANY
or \
689 (deleting
== dns
.rdataclass
.NONE
and \
690 section
is self
.message
.answer
):
691 covers
= dns
.rdatatype
.NONE
694 rd
= dns
.rdata
.from_wire(rdclass
, rdtype
, self
.wire
,
698 if self
.message
.xfr
and rdtype
== dns
.rdatatype
.SOA
:
700 rrset
= self
.message
.find_rrset(section
, name
,
701 rdclass
, rdtype
, covers
,
702 deleting
, True, force_unique
)
705 self
.current
= self
.current
+ rdlen
708 """Read a wire format DNS message and build a dns.message.Message
714 (self
.message
.id, self
.message
.flags
, qcount
, ancount
,
715 aucount
, adcount
) = struct
.unpack('!HHHHHH', self
.wire
[:12])
717 if dns
.opcode
.is_update(self
.message
.flags
):
719 self
._get
_question
(qcount
)
720 if self
.question_only
:
722 self
._get
_section
(self
.message
.answer
, ancount
)
723 self
._get
_section
(self
.message
.authority
, aucount
)
724 self
._get
_section
(self
.message
.additional
, adcount
)
725 if self
.current
!= l
:
727 if self
.message
.multi
and self
.message
.tsig_ctx
and \
728 not self
.message
.had_tsig
:
729 self
.message
.tsig_ctx
.update(self
.wire
)
732 def from_wire(wire
, keyring
=None, request_mac
='', xfr
=False, origin
=None,
733 tsig_ctx
= None, multi
= False, first
= True,
734 question_only
= False, one_rr_per_rrset
= False):
735 """Convert a DNS wire format message into a message
738 @param keyring: The keyring to use if the message is signed.
740 @param request_mac: If the message is a response to a TSIG-signed request,
741 I{request_mac} should be set to the MAC of that request.
742 @type request_mac: string
743 @param xfr: Is this message part of a zone transfer?
745 @param origin: If the message is part of a zone transfer, I{origin}
746 should be the origin name of the zone.
747 @type origin: dns.name.Name object
748 @param tsig_ctx: The ongoing TSIG context, used when validating zone
750 @type tsig_ctx: hmac.HMAC object
751 @param multi: Is this message part of a multiple message sequence?
753 @param first: Is this message standalone, or the first of a multi
756 @param question_only: Read only up to the end of the question section?
757 @type question_only: bool
758 @param one_rr_per_rrset: Put each RR into its own RRset
759 @type one_rr_per_rrset: bool
760 @raises ShortHeader: The message is less than 12 octets long.
761 @raises TrailingJunk: There were octets in the message past the end
762 of the proper DNS message.
763 @raises BadEDNS: An OPT record was in the wrong section, or occurred more
765 @raises BadTSIG: A TSIG record was not the last record of the additional
767 @rtype: dns.message.Message object"""
771 m
.request_mac
= request_mac
774 m
.tsig_ctx
= tsig_ctx
778 reader
= _WireReader(wire
, m
, question_only
, one_rr_per_rrset
)
784 class _TextReader(object):
785 """Text format reader.
787 @ivar tok: the tokenizer
788 @type tok: dns.tokenizer.Tokenizer object
789 @ivar message: The message object being built
790 @type message: dns.message.Message object
791 @ivar updating: Is the message a dynamic update?
793 @ivar zone_rdclass: The class of the zone in messages which are
795 @type zone_rdclass: int
796 @ivar last_name: The most recently read name when building a message object
798 @type last_name: dns.name.Name object
801 def __init__(self
, text
, message
):
802 self
.message
= message
803 self
.tok
= dns
.tokenizer
.Tokenizer(text
)
804 self
.last_name
= None
805 self
.zone_rdclass
= dns
.rdataclass
.IN
806 self
.updating
= False
808 def _header_line(self
, section
):
809 """Process one line from the text format header section."""
811 token
= self
.tok
.get()
814 self
.message
.id = self
.tok
.get_int()
815 elif what
== 'flags':
817 token
= self
.tok
.get()
818 if not token
.is_identifier():
819 self
.tok
.unget(token
)
821 self
.message
.flags
= self
.message
.flags | \
822 dns
.flags
.from_text(token
.value
)
823 if dns
.opcode
.is_update(self
.message
.flags
):
826 self
.message
.edns
= self
.tok
.get_int()
827 self
.message
.ednsflags
= self
.message
.ednsflags | \
828 (self
.message
.edns
<< 16)
829 elif what
== 'eflags':
830 if self
.message
.edns
< 0:
831 self
.message
.edns
= 0
833 token
= self
.tok
.get()
834 if not token
.is_identifier():
835 self
.tok
.unget(token
)
837 self
.message
.ednsflags
= self
.message
.ednsflags | \
838 dns
.flags
.edns_from_text(token
.value
)
839 elif what
== 'payload':
840 self
.message
.payload
= self
.tok
.get_int()
841 if self
.message
.edns
< 0:
842 self
.message
.edns
= 0
843 elif what
== 'opcode':
844 text
= self
.tok
.get_string()
845 self
.message
.flags
= self
.message
.flags | \
846 dns
.opcode
.to_flags(dns
.opcode
.from_text(text
))
847 elif what
== 'rcode':
848 text
= self
.tok
.get_string()
849 self
.message
.set_rcode(dns
.rcode
.from_text(text
))
851 raise UnknownHeaderField
854 def _question_line(self
, section
):
855 """Process one line from the text format question section."""
857 token
= self
.tok
.get(want_leading
= True)
858 if not token
.is_whitespace():
859 self
.last_name
= dns
.name
.from_text(token
.value
, None)
860 name
= self
.last_name
861 token
= self
.tok
.get()
862 if not token
.is_identifier():
863 raise dns
.exception
.SyntaxError
866 rdclass
= dns
.rdataclass
.from_text(token
.value
)
867 token
= self
.tok
.get()
868 if not token
.is_identifier():
869 raise dns
.exception
.SyntaxError
870 except dns
.exception
.SyntaxError:
871 raise dns
.exception
.SyntaxError
873 rdclass
= dns
.rdataclass
.IN
875 rdtype
= dns
.rdatatype
.from_text(token
.value
)
876 self
.message
.find_rrset(self
.message
.question
, name
,
877 rdclass
, rdtype
, create
=True,
880 self
.zone_rdclass
= rdclass
883 def _rr_line(self
, section
):
884 """Process one line from the text format answer, authority, or
885 additional data sections.
890 token
= self
.tok
.get(want_leading
= True)
891 if not token
.is_whitespace():
892 self
.last_name
= dns
.name
.from_text(token
.value
, None)
893 name
= self
.last_name
894 token
= self
.tok
.get()
895 if not token
.is_identifier():
896 raise dns
.exception
.SyntaxError
899 ttl
= int(token
.value
, 0)
900 token
= self
.tok
.get()
901 if not token
.is_identifier():
902 raise dns
.exception
.SyntaxError
903 except dns
.exception
.SyntaxError:
904 raise dns
.exception
.SyntaxError
909 rdclass
= dns
.rdataclass
.from_text(token
.value
)
910 token
= self
.tok
.get()
911 if not token
.is_identifier():
912 raise dns
.exception
.SyntaxError
913 if rdclass
== dns
.rdataclass
.ANY
or rdclass
== dns
.rdataclass
.NONE
:
915 rdclass
= self
.zone_rdclass
916 except dns
.exception
.SyntaxError:
917 raise dns
.exception
.SyntaxError
919 rdclass
= dns
.rdataclass
.IN
921 rdtype
= dns
.rdatatype
.from_text(token
.value
)
922 token
= self
.tok
.get()
923 if not token
.is_eol_or_eof():
924 self
.tok
.unget(token
)
925 rd
= dns
.rdata
.from_text(rdclass
, rdtype
, self
.tok
, None)
929 covers
= dns
.rdatatype
.NONE
930 rrset
= self
.message
.find_rrset(section
, name
,
931 rdclass
, rdtype
, covers
,
932 deleting
, True, self
.updating
)
937 """Read a text format DNS message and build a dns.message.Message
940 line_method
= self
._header
_line
943 token
= self
.tok
.get(True, True)
944 if token
.is_eol_or_eof():
946 if token
.is_comment():
947 u
= token
.value
.upper()
949 line_method
= self
._header
_line
950 elif u
== 'QUESTION' or u
== 'ZONE':
951 line_method
= self
._question
_line
952 section
= self
.message
.question
953 elif u
== 'ANSWER' or u
== 'PREREQ':
954 line_method
= self
._rr
_line
955 section
= self
.message
.answer
956 elif u
== 'AUTHORITY' or u
== 'UPDATE':
957 line_method
= self
._rr
_line
958 section
= self
.message
.authority
959 elif u
== 'ADDITIONAL':
960 line_method
= self
._rr
_line
961 section
= self
.message
.additional
964 self
.tok
.unget(token
)
969 """Convert the text format message into a message object.
971 @param text: The text format message.
973 @raises UnknownHeaderField:
974 @raises dns.exception.SyntaxError:
975 @rtype: dns.message.Message object"""
977 # 'text' can also be a file, but we don't publish that fact
978 # since it's an implementation detail. The official file
979 # interface is from_file().
983 reader
= _TextReader(text
, m
)
989 """Read the next text format message from the specified file.
991 @param f: file or string. If I{f} is a string, it is treated
992 as the name of a file to open.
993 @raises UnknownHeaderField:
994 @raises dns.exception.SyntaxError:
995 @rtype: dns.message.Message object"""
997 if sys
.hexversion
>= 0x02030000:
998 # allow Unicode filenames; turn on universal newline support
999 str_type
= basestring
1004 if isinstance(f
, str_type
):
1017 def make_query(qname
, rdtype
, rdclass
= dns
.rdataclass
.IN
, use_edns
=None,
1019 """Make a query message.
1021 The query name, type, and class may all be specified either
1022 as objects of the appropriate type, or as strings.
1024 The query will have a randomly choosen query id, and its DNS flags
1025 will be set to dns.flags.RD.
1027 @param qname: The query name.
1028 @type qname: dns.name.Name object or string
1029 @param rdtype: The desired rdata type.
1031 @param rdclass: The desired rdata class; the default is class IN.
1033 @param use_edns: The EDNS level to use; the default is None (no EDNS).
1034 See the description of dns.message.Message.use_edns() for the possible
1035 values for use_edns and their meanings.
1036 @type use_edns: int or bool or None
1037 @param want_dnssec: Should the query indicate that DNSSEC is desired?
1038 @type want_dnssec: bool
1039 @rtype: dns.message.Message object"""
1041 if isinstance(qname
, (str, unicode)):
1042 qname
= dns
.name
.from_text(qname
)
1043 if isinstance(rdtype
, (str, unicode)):
1044 rdtype
= dns
.rdatatype
.from_text(rdtype
)
1045 if isinstance(rdclass
, (str, unicode)):
1046 rdclass
= dns
.rdataclass
.from_text(rdclass
)
1048 m
.flags |
= dns
.flags
.RD
1049 m
.find_rrset(m
.question
, qname
, rdclass
, rdtype
, create
=True,
1051 m
.use_edns(use_edns
)
1052 m
.want_dnssec(want_dnssec
)
1055 def make_response(query
, recursion_available
=False, our_payload
=8192):
1056 """Make a message which is a response for the specified query.
1057 The message returned is really a response skeleton; it has all
1058 of the infrastructure required of a response, but none of the
1061 The response's question section is a shallow copy of the query's
1062 question section, so the query's question RRsets should not be
1065 @param query: the query to respond to
1066 @type query: dns.message.Message object
1067 @param recursion_available: should RA be set in the response?
1068 @type recursion_available: bool
1069 @param our_payload: payload size to advertise in EDNS responses; default
1071 @type our_payload: int
1072 @rtype: dns.message.Message object"""
1074 if query
.flags
& dns
.flags
.QR
:
1075 raise dns
.exception
.FormError('specified query message is not a query')
1076 response
= dns
.message
.Message(query
.id)
1077 response
.flags
= dns
.flags
.QR |
(query
.flags
& dns
.flags
.RD
)
1078 if recursion_available
:
1079 response
.flags |
= dns
.flags
.RA
1080 response
.set_opcode(query
.opcode())
1081 response
.question
= list(query
.question
)
1083 response
.use_edns(0, 0, our_payload
, query
.payload
)
1084 if not query
.keyname
is None:
1085 response
.keyname
= query
.keyname
1086 response
.keyring
= query
.keyring
1087 response
.request_mac
= query
.mac