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