dnspython  1.16.0
About: dnspython is a DNS toolkit (for Python 2.x) that supports almost all record types.
  Fossies Dox: dnspython-1.16.0.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

message.py
Go to the documentation of this file.
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 
20 from __future__ import absolute_import
21 
22 from io import StringIO
23 import struct
24 import time
25 
26 import dns.edns
27 import dns.exception
28 import dns.flags
29 import dns.name
30 import dns.opcode
31 import dns.entropy
32 import dns.rcode
33 import dns.rdata
34 import dns.rdataclass
35 import dns.rdatatype
36 import dns.rrset
37 import dns.renderer
38 import dns.tsig
39 import dns.wiredata
40 
41 from ._compat import long, xrange, string_types
42 
43 
45  """The DNS packet passed to from_wire() is too short."""
46 
47 
49  """The DNS packet passed to from_wire() has extra junk at the end of it."""
50 
51 
53  """The header field name was not recognized when converting from text
54  into a message."""
55 
56 
58  """An OPT record occurred somewhere other than the start of
59  the additional data section."""
60 
61 
63  """A TSIG record occurred somewhere other than the end of
64  the additional data section."""
65 
66 
68  """A TSIG with an unknown key was received."""
69 
70 
71 #: The question section number
72 QUESTION = 0
73 
74 #: The answer section number
75 ANSWER = 1
76 
77 #: The authority section number
78 AUTHORITY = 2
79 
80 #: The additional section number
81 ADDITIONAL = 3
82 
83 class Message(object):
84  """A DNS message."""
85 
86  def __init__(self, id=None):
87  if id is None:
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 = []
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' %
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' %
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 
585 class _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):
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:
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
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) = \
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 
759 def 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 
828 class _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
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 | \
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():
903  # Class
904  try:
905  rdclass = dns.rdataclass.from_text(token.value)
906  token = self.tok.get()
907  if not token.is_identifier():
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():
936  # TTL
937  try:
938  ttl = int(token.value, 0)
939  token = self.tok.get()
940  if not token.is_identifier():
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():
952  if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE:
953  deleting = rdclass
954  rdclass = self.zone_rdclass
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 
1007 def 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 
1031 def 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 
1061 def 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 
1138 def 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
dns.message.Message.xfr
xfr
Definition: message.py:110
dns.opcode.is_update
def is_update(flags)
Definition: opcode.py:111
dns.renderer.Renderer
Definition: renderer.py:36
dns.message.Message.edns
edns
Definition: message.py:96
dns.message.Message.__repr__
def __repr__(self)
Definition: message.py:118
dns.flags.from_text
def from_text(text)
Definition: flags.py:93
dns.exception.FormError
Definition: exception.py:109
dns.exception.SyntaxError
Definition: exception.py:113
dns.entropy
Definition: entropy.py:1
dns.message._WireReader.one_rr_per_rrset
one_rr_per_rrset
Definition: message.py:608
dns.message.Message.section_from_number
def section_from_number(self, number)
Definition: message.py:264
dns.message.Message.index
index
Definition: message.py:116
dns.message.Message.original_id
original_id
Definition: message.py:108
dns.message._TextReader.updating
updating
Definition: message.py:845
dns.message.Message.use_edns
def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None, options=None)
Definition: message.py:487
dns.message.Message.want_dnssec
def want_dnssec(self, wanted=True)
Definition: message.py:533
dns.tsig
Definition: tsig.py:1
dns.message._WireReader.updating
updating
Definition: message.py:605
dns.message.Message.opcode
def opcode(self)
Definition: message.py:569
dns.message.Message.other_data
other_data
Definition: message.py:105
dns.message._WireReader._get_section
def _get_section(self, section, count)
Definition: message.py:635
dns.message.ShortHeader
Definition: message.py:44
dns.rrset
Definition: rrset.py:1
dns.name.from_text
def from_text(text, origin=root, idna_codec=None)
Definition: name.py:873
dns.message._TextReader._header_line
def _header_line(self, section)
Definition: message.py:847
dns.message._WireReader.read
def read(self)
Definition: message.py:734
dns.message.Message.question
question
Definition: message.py:92
dns.opcode.to_flags
def to_flags(value)
Definition: opcode.py:83
dns.message.Message.get_rrset
def get_rrset(self, section, name, rdclass, rdtype, covers=dns.rdatatype.NONE, deleting=None, create=False, force_unique=False)
Definition: message.py:348
dns.rdataclass
Definition: rdataclass.py:1
dns.opcode.to_text
def to_text(value)
Definition: opcode.py:95
dns.rcode.from_text
def from_text(text)
Definition: rcode.py:74
dns.message.Message.request_mac
request_mac
Definition: message.py:104
dns.message.UnknownHeaderField
Definition: message.py:52
dns.message._WireReader.question_only
question_only
Definition: message.py:607
dns.message.Message.section_number
def section_number(self, section)
Definition: message.py:241
dns.rcode
Definition: rcode.py:1
dns.exception.DNSException
Definition: exception.py:24
dns.message.Message.options
options
Definition: message.py:99
dns.message.Message.flags
flags
Definition: message.py:91
dns.message._WireReader._get_question
def _get_question(self, qcount)
Definition: message.py:612
dns.opcode.from_flags
def from_flags(flags)
Definition: opcode.py:72
dns.message.Message.origin
origin
Definition: message.py:111
dns.message.from_text
def from_text(text)
Definition: message.py:1007
dns.flags.edns_to_text
def edns_to_text(flags)
Definition: flags.py:123
dns.message.BadTSIG
Definition: message.py:62
dns.message.Message.tsig_ctx
tsig_ctx
Definition: message.py:112
dns.message._WireReader.__init__
def __init__(self, wire, message, question_only=False, one_rr_per_rrset=False, ignore_trailing=False)
Definition: message.py:601
dns.message.Message.answer
answer
Definition: message.py:93
dns.renderer
Definition: renderer.py:1
dns.message._TextReader.zone_rdclass
zone_rdclass
Definition: message.py:844
dns.message._WireReader.wire
wire
Definition: message.py:602
dns.message._TextReader.__init__
def __init__(self, text, message)
Definition: message.py:840
dns.message.Message.set_rcode
def set_rcode(self, rcode)
Definition: message.py:556
dns.rdata.from_text
def from_text(rdclass, rdtype, tok, origin=None, relativize=True)
Definition: rdata.py:344
dns.message.from_wire
def from_wire(wire, keyring=None, request_mac=b'', xfr=False, origin=None, tsig_ctx=None, multi=False, first=True, question_only=False, one_rr_per_rrset=False, ignore_trailing=False)
Definition: message.py:759
dns.name.from_wire
def from_wire(message, current)
Definition: name.py:945
dns.rcode.to_text
def to_text(value)
Definition: rcode.py:129
dns.message.Message.authority
authority
Definition: message.py:94
dns.flags
Definition: flags.py:1
dns.edns.option_from_wire
def option_from_wire(otype, wire, current, olen)
Definition: edns.py:253
dns.message.Message.rcode
def rcode(self)
Definition: message.py:549
dns.message.Message.__init__
def __init__(self, id=None)
Definition: message.py:86
dns.message._TextReader.read
def read(self)
Definition: message.py:975
dns.rcode.from_flags
def from_flags(flags, ednsflags)
Definition: rcode.py:94
dns.hash.get
def get(algorithm)
Definition: hash.py:36
dns.message.BadEDNS
Definition: message.py:57
dns.entropy.random_16
def random_16()
Definition: entropy.py:138
dns.message.from_file
def from_file(f)
Definition: message.py:1031
dns.wiredata
Definition: wiredata.py:1
dns.message.Message.keyname
keyname
Definition: message.py:102
dns.edns
Definition: edns.py:1
dns.message.Message.keyalgorithm
keyalgorithm
Definition: message.py:103
dns.tokenizer.Tokenizer
Definition: tokenizer.py:152
dns.name
Definition: name.py:1
dns.message.Message.id
id
Definition: message.py:88
dns.message.Message.set_opcode
def set_opcode(self, opcode)
Definition: message.py:576
dns.message.Message.mac
mac
Definition: message.py:109
dns.message._TextReader._question_line
def _question_line(self, section)
Definition: message.py:893
dns.message.Message.find_rrset
def find_rrset(self, section, name, rdclass, rdtype, covers=dns.rdatatype.NONE, deleting=None, create=False, force_unique=False)
Definition: message.py:287
dns.message._WireReader.zone_rdclass
zone_rdclass
Definition: message.py:606
dns.message._TextReader._rr_line
def _rr_line(self, section)
Definition: message.py:922
dns.message.Message.to_wire
def to_wire(self, origin=None, max_size=0, **kw)
Definition: message.py:392
dns.message._WireReader.message
message
Definition: message.py:603
dns.message.Message
Definition: message.py:83
dns.message.UnknownTSIGKey
Definition: message.py:67
dns.message.Message.additional
additional
Definition: message.py:95
dns.message._TextReader.tok
tok
Definition: message.py:842
dns.message.Message.request_payload
request_payload
Definition: message.py:100
dns.rdata
Definition: rdata.py:1
dns.rdatatype
Definition: rdatatype.py:1
dns.message.Message.__str__
def __str__(self)
Definition: message.py:121
dns.message.Message.multi
multi
Definition: message.py:114
dns.opcode
Definition: opcode.py:1
dns.message._TextReader
Definition: message.py:828
dns.message.Message.had_tsig
had_tsig
Definition: message.py:113
dns.message.Message.__ne__
def __ne__(self, other)
Definition: message.py:214
dns.message._TextReader.last_name
last_name
Definition: message.py:843
dns.flags.edns_from_text
def edns_from_text(text)
Definition: flags.py:113
dns.rcode.to_flags
def to_flags(value)
Definition: rcode.py:112
dns.message.Message.payload
payload
Definition: message.py:98
dns.message.Message.fudge
fudge
Definition: message.py:107
dns.rdatatype.from_text
def from_text(text)
Definition: rdatatype.py:193
dns.message.Message.__eq__
def __eq__(self, other)
Definition: message.py:181
dns.message._TextReader.message
message
Definition: message.py:841
dns.message.Message.is_response
def is_response(self, other)
Definition: message.py:217
dns.message._WireReader
Definition: message.py:585
dns.message._WireReader.ignore_trailing
ignore_trailing
Definition: message.py:609
dns.flags.to_text
def to_text(flags)
Definition: flags.py:103
dns.rrset.RRset
Definition: rrset.py:28
dns.rdata.from_wire
def from_wire(rdclass, rdtype, wire, current, rdlen, origin=None)
Definition: rdata.py:394
dns.tsig.validate
def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata, tsig_rdlen, ctx=None, multi=False, first=True)
Definition: tsig.py:150
dns.message.Message.to_text
def to_text(self, origin=None, relativize=True, **kw)
Definition: message.py:124
dns.message.Message.first
first
Definition: message.py:115
dns._compat.xrange
xrange
Definition: _compat.py:11
dns.message.Message.tsig_error
tsig_error
Definition: message.py:106
dns.tsig.get_algorithm_and_mac
def get_algorithm_and_mac(wire, tsig_rdata, tsig_rdlen)
Definition: tsig.py:222
dns.message.make_response
def make_response(query, recursion_available=False, our_payload=8192, fudge=300)
Definition: message.py:1138
dns.message.Message.ednsflags
ednsflags
Definition: message.py:97
dns.message.make_query
def make_query(qname, rdtype, rdclass=dns.rdataclass.IN, use_edns=None, want_dnssec=False, ednsflags=None, payload=None, request_payload=None, options=None)
Definition: message.py:1061
dns._compat.long
long
Definition: _compat.py:10
dns.exception
Definition: exception.py:1
dns.message.TrailingJunk
Definition: message.py:48
dns.message.Message.keyring
keyring
Definition: message.py:101
dns.rdataclass.from_text
def from_text(text)
Definition: rdataclass.py:67
dns.message._WireReader.current
current
Definition: message.py:604
dns.message.Message.use_tsig
def use_tsig(self, keyring, keyname=None, fudge=300, original_id=None, tsig_error=0, other_data=b'', algorithm=dns.tsig.default_algorithm)
Definition: message.py:440
dns.opcode.from_text
def from_text(text)
Definition: opcode.py:52
dns.wiredata.maybe_wrap
def maybe_wrap(wire)
Definition: wiredata.py:96