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