1
# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
3
# Permission to use, copy, modify, and distribute this software and its
4
# documentation for any purpose with or without fee is hereby granted,
5
# provided that the above copyright notice and this permission notice
6
# appear in all copies.
8
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
9
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
11
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
14
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
38
class ShortHeader(dns.exception.FormError):
39
"""Raised if the DNS packet passed to from_wire() is too short."""
42
class TrailingJunk(dns.exception.FormError):
43
"""Raised if the DNS packet passed to from_wire() has extra junk
47
class UnknownHeaderField(dns.exception.DNSException):
48
"""Raised if a header field name is not recognized when converting from
49
text into a message."""
52
class BadEDNS(dns.exception.FormError):
53
"""Raised if an OPT record occurs somewhere other than the start of
54
the additional data section."""
57
class BadTSIG(dns.exception.FormError):
58
"""Raised if a TSIG record occurs somewhere other than the end of
59
the additional data section."""
62
class UnknownTSIGKey(dns.exception.DNSException):
63
"""Raised if we got a TSIG but don't know the key."""
66
class Message(object):
69
@ivar id: The query id; the default is a randomly chosen id.
71
@ivar flags: The DNS flags of the message. @see: RFC 1035 for an
72
explanation of these flags.
74
@ivar question: The question section.
75
@type question: list of dns.rrset.RRset objects
76
@ivar answer: The answer section.
77
@type answer: list of dns.rrset.RRset objects
78
@ivar authority: The authority section.
79
@type authority: list of dns.rrset.RRset objects
80
@ivar additional: The additional data section.
81
@type additional: list of dns.rrset.RRset objects
82
@ivar edns: The EDNS level to use. The default is -1, no Edns.
84
@ivar ednsflags: The EDNS flags
86
@ivar payload: The EDNS payload size. The default is 0.
88
@ivar options: The EDNS options
89
@type options: list of dns.edns.Option objects
90
@ivar request_payload: The associated request's EDNS payload size.
91
@type request_payload: int
92
@ivar keyring: The TSIG keyring to use. The default is None.
94
@ivar keyname: The TSIG keyname to use. The default is None.
95
@type keyname: dns.name.Name object
96
@ivar keyalgorithm: The TSIG algorithm to use; defaults to
97
dns.tsig.default_algorithm. Constants for TSIG algorithms are defined
98
in dns.tsig, and the currently implemented algorithms are
99
HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and
101
@type keyalgorithm: string
102
@ivar request_mac: The TSIG MAC of the request message associated with
103
this message; used when validating TSIG signatures. @see: RFC 2845 for
104
more information on TSIG fields.
105
@type request_mac: string
106
@ivar fudge: TSIG time fudge; default is 300 seconds.
108
@ivar original_id: TSIG original id; defaults to the message's id
109
@type original_id: int
110
@ivar tsig_error: TSIG error code; default is 0.
111
@type tsig_error: int
112
@ivar other_data: TSIG other data.
113
@type other_data: string
114
@ivar mac: The TSIG MAC for this message.
116
@ivar xfr: Is the message being used to contain the results of a DNS
117
zone transfer? The default is False.
119
@ivar origin: The origin of the zone in messages which are used for
120
zone transfers or for DNS dynamic updates. The default is None.
121
@type origin: dns.name.Name object
122
@ivar tsig_ctx: The TSIG signature context associated with this
123
message. The default is None.
124
@type tsig_ctx: hmac.HMAC object
125
@ivar had_tsig: Did the message decoded from wire format have a TSIG
128
@ivar multi: Is this message part of a multi-message sequence? The
129
default is false. This variable is used when validating TSIG signatures
130
on messages which are part of a zone transfer.
132
@ivar first: Is this message standalone, or the first of a multi
133
message sequence? This variable is used when validating TSIG signatures
134
on messages which are part of a zone transfer.
136
@ivar index: An index of rrsets in the message. The index key is
137
(section, name, rdclass, rdtype, covers, deleting). Indexing can be
138
disabled by setting the index to None.
142
def __init__(self, id=None):
144
self.id = dns.entropy.random_16()
156
self.request_payload = 0
159
self.keyalgorithm = dns.tsig.default_algorithm
160
self.request_mac = ''
164
self.original_id = self.id
169
self.had_tsig = False
175
return '<DNS message, ID ' + `self.id` + '>'
178
return self.to_text()
180
def to_text(self, origin=None, relativize=True, **kw):
181
"""Convert the message to text.
183
The I{origin}, I{relativize}, and any other keyword
184
arguments are passed to the rrset to_wire() method.
189
s = cStringIO.StringIO()
190
print >> s, 'id %d' % self.id
191
print >> s, 'opcode %s' % \
192
dns.opcode.to_text(dns.opcode.from_flags(self.flags))
193
rc = dns.rcode.from_flags(self.flags, self.ednsflags)
194
print >> s, 'rcode %s' % dns.rcode.to_text(rc)
195
print >> s, 'flags %s' % dns.flags.to_text(self.flags)
197
print >> s, 'edns %s' % self.edns
198
if self.ednsflags != 0:
199
print >> s, 'eflags %s' % \
200
dns.flags.edns_to_text(self.ednsflags)
201
print >> s, 'payload', self.payload
202
is_update = dns.opcode.is_update(self.flags)
206
print >> s, ';QUESTION'
207
for rrset in self.question:
208
print >> s, rrset.to_text(origin, relativize, **kw)
210
print >> s, ';PREREQ'
212
print >> s, ';ANSWER'
213
for rrset in self.answer:
214
print >> s, rrset.to_text(origin, relativize, **kw)
216
print >> s, ';UPDATE'
218
print >> s, ';AUTHORITY'
219
for rrset in self.authority:
220
print >> s, rrset.to_text(origin, relativize, **kw)
221
print >> s, ';ADDITIONAL'
222
for rrset in self.additional:
223
print >> s, rrset.to_text(origin, relativize, **kw)
225
# We strip off the final \n so the caller can print the result without
226
# doing weird things to get around eccentricities in Python print
229
return s.getvalue()[:-1]
231
def __eq__(self, other):
232
"""Two messages are equal if they have the same content in the
233
header, question, answer, and authority sections.
235
if not isinstance(other, Message):
237
if self.id != other.id:
239
if self.flags != other.flags:
241
for n in self.question:
242
if n not in other.question:
244
for n in other.question:
245
if n not in self.question:
247
for n in self.answer:
248
if n not in other.answer:
250
for n in other.answer:
251
if n not in self.answer:
253
for n in self.authority:
254
if n not in other.authority:
256
for n in other.authority:
257
if n not in self.authority:
261
def __ne__(self, other):
262
"""Are two messages not equal?
264
return not self.__eq__(other)
266
def is_response(self, other):
267
"""Is other a response to self?
269
if other.flags & dns.flags.QR == 0 or \
270
self.id != other.id or \
271
dns.opcode.from_flags(self.flags) != \
272
dns.opcode.from_flags(other.flags):
274
if dns.rcode.from_flags(other.flags, other.ednsflags) != \
277
if dns.opcode.is_update(self.flags):
279
for n in self.question:
280
if n not in other.question:
282
for n in other.question:
283
if n not in self.question:
287
def section_number(self, section):
288
if section is self.question:
290
elif section is self.answer:
292
elif section is self.authority:
294
elif section is self.additional:
297
raise ValueError('unknown section')
299
def find_rrset(self, section, name, rdclass, rdtype,
300
covers=dns.rdatatype.NONE, deleting=None, create=False,
302
"""Find the RRset with the given attributes in the specified section.
304
@param section: the section of the message to look in, e.g.
306
@type section: list of dns.rrset.RRset objects
307
@param name: the name of the RRset
308
@type name: dns.name.Name object
309
@param rdclass: the class of the RRset
311
@param rdtype: the type of the RRset
313
@param covers: the covers value of the RRset
315
@param deleting: the deleting value of the RRset
317
@param create: If True, create the RRset if it is not found.
318
The created RRset is appended to I{section}.
320
@param force_unique: If True and create is also True, create a
321
new RRset regardless of whether a matching RRset exists already.
322
@type force_unique: bool
323
@raises KeyError: the RRset was not found and create was False
324
@rtype: dns.rrset.RRset object"""
326
key = (self.section_number(section),
327
name, rdclass, rdtype, covers, deleting)
329
if not self.index is None:
330
rrset = self.index.get(key)
331
if not rrset is None:
334
for rrset in section:
335
if rrset.match(name, rdclass, rdtype, covers, deleting):
339
rrset = dns.rrset.RRset(name, rdclass, rdtype, covers, deleting)
340
section.append(rrset)
341
if not self.index is None:
342
self.index[key] = rrset
345
def get_rrset(self, section, name, rdclass, rdtype,
346
covers=dns.rdatatype.NONE, deleting=None, create=False,
348
"""Get the RRset with the given attributes in the specified section.
350
If the RRset is not found, None is returned.
352
@param section: the section of the message to look in, e.g.
354
@type section: list of dns.rrset.RRset objects
355
@param name: the name of the RRset
356
@type name: dns.name.Name object
357
@param rdclass: the class of the RRset
359
@param rdtype: the type of the RRset
361
@param covers: the covers value of the RRset
363
@param deleting: the deleting value of the RRset
365
@param create: If True, create the RRset if it is not found.
366
The created RRset is appended to I{section}.
368
@param force_unique: If True and create is also True, create a
369
new RRset regardless of whether a matching RRset exists already.
370
@type force_unique: bool
371
@rtype: dns.rrset.RRset object or None"""
374
rrset = self.find_rrset(section, name, rdclass, rdtype, covers,
375
deleting, create, force_unique)
380
def to_wire(self, origin=None, max_size=0, **kw):
381
"""Return a string containing the message in DNS compressed wire
384
Additional keyword arguments are passed to the rrset to_wire()
387
@param origin: The origin to be appended to any relative names.
388
@type origin: dns.name.Name object
389
@param max_size: The maximum size of the wire format output; default
390
is 0, which means 'the message's request payload, if nonzero, or
393
@raises dns.exception.TooBig: max_size was exceeded
398
if self.request_payload != 0:
399
max_size = self.request_payload
404
elif max_size > 65535:
406
r = dns.renderer.Renderer(self.id, self.flags, max_size, origin)
407
for rrset in self.question:
408
r.add_question(rrset.name, rrset.rdtype, rrset.rdclass)
409
for rrset in self.answer:
410
r.add_rrset(dns.renderer.ANSWER, rrset, **kw)
411
for rrset in self.authority:
412
r.add_rrset(dns.renderer.AUTHORITY, rrset, **kw)
414
r.add_edns(self.edns, self.ednsflags, self.payload, self.options)
415
for rrset in self.additional:
416
r.add_rrset(dns.renderer.ADDITIONAL, rrset, **kw)
418
if not self.keyname is None:
419
r.add_tsig(self.keyname, self.keyring[self.keyname],
420
self.fudge, self.original_id, self.tsig_error,
421
self.other_data, self.request_mac,
426
def use_tsig(self, keyring, keyname=None, fudge=300,
427
original_id=None, tsig_error=0, other_data='',
428
algorithm=dns.tsig.default_algorithm):
429
"""When sending, a TSIG signature using the specified keyring
430
and keyname should be added.
432
@param keyring: The TSIG keyring to use; defaults to None.
434
@param keyname: The name of the TSIG key to use; defaults to None.
435
The key must be defined in the keyring. If a keyring is specified
436
but a keyname is not, then the key used will be the first key in the
437
keyring. Note that the order of keys in a dictionary is not defined,
438
so applications should supply a keyname when a keyring is used, unless
439
they know the keyring contains only one key.
440
@type keyname: dns.name.Name or string
441
@param fudge: TSIG time fudge; default is 300 seconds.
443
@param original_id: TSIG original id; defaults to the message's id
444
@type original_id: int
445
@param tsig_error: TSIG error code; default is 0.
446
@type tsig_error: int
447
@param other_data: TSIG other data.
448
@type other_data: string
449
@param algorithm: The TSIG algorithm to use; defaults to
450
dns.tsig.default_algorithm
453
self.keyring = keyring
455
self.keyname = self.keyring.keys()[0]
457
if isinstance(keyname, (str, unicode)):
458
keyname = dns.name.from_text(keyname)
459
self.keyname = keyname
460
self.keyalgorithm = algorithm
462
if original_id is None:
463
self.original_id = self.id
465
self.original_id = original_id
466
self.tsig_error = tsig_error
467
self.other_data = other_data
469
def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None, options=None):
470
"""Configure EDNS behavior.
471
@param edns: The EDNS level to use. Specifying None, False, or -1
472
means 'do not use EDNS', and in this case the other parameters are
473
ignored. Specifying True is equivalent to specifying 0, i.e. 'use
475
@type edns: int or bool or None
476
@param ednsflags: EDNS flag values.
478
@param payload: The EDNS sender's payload field, which is the maximum
479
size of UDP datagram the sender can handle.
481
@param request_payload: The EDNS payload size to use when sending
482
this message. If not specified, defaults to the value of payload.
483
@type request_payload: int or None
484
@param options: The EDNS options
485
@type options: None or list of dns.edns.Option objects
488
if edns is None or edns is False:
492
if request_payload is None:
493
request_payload = payload
500
# make sure the EDNS version in ednsflags agrees with edns
501
ednsflags &= 0xFF00FFFFL
502
ednsflags |= (edns << 16)
506
self.ednsflags = ednsflags
507
self.payload = payload
508
self.options = options
509
self.request_payload = request_payload
511
def want_dnssec(self, wanted=True):
512
"""Enable or disable 'DNSSEC desired' flag in requests.
513
@param wanted: Is DNSSEC desired? If True, EDNS is enabled if
514
required, and then the DO bit is set. If False, the DO bit is
515
cleared if EDNS is enabled.
521
self.ednsflags |= dns.flags.DO
523
self.ednsflags &= ~dns.flags.DO
529
return dns.rcode.from_flags(self.flags, self.ednsflags)
531
def set_rcode(self, rcode):
533
@param rcode: the rcode
536
(value, evalue) = dns.rcode.to_flags(rcode)
539
self.ednsflags &= 0x00FFFFFFL
540
self.ednsflags |= evalue
541
if self.ednsflags != 0 and self.edns < 0:
545
"""Return the opcode.
548
return dns.opcode.from_flags(self.flags)
550
def set_opcode(self, opcode):
552
@param opcode: the opcode
556
self.flags |= dns.opcode.to_flags(opcode)
558
class _WireReader(object):
559
"""Wire format reader.
561
@ivar wire: the wire-format message.
563
@ivar message: The message object being built
564
@type message: dns.message.Message object
565
@ivar current: When building a message object from wire format, this
566
variable contains the offset from the beginning of wire of the next octet
569
@ivar updating: Is the message a dynamic update?
571
@ivar one_rr_per_rrset: Put each RR into its own RRset?
572
@type one_rr_per_rrset: bool
573
@ivar zone_rdclass: The class of the zone in messages which are
575
@type zone_rdclass: int
578
def __init__(self, wire, message, question_only=False,
579
one_rr_per_rrset=False):
581
self.message = message
583
self.updating = False
584
self.zone_rdclass = dns.rdataclass.IN
585
self.question_only = question_only
586
self.one_rr_per_rrset = one_rr_per_rrset
588
def _get_question(self, qcount):
589
"""Read the next I{qcount} records from the wire data and add them to
590
the question section.
591
@param qcount: the number of questions in the message
594
if self.updating and qcount > 1:
595
raise dns.exception.FormError
597
for i in xrange(0, qcount):
598
(qname, used) = dns.name.from_wire(self.wire, self.current)
599
if not self.message.origin is None:
600
qname = qname.relativize(self.message.origin)
601
self.current = self.current + used
602
(rdtype, rdclass) = \
604
self.wire[self.current:self.current + 4])
605
self.current = self.current + 4
606
self.message.find_rrset(self.message.question, qname,
607
rdclass, rdtype, create=True,
610
self.zone_rdclass = rdclass
612
def _get_section(self, section, count):
613
"""Read the next I{count} records from the wire data and add them to
614
the specified section.
615
@param section: the section of the message to which to add records
616
@type section: list of dns.rrset.RRset objects
617
@param count: the number of records to read
620
if self.updating or self.one_rr_per_rrset:
625
for i in xrange(0, count):
626
rr_start = self.current
627
(name, used) = dns.name.from_wire(self.wire, self.current)
629
if not self.message.origin is None:
630
name = name.relativize(self.message.origin)
631
self.current = self.current + used
632
(rdtype, rdclass, ttl, rdlen) = \
633
struct.unpack('!HHIH',
634
self.wire[self.current:self.current + 10])
635
self.current = self.current + 10
636
if rdtype == dns.rdatatype.OPT:
637
if not section is self.message.additional or seen_opt:
639
self.message.payload = rdclass
640
self.message.ednsflags = ttl
641
self.message.edns = (ttl & 0xff0000) >> 16
642
self.message.options = []
643
current = self.current
648
self.wire[current:current + 4])
649
current = current + 4
650
opt = dns.edns.option_from_wire(otype, self.wire, current, olen)
651
self.message.options.append(opt)
652
current = current + olen
653
optslen = optslen - 4 - olen
655
elif rdtype == dns.rdatatype.TSIG:
656
if not (section is self.message.additional and
659
if self.message.keyring is None:
660
raise UnknownTSIGKey('got signed message without keyring')
661
secret = self.message.keyring.get(absolute_name)
663
raise UnknownTSIGKey("key '%s' unknown" % name)
664
self.message.tsig_ctx = \
665
dns.tsig.validate(self.wire,
669
self.message.request_mac,
673
self.message.tsig_ctx,
676
self.message.had_tsig = True
680
if self.updating and \
681
(rdclass == dns.rdataclass.ANY or
682
rdclass == dns.rdataclass.NONE):
684
rdclass = self.zone_rdclass
687
if deleting == dns.rdataclass.ANY or \
688
(deleting == dns.rdataclass.NONE and \
689
section is self.message.answer):
690
covers = dns.rdatatype.NONE
693
rd = dns.rdata.from_wire(rdclass, rdtype, self.wire,
697
if self.message.xfr and rdtype == dns.rdatatype.SOA:
699
rrset = self.message.find_rrset(section, name,
700
rdclass, rdtype, covers,
701
deleting, True, force_unique)
704
self.current = self.current + rdlen
707
"""Read a wire format DNS message and build a dns.message.Message
713
(self.message.id, self.message.flags, qcount, ancount,
714
aucount, adcount) = struct.unpack('!HHHHHH', self.wire[:12])
716
if dns.opcode.is_update(self.message.flags):
718
self._get_question(qcount)
719
if self.question_only:
721
self._get_section(self.message.answer, ancount)
722
self._get_section(self.message.authority, aucount)
723
self._get_section(self.message.additional, adcount)
724
if self.current != l:
726
if self.message.multi and self.message.tsig_ctx and \
727
not self.message.had_tsig:
728
self.message.tsig_ctx.update(self.wire)
731
def from_wire(wire, keyring=None, request_mac='', xfr=False, origin=None,
732
tsig_ctx = None, multi = False, first = True,
733
question_only = False, one_rr_per_rrset = False):
734
"""Convert a DNS wire format message into a message
737
@param keyring: The keyring to use if the message is signed.
739
@param request_mac: If the message is a response to a TSIG-signed request,
740
I{request_mac} should be set to the MAC of that request.
741
@type request_mac: string
742
@param xfr: Is this message part of a zone transfer?
744
@param origin: If the message is part of a zone transfer, I{origin}
745
should be the origin name of the zone.
746
@type origin: dns.name.Name object
747
@param tsig_ctx: The ongoing TSIG context, used when validating zone
749
@type tsig_ctx: hmac.HMAC object
750
@param multi: Is this message part of a multiple message sequence?
752
@param first: Is this message standalone, or the first of a multi
755
@param question_only: Read only up to the end of the question section?
756
@type question_only: bool
757
@param one_rr_per_rrset: Put each RR into its own RRset
758
@type one_rr_per_rrset: bool
759
@raises ShortHeader: The message is less than 12 octets long.
760
@raises TrailingJunk: There were octets in the message past the end
761
of the proper DNS message.
762
@raises BadEDNS: An OPT record was in the wrong section, or occurred more
764
@raises BadTSIG: A TSIG record was not the last record of the additional
766
@rtype: dns.message.Message object"""
770
m.request_mac = request_mac
773
m.tsig_ctx = tsig_ctx
777
reader = _WireReader(wire, m, question_only, one_rr_per_rrset)
783
class _TextReader(object):
784
"""Text format reader.
786
@ivar tok: the tokenizer
787
@type tok: dns.tokenizer.Tokenizer object
788
@ivar message: The message object being built
789
@type message: dns.message.Message object
790
@ivar updating: Is the message a dynamic update?
792
@ivar zone_rdclass: The class of the zone in messages which are
794
@type zone_rdclass: int
795
@ivar last_name: The most recently read name when building a message object
797
@type last_name: dns.name.Name object
800
def __init__(self, text, message):
801
self.message = message
802
self.tok = dns.tokenizer.Tokenizer(text)
803
self.last_name = None
804
self.zone_rdclass = dns.rdataclass.IN
805
self.updating = False
807
def _header_line(self, section):
808
"""Process one line from the text format header section."""
810
token = self.tok.get()
813
self.message.id = self.tok.get_int()
814
elif what == 'flags':
816
token = self.tok.get()
817
if not token.is_identifier():
818
self.tok.unget(token)
820
self.message.flags = self.message.flags | \
821
dns.flags.from_text(token.value)
822
if dns.opcode.is_update(self.message.flags):
825
self.message.edns = self.tok.get_int()
826
self.message.ednsflags = self.message.ednsflags | \
827
(self.message.edns << 16)
828
elif what == 'eflags':
829
if self.message.edns < 0:
830
self.message.edns = 0
832
token = self.tok.get()
833
if not token.is_identifier():
834
self.tok.unget(token)
836
self.message.ednsflags = self.message.ednsflags | \
837
dns.flags.edns_from_text(token.value)
838
elif what == 'payload':
839
self.message.payload = self.tok.get_int()
840
if self.message.edns < 0:
841
self.message.edns = 0
842
elif what == 'opcode':
843
text = self.tok.get_string()
844
self.message.flags = self.message.flags | \
845
dns.opcode.to_flags(dns.opcode.from_text(text))
846
elif what == 'rcode':
847
text = self.tok.get_string()
848
self.message.set_rcode(dns.rcode.from_text(text))
850
raise UnknownHeaderField
853
def _question_line(self, section):
854
"""Process one line from the text format question section."""
856
token = self.tok.get(want_leading = True)
857
if not token.is_whitespace():
858
self.last_name = dns.name.from_text(token.value, None)
859
name = self.last_name
860
token = self.tok.get()
861
if not token.is_identifier():
862
raise dns.exception.SyntaxError
865
rdclass = dns.rdataclass.from_text(token.value)
866
token = self.tok.get()
867
if not token.is_identifier():
868
raise dns.exception.SyntaxError
869
except dns.exception.SyntaxError:
870
raise dns.exception.SyntaxError
872
rdclass = dns.rdataclass.IN
874
rdtype = dns.rdatatype.from_text(token.value)
875
self.message.find_rrset(self.message.question, name,
876
rdclass, rdtype, create=True,
879
self.zone_rdclass = rdclass
882
def _rr_line(self, section):
883
"""Process one line from the text format answer, authority, or
884
additional data sections.
889
token = self.tok.get(want_leading = True)
890
if not token.is_whitespace():
891
self.last_name = dns.name.from_text(token.value, None)
892
name = self.last_name
893
token = self.tok.get()
894
if not token.is_identifier():
895
raise dns.exception.SyntaxError
898
ttl = int(token.value, 0)
899
token = self.tok.get()
900
if not token.is_identifier():
901
raise dns.exception.SyntaxError
902
except dns.exception.SyntaxError:
903
raise dns.exception.SyntaxError
908
rdclass = dns.rdataclass.from_text(token.value)
909
token = self.tok.get()
910
if not token.is_identifier():
911
raise dns.exception.SyntaxError
912
if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE:
914
rdclass = self.zone_rdclass
915
except dns.exception.SyntaxError:
916
raise dns.exception.SyntaxError
918
rdclass = dns.rdataclass.IN
920
rdtype = dns.rdatatype.from_text(token.value)
921
token = self.tok.get()
922
if not token.is_eol_or_eof():
923
self.tok.unget(token)
924
rd = dns.rdata.from_text(rdclass, rdtype, self.tok, None)
928
covers = dns.rdatatype.NONE
929
rrset = self.message.find_rrset(section, name,
930
rdclass, rdtype, covers,
931
deleting, True, self.updating)
936
"""Read a text format DNS message and build a dns.message.Message
939
line_method = self._header_line
942
token = self.tok.get(True, True)
943
if token.is_eol_or_eof():
945
if token.is_comment():
946
u = token.value.upper()
948
line_method = self._header_line
949
elif u == 'QUESTION' or u == 'ZONE':
950
line_method = self._question_line
951
section = self.message.question
952
elif u == 'ANSWER' or u == 'PREREQ':
953
line_method = self._rr_line
954
section = self.message.answer
955
elif u == 'AUTHORITY' or u == 'UPDATE':
956
line_method = self._rr_line
957
section = self.message.authority
958
elif u == 'ADDITIONAL':
959
line_method = self._rr_line
960
section = self.message.additional
963
self.tok.unget(token)
968
"""Convert the text format message into a message object.
970
@param text: The text format message.
972
@raises UnknownHeaderField:
973
@raises dns.exception.SyntaxError:
974
@rtype: dns.message.Message object"""
976
# 'text' can also be a file, but we don't publish that fact
977
# since it's an implementation detail. The official file
978
# interface is from_file().
982
reader = _TextReader(text, m)
988
"""Read the next text format message from the specified file.
990
@param f: file or string. If I{f} is a string, it is treated
991
as the name of a file to open.
992
@raises UnknownHeaderField:
993
@raises dns.exception.SyntaxError:
994
@rtype: dns.message.Message object"""
996
if sys.hexversion >= 0x02030000:
997
# allow Unicode filenames; turn on universal newline support
998
str_type = basestring
1003
if isinstance(f, str_type):
1016
def make_query(qname, rdtype, rdclass = dns.rdataclass.IN, use_edns=None,
1018
"""Make a query message.
1020
The query name, type, and class may all be specified either
1021
as objects of the appropriate type, or as strings.
1023
The query will have a randomly choosen query id, and its DNS flags
1024
will be set to dns.flags.RD.
1026
@param qname: The query name.
1027
@type qname: dns.name.Name object or string
1028
@param rdtype: The desired rdata type.
1030
@param rdclass: The desired rdata class; the default is class IN.
1032
@param use_edns: The EDNS level to use; the default is None (no EDNS).
1033
See the description of dns.message.Message.use_edns() for the possible
1034
values for use_edns and their meanings.
1035
@type use_edns: int or bool or None
1036
@param want_dnssec: Should the query indicate that DNSSEC is desired?
1037
@type want_dnssec: bool
1038
@rtype: dns.message.Message object"""
1040
if isinstance(qname, (str, unicode)):
1041
qname = dns.name.from_text(qname)
1042
if isinstance(rdtype, (str, unicode)):
1043
rdtype = dns.rdatatype.from_text(rdtype)
1044
if isinstance(rdclass, (str, unicode)):
1045
rdclass = dns.rdataclass.from_text(rdclass)
1047
m.flags |= dns.flags.RD
1048
m.find_rrset(m.question, qname, rdclass, rdtype, create=True,
1050
m.use_edns(use_edns)
1051
m.want_dnssec(want_dnssec)
1054
def make_response(query, recursion_available=False, our_payload=8192):
1055
"""Make a message which is a response for the specified query.
1056
The message returned is really a response skeleton; it has all
1057
of the infrastructure required of a response, but none of the
1060
The response's question section is a shallow copy of the query's
1061
question section, so the query's question RRsets should not be
1064
@param query: the query to respond to
1065
@type query: dns.message.Message object
1066
@param recursion_available: should RA be set in the response?
1067
@type recursion_available: bool
1068
@param our_payload: payload size to advertise in EDNS responses; default
1070
@type our_payload: int
1071
@rtype: dns.message.Message object"""
1073
if query.flags & dns.flags.QR:
1074
raise dns.exception.FormError('specified query message is not a query')
1075
response = dns.message.Message(query.id)
1076
response.flags = dns.flags.QR | (query.flags & dns.flags.RD)
1077
if recursion_available:
1078
response.flags |= dns.flags.RA
1079
response.set_opcode(query.opcode())
1080
response.question = list(query.question)
1082
response.use_edns(0, 0, our_payload, query.payload)
1083
if not query.keyname is None:
1084
response.keyname = query.keyname
1085
response.keyring = query.keyring
1086
response.request_mac = query.mac