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