Codebase list dnspython / c91d44e
Imported Upstream version 1.8.0 SVN-Git Migration 8 years ago
99 changed file(s) with 1032 addition(s) and 719 deletion(s). Raw diff Collapse all Expand all
0 2010-01-13 Bob Halley <halley@dnspython.org>
1
2 * dns/dnssec.py: Added RSASHA256 and RSASHA512 codepoints; added
3 other missing codepoints to _algorithm_by_text.
4
5 2010-01-12 Bob Halley <halley@dnspython.org>
6
7 * Escapes in masterfiles now work correctly. Previously they were
8 only working correctly when the text involved was part of a domain
9 name.
10
11 * dns/tokenizer.py: The tokenizer's get() method now returns Token
12 objects, not (type, text) tuples.
13
14 2009-11-13 Bob Halley <halley@dnspython.org>
15
16 * Support has been added for hmac-sha1, hmac-sha224, hmac-sha256,
17 hmac-sha384 and hmac-sha512. Thanks to Kevin Chen for a
18 thoughtful, high quality patch.
19
20 * dns/update.py (Update::present): A zero TTL was not added if
21 present() was called with a single rdata, causing _add() to be
22 unhappy. Thanks to Eugene Kim for reporting the problem and
23 submitting a patch.
24
25 * dns/entropy.py: Use os.urandom() if present. Don't seed until
26 someone wants randomness.
27
28 2009-09-16 Bob Halley <halley@dnspython.org>
29
30 * dns/entropy.py: The entropy module needs locking in order to be
31 used safely in a multithreaded environment. Thanks to Beda Kosata
32 for reporting the problem.
33
34 2009-07-27 Bob Halley <halley@dnspython.org>
35
36 * dns/query.py (xfr): The socket was not set to nonblocking mode.
37 Thanks to Erik Romijn for reporting this problem.
38
39 2009-07-23 Bob Halley <halley@dnspython.org>
40
41 * dns/rdtypes/IN/SRV.py (SRV._cmp): SRV records were compared
42 incorrectly due to a cut-and-paste error. Thanks to Tommie
43 Gannert for reporting this bug.
44
45 * dns/e164.py (query): The resolver parameter was not used.
46 Thanks to Matías Bellone for reporting this bug.
47
48 2009-06-23 Bob Halley <halley@dnspython.org>
49
50 * dns/entropy.py (EntropyPool.__init__): open /dev/random unbuffered;
51 there's no need to consume more randomness than we need. Thanks
52 to Brian Wellington for the patch.
53
054 2009-06-19 Bob Halley <halley@dnspython.org>
155
256 * (Version 1.7.1 released)
00 Metadata-Version: 1.1
11 Name: dnspython
2 Version: 1.7.1
2 Version: 1.8.0
33 Summary: DNS toolkit
44 Home-page: http://www.dnspython.org
55 Author: Bob Halley
66 Author-email: halley@dnspython.org
77 License: BSD-like
8 Download-URL: http://www.dnspython.org/kits/1.7.1/dnspython-1.7.1.tar.gz
8 Download-URL: http://www.dnspython.org/kits/1.8.0/dnspython-1.8.0.tar.gz
99 Description: dnspython is a DNS toolkit for Python. It supports almost all
1010 record types. It can be used for queries, zone transfers, and dynamic
1111 updates. It supports TSIG authenticated messages and EDNS0.
2121
2222 ABOUT THIS RELEASE
2323
24 This is dnspython 1.7.1.
24 This is dnspython 1.8.0
25
26 New since 1.7.1:
27
28 Support for hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384
29 and hmac-sha512 has been contributed by Kevin Chen.
30
31 The tokenizer's tokens are now Token objects instead of (type,
32 value) tuples.
33
34 Bugs fixed since 1.7.1:
35
36 Escapes in masterfiles now work correctly. Previously they
37 were only working correctly when the text involved was part of
38 a domain name.
39
40 When constructing a DDNS update, if the present() method was
41 used with a single rdata, a zero TTL was not added.
42
43 The entropy pool needed locking to be thread safe.
44
45 The entropy pool's reading of /dev/random could cause
46 dnspython to block.
47
48 The entropy pool did buffered reads, potentially consuming more
49 randomness than we needed.
50
51 The entropy pool did not seed with high quality randomness on
52 Windows.
53
54 SRV records were compared incorrectly.
55
56 In the e164 query function, the resolver parameter was not
57 used.
2558
2659 New since 1.7.0:
2760
2121 RSASHA1 = 5
2222 DSANSEC3SHA1 = 6
2323 RSASHA1NSEC3SHA1 = 7
24 RSASHA256 = 8
25 RSASHA512 = 10
2426 INDIRECT = 252
2527 PRIVATEDNS = 253
2628 PRIVATEOID = 254
3032 'DH' : DH,
3133 'DSA' : DSA,
3234 'ECC' : ECC,
35 'RSASHA1' : RSASHA1,
36 'DSANSEC3SHA1' : DSANSEC3SHA1,
37 'RSASHA1NSEC3SHA1' : RSASHA1NSEC3SHA1,
38 'RSASHA256' : RSASHA256,
39 'RSASHA512' : RSASHA512,
3340 'INDIRECT' : INDIRECT,
3441 'PRIVATEDNS' : PRIVATEDNS,
3542 'PRIVATEOID' : PRIVATEOID,
5353 name = name.relativize(origin)
5454 dlabels = [d for d in name.labels if (d.isdigit() and len(d) == 1)]
5555 if len(dlabels) != len(name.labels):
56 raise dns.exception.SyntaxError, 'non-digit labels in ENUM domain name'
56 raise dns.exception.SyntaxError('non-digit labels in ENUM domain name')
5757 dlabels.reverse()
5858 text = ''.join(dlabels)
5959 if want_plus_prefix:
7272 domain = dns.name.from_text(domain)
7373 qname = dns.e164.from_e164(number, domain)
7474 try:
75 return dns.resolver.query(qname, 'NAPTR')
75 return resolver.query(qname, 'NAPTR')
7676 except dns.resolver.NXDOMAIN:
7777 pass
7878 raise dns.resolver.NXDOMAIN
1212 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
1313 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1414
15 import os
1516 import time
17 try:
18 import threading as _threading
19 except ImportError:
20 import dummy_threading as _threading
1621
1722 class EntropyPool(object):
1823 def __init__(self, seed=None):
1924 self.pool_index = 0
2025 self.digest = None
2126 self.next_byte = 0
22 if seed is None:
23 try:
24 r = file('/dev/random')
25 try:
26 seed = r.read(16)
27 finally:
28 r.close()
29 except:
30 seed = str(time.time())
27 self.lock = _threading.Lock()
3128 try:
3229 import hashlib
3330 self.hash = hashlib.sha1()
4239 self.hash = md5.new()
4340 self.hash_len = 16
4441 self.pool = '\0' * self.hash_len
45 self.stir(seed)
42 if not seed is None:
43 self.stir(seed)
44 self.seeded = True
45 else:
46 self.seeded = False
4647
47 def stir(self, entropy):
48 bytes = [ord(c) for c in self.pool]
49 for c in entropy:
50 if self.pool_index == self.hash_len:
51 self.pool_index = 0
52 b = ord(c) & 0xff
53 bytes[self.pool_index] ^= b
54 self.pool_index += 1
55 self.pool = ''.join([chr(c) for c in bytes])
48 def stir(self, entropy, already_locked=False):
49 if not already_locked:
50 self.lock.acquire()
51 try:
52 bytes = [ord(c) for c in self.pool]
53 for c in entropy:
54 if self.pool_index == self.hash_len:
55 self.pool_index = 0
56 b = ord(c) & 0xff
57 bytes[self.pool_index] ^= b
58 self.pool_index += 1
59 self.pool = ''.join([chr(c) for c in bytes])
60 finally:
61 if not already_locked:
62 self.lock.release()
63
64 def _maybe_seed(self):
65 if not self.seeded:
66 try:
67 seed = os.urandom(16)
68 except:
69 try:
70 r = file('/dev/urandom', 'r', 0)
71 try:
72 seed = r.read(16)
73 finally:
74 r.close()
75 except:
76 seed = str(time.time())
77 self.seeded = True
78 self.stir(seed, True)
5679
5780 def random_8(self):
58 if self.digest is None or self.next_byte == self.hash_len:
59 self.hash.update(self.pool)
60 self.digest = self.hash.digest()
61 self.stir(self.digest)
62 self.next_byte = 0
63 value = ord(self.digest[self.next_byte])
64 self.next_byte += 1
81 self.lock.acquire()
82 self._maybe_seed()
83 try:
84 if self.digest is None or self.next_byte == self.hash_len:
85 self.hash.update(self.pool)
86 self.digest = self.hash.digest()
87 self.stir(self.digest, True)
88 self.next_byte = 0
89 value = ord(self.digest[self.next_byte])
90 self.next_byte += 1
91 finally:
92 self.lock.release()
6593 return value
6694
6795 def random_16(self):
73101 def random_between(self, first, last):
74102 size = last - first + 1
75103 if size > 4294967296L:
76 raise ValueError, 'too big'
104 raise ValueError('too big')
77105 if size > 65536:
78106 rand = self.random_32
79107 max = 4294967295L
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2001-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
3131 """
3232
3333 if len(address) != 16:
34 raise ValueError, "IPv6 addresses are 16 bytes long"
34 raise ValueError("IPv6 addresses are 16 bytes long")
3535 hex = address.encode('hex_codec')
3636 chunks = []
3737 i = 0
9494
9595 def inet_aton(text):
9696 """Convert a text format IPv6 address into network format.
97
97
9898 @param text: the textual address
9999 @type text: string
100100 @rtype: string
101101 @raises dns.exception.SyntaxError: the text was not properly formatted
102102 """
103
103
104104 #
105105 # Our aim here is not something fast; we just want something that works.
106106 #
0 # Copyright (C) 2001-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
9191 @type keyring: dict
9292 @ivar keyname: The TSIG keyname to use. The default is None.
9393 @type keyname: dns.name.Name object
94 @ivar keyalgorithm: The TSIG key algorithm to use. The default is
95 dns.tsig.default_algorithm.
96 @type keyalgorithm: string
9497 @ivar request_mac: The TSIG MAC of the request message associated with
9598 this message; used when validating TSIG signatures. @see: RFC 2845 for
9699 more information on TSIG fields.
148151 self.request_payload = 0
149152 self.keyring = None
150153 self.keyname = None
154 self.keyalgorithm = dns.tsig.default_algorithm
151155 self.request_mac = ''
152156 self.other_data = ''
153157 self.tsig_error = 0
285289 elif section is self.additional:
286290 return 3
287291 else:
288 raise ValueError, 'unknown section'
292 raise ValueError('unknown section')
289293
290294 def find_rrset(self, section, name, rdclass, rdtype,
291295 covers=dns.rdatatype.NONE, deleting=None, create=False,
409413 if not self.keyname is None:
410414 r.add_tsig(self.keyname, self.keyring[self.keyname],
411415 self.fudge, self.original_id, self.tsig_error,
412 self.other_data, self.request_mac)
416 self.other_data, self.request_mac,
417 self.keyalgorithm)
413418 self.mac = r.mac
414419 return r.get_wire()
415420
416 def use_tsig(self, keyring, keyname=None, fudge=300, original_id=None,
417 tsig_error=0, other_data=''):
421 def use_tsig(self, keyring, keyname=None, fudge=300,
422 original_id=None, tsig_error=0, other_data='',
423 algorithm=dns.tsig.default_algorithm):
418424 """When sending, a TSIG signature using the specified keyring
419425 and keyname should be added.
420426
435441 @type tsig_error: int
436442 @param other_data: TSIG other data.
437443 @type other_data: string
444 @param algorithm: The TSIG algorithm to use; defaults to
445 dns.tsig.default_algorithm
438446 """
439447
440448 self.keyring = keyring
444452 if isinstance(keyname, (str, unicode)):
445453 keyname = dns.name.from_text(keyname)
446454 self.keyname = keyname
455 self.keyalgorithm = algorithm
447456 self.fudge = fudge
448457 if original_id is None:
449458 self.original_id = self.id
643652 i == (count - 1)):
644653 raise BadTSIG
645654 if self.message.keyring is None:
646 raise UnknownTSIGKey, 'got signed message without keyring'
655 raise UnknownTSIGKey('got signed message without keyring')
647656 secret = self.message.keyring.get(absolute_name)
648657 if secret is None:
649 raise UnknownTSIGKey, "key '%s' unknown" % name
658 raise UnknownTSIGKey("key '%s' unknown" % name)
650659 self.message.tsig_ctx = \
651660 dns.tsig.validate(self.wire,
652661 absolute_name,
793802 def _header_line(self, section):
794803 """Process one line from the text format header section."""
795804
796 (ttype, what) = self.tok.get()
805 token = self.tok.get()
806 what = token.value
797807 if what == 'id':
798808 self.message.id = self.tok.get_int()
799809 elif what == 'flags':
800810 while True:
801811 token = self.tok.get()
802 if token[0] != dns.tokenizer.IDENTIFIER:
812 if not token.is_identifier():
803813 self.tok.unget(token)
804814 break
805815 self.message.flags = self.message.flags | \
806 dns.flags.from_text(token[1])
816 dns.flags.from_text(token.value)
807817 if dns.opcode.is_update(self.message.flags):
808818 self.updating = True
809819 elif what == 'edns':
815825 self.message.edns = 0
816826 while True:
817827 token = self.tok.get()
818 if token[0] != dns.tokenizer.IDENTIFIER:
828 if not token.is_identifier():
819829 self.tok.unget(token)
820830 break
821831 self.message.ednsflags = self.message.ednsflags | \
822 dns.flags.edns_from_text(token[1])
832 dns.flags.edns_from_text(token.value)
823833 elif what == 'payload':
824834 self.message.payload = self.tok.get_int()
825835 if self.message.edns < 0:
839849 """Process one line from the text format question section."""
840850
841851 token = self.tok.get(want_leading = True)
842 if token[0] != dns.tokenizer.WHITESPACE:
843 self.last_name = dns.name.from_text(token[1], None)
852 if not token.is_whitespace():
853 self.last_name = dns.name.from_text(token.value, None)
844854 name = self.last_name
845855 token = self.tok.get()
846 if token[0] != dns.tokenizer.IDENTIFIER:
856 if not token.is_identifier():
847857 raise dns.exception.SyntaxError
848858 # Class
849859 try:
850 rdclass = dns.rdataclass.from_text(token[1])
860 rdclass = dns.rdataclass.from_text(token.value)
851861 token = self.tok.get()
852 if token[0] != dns.tokenizer.IDENTIFIER:
862 if not token.is_identifier():
853863 raise dns.exception.SyntaxError
854864 except dns.exception.SyntaxError:
855865 raise dns.exception.SyntaxError
856866 except:
857867 rdclass = dns.rdataclass.IN
858868 # Type
859 rdtype = dns.rdatatype.from_text(token[1])
869 rdtype = dns.rdatatype.from_text(token.value)
860870 self.message.find_rrset(self.message.question, name,
861871 rdclass, rdtype, create=True,
862872 force_unique=True)
872882 deleting = None
873883 # Name
874884 token = self.tok.get(want_leading = True)
875 if token[0] != dns.tokenizer.WHITESPACE:
876 self.last_name = dns.name.from_text(token[1], None)
885 if not token.is_whitespace():
886 self.last_name = dns.name.from_text(token.value, None)
877887 name = self.last_name
878888 token = self.tok.get()
879 if token[0] != dns.tokenizer.IDENTIFIER:
889 if not token.is_identifier():
880890 raise dns.exception.SyntaxError
881891 # TTL
882892 try:
883 ttl = int(token[1], 0)
893 ttl = int(token.value, 0)
884894 token = self.tok.get()
885 if token[0] != dns.tokenizer.IDENTIFIER:
895 if not token.is_identifier():
886896 raise dns.exception.SyntaxError
887897 except dns.exception.SyntaxError:
888898 raise dns.exception.SyntaxError
890900 ttl = 0
891901 # Class
892902 try:
893 rdclass = dns.rdataclass.from_text(token[1])
903 rdclass = dns.rdataclass.from_text(token.value)
894904 token = self.tok.get()
895 if token[0] != dns.tokenizer.IDENTIFIER:
905 if not token.is_identifier():
896906 raise dns.exception.SyntaxError
897907 if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE:
898908 deleting = rdclass
902912 except:
903913 rdclass = dns.rdataclass.IN
904914 # Type
905 rdtype = dns.rdatatype.from_text(token[1])
915 rdtype = dns.rdatatype.from_text(token.value)
906916 token = self.tok.get()
907 if token[0] != dns.tokenizer.EOL and token[0] != dns.tokenizer.EOF:
917 if not token.is_eol_or_eof():
908918 self.tok.unget(token)
909919 rd = dns.rdata.from_text(rdclass, rdtype, self.tok, None)
910920 covers = rd.covers()
925935 section = None
926936 while 1:
927937 token = self.tok.get(True, True)
928 if token[0] == dns.tokenizer.EOL or token[0] == dns.tokenizer.EOF:
938 if token.is_eol_or_eof():
929939 break
930 if token[0] == dns.tokenizer.COMMENT:
931 u = token[1].upper()
940 if token.is_comment():
941 u = token.value.upper()
932942 if u == 'HEADER':
933943 line_method = self._header_line
934944 elif u == 'QUESTION' or u == 'ZONE':
10561066 @rtype: dns.message.Message object"""
10571067
10581068 if query.flags & dns.flags.QR:
1059 raise dns.exception.FormError, 'specified query message is not a query'
1069 raise dns.exception.FormError('specified query message is not a query')
10601070 response = dns.message.Message(query.id)
10611071 response.flags = dns.flags.QR | (query.flags & dns.flags.RD)
10621072 if recursion_available:
0 # Copyright (C) 2001-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
106106 @raises LabelTooLong: an individual label is too long
107107 @raises EmptyLabel: a label is empty (i.e. the root label) and appears
108108 in a position other than the end of the label sequence"""
109
109
110110 l = len(labels)
111111 total = 0
112112 i = -1
129129
130130 The dns.name.Name class represents a DNS name as a tuple of labels.
131131 Instances of the class are immutable.
132
132
133133 @ivar labels: The tuple of labels in the name. Each label is a string of
134134 up to 63 octets."""
135135
136136 __slots__ = ['labels']
137
137
138138 def __init__(self, labels):
139139 """Initialize a domain name from a list of labels.
140140 @param labels: the labels
141141 @type labels: any iterable whose values are strings
142142 """
143
143
144144 super(Name, self).__setattr__('labels', tuple(labels))
145145 _validate_labels(self.labels)
146146
147147 def __setattr__(self, name, value):
148 raise TypeError, "object doesn't support attribute assignment"
148 raise TypeError("object doesn't support attribute assignment")
149149
150150 def is_absolute(self):
151151 """Is the most significant label of this name the root label?
152152 @rtype: bool
153153 """
154
154
155155 return len(self.labels) > 0 and self.labels[-1] == ''
156156
157157 def is_wild(self):
158158 """Is this name wild? (I.e. Is the least significant label '*'?)
159159 @rtype: bool
160160 """
161
161
162162 return len(self.labels) > 0 and self.labels[0] == '*'
163163
164164 def __hash__(self):
165165 """Return a case-insensitive hash of the name.
166166 @rtype: int
167167 """
168
168
169169 h = 0L
170170 for label in self.labels:
171171 for c in label:
184184 0 if self == other. A relative name is always less than an
185185 absolute name. If both names have the same relativity, then
186186 the DNSSEC order relation is used to order them.
187
187
188188 I{nlabels} is the number of significant labels that the two names
189189 have in common.
190190 """
239239 The notion of subdomain includes equality.
240240 @rtype: bool
241241 """
242
242
243243 (nr, o, nl) = self.fullcompare(other)
244244 if nr == NAMERELN_SUBDOMAIN or nr == NAMERELN_EQUAL:
245245 return True
251251 The notion of subdomain includes equality.
252252 @rtype: bool
253253 """
254
254
255255 (nr, o, nl) = self.fullcompare(other)
256256 if nr == NAMERELN_SUPERDOMAIN or nr == NAMERELN_EQUAL:
257257 return True
262262 DNSSEC canonical form.
263263 @rtype: dns.name.Name object
264264 """
265
265
266266 return Name([x.lower() for x in self.labels])
267267
268268 def __eq__(self, other):
303303
304304 def __repr__(self):
305305 return '<DNS name ' + self.__str__() + '>'
306
306
307307 def __str__(self):
308308 return self.to_text(False)
309309
313313 root label) for absolute names. The default is False.
314314 @rtype: string
315315 """
316
316
317317 if len(self.labels) == 0:
318318 return '@'
319319 if len(self.labels) == 1 and self.labels[0] == '':
334334 root label) for absolute names. The default is False.
335335 @rtype: string
336336 """
337
337
338338 if len(self.labels) == 0:
339339 return u'@'
340340 if len(self.labels) == 1 and self.labels[0] == '':
350350 """Convert name to a format suitable for digesting in hashes.
351351
352352 The name is canonicalized and converted to uncompressed wire format.
353
353
354354 @param origin: If the name is relative and origin is not None, then
355355 origin will be appended to it.
356356 @type origin: dns.name.Name object
359359 if it is missing, then this exception is raised
360360 @rtype: string
361361 """
362
362
363363 if not self.is_absolute():
364364 if origin is None or not origin.is_absolute():
365365 raise NeedAbsoluteNameOrOrigin
369369 labels = self.labels
370370 dlabels = ["%s%s" % (chr(len(x)), x.lower()) for x in labels]
371371 return ''.join(dlabels)
372
372
373373 def to_wire(self, file = None, compress = None, origin = None):
374374 """Convert name to wire format, possibly compressing it.
375375
430430 """The length of the name (in labels).
431431 @rtype: int
432432 """
433
433
434434 return len(self.labels)
435435
436436 def __getitem__(self, index):
455455 @returns: the tuple (prefix, suffix)
456456 @rtype: tuple
457457 """
458
458
459459 l = len(self.labels)
460460 if depth == 0:
461461 return (self, dns.name.empty)
462462 elif depth == l:
463463 return (dns.name.empty, self)
464464 elif depth < 0 or depth > l:
465 raise ValueError, \
466 'depth must be >= 0 and <= the length of the name'
465 raise ValueError('depth must be >= 0 and <= the length of the name')
467466 return (Name(self[: -depth]), Name(self[-depth :]))
468467
469468 def concatenate(self, other):
472471 @raises AbsoluteConcatenation: self is absolute and other is
473472 not the empty name
474473 """
475
474
476475 if self.is_absolute() and len(other) > 0:
477476 raise AbsoluteConcatenation
478477 labels = list(self.labels)
484483 relative to origin. Otherwise return self.
485484 @rtype: dns.name.Name object
486485 """
487
486
488487 if not origin is None and self.is_subdomain(origin):
489488 return Name(self[: -len(origin)])
490489 else:
495494 concatenation of self and origin. Otherwise return self.
496495 @rtype: dns.name.Name object
497496 """
498
497
499498 if not self.is_absolute():
500499 return self.concatenate(origin)
501500 else:
508507 false the name is derelativized.
509508 @rtype: dns.name.Name object
510509 """
511
510
512511 if origin:
513512 if relativize:
514513 return self.relativize(origin)
526525 if self == root or self == empty:
527526 raise NoParent
528527 return Name(self.labels[1:])
529
528
530529 root = Name([''])
531530 empty = Name([])
532531
534533 """Convert unicode text into a Name object.
535534
536535 Lables are encoded in IDN ACE form.
537
536
538537 @rtype: dns.name.Name object
539538 """
540539
541540 if not isinstance(text, unicode):
542 raise ValueError, "input to from_unicode() must be a unicode string"
541 raise ValueError("input to from_unicode() must be a unicode string")
543542 if not (origin is None or isinstance(origin, Name)):
544 raise ValueError, "origin must be a Name or None"
543 raise ValueError("origin must be a Name or None")
545544 labels = []
546545 label = u''
547546 escaping = False
601600 if isinstance(text, unicode) and sys.hexversion >= 0x02030000:
602601 return from_unicode(text, origin)
603602 else:
604 raise ValueError, "input to from_text() must be a string"
603 raise ValueError("input to from_text() must be a string")
605604 if not (origin is None or isinstance(origin, Name)):
606 raise ValueError, "origin must be a Name or None"
605 raise ValueError("origin must be a Name or None")
607606 labels = []
608607 label = ''
609608 escaping = False
669668 """
670669
671670 if not isinstance(message, str):
672 raise ValueError, "input to from_wire() must be a byte string"
671 raise ValueError("input to from_wire() must be a byte string")
673672 labels = []
674673 biggest_pointer = current
675674 hops = 0
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
3030
3131 def __setitem__(self, key, value):
3232 if not isinstance(key, dns.name.Name):
33 raise ValueError, 'NameDict key must be a name'
33 raise ValueError('NameDict key must be a name')
3434 depth = len(key)
3535 if depth > self.max_depth:
3636 self.max_depth = depth
3838
3939 def get_deepest_match(self, name):
4040 """Find the deepest match to I{name} in the dictionary.
41
41
4242 The deepest match is the longest name in the dictionary which is
4343 a superdomain of I{name}.
4444
4646 @type name: dns.name.Name object
4747 @rtype: (key, value) tuple
4848 """
49
49
5050 depth = len(name)
5151 if depth > self.max_depth:
5252 depth = self.max_depth
0 # Copyright (C) 2001-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2001-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
139139 from_address[1:] == destination[1:]):
140140 break
141141 if not ignore_unexpected:
142 raise UnexpectedSource, \
143 'got a response from %s instead of %s' % (from_address,
144 destination)
142 raise UnexpectedSource('got a response from '
143 '%s instead of %s' % (from_address,
144 destination))
145145 finally:
146146 s.close()
147147 r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
185185 if v[0] != errno.EINPROGRESS and \
186186 v[0] != errno.EWOULDBLOCK and \
187187 v[0] != errno.EALREADY:
188 raise ty, v
188 raise v
189189
190190 def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0,
191191 one_rr_per_rrset=False):
257257 def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN,
258258 timeout=None, port=53, keyring=None, keyname=None, relativize=True,
259259 af=None, lifetime=None, source=None, source_port=0, serial=0,
260 use_udp=False):
260 use_udp=False, keyalgorithm=dns.tsig.default_algorithm):
261261 """Return a generator for the responses to a zone transfer.
262262
263263 @param where: where to send the message
302302 @type serial: int
303303 @param use_udp: Use UDP (only meaningful for IXFR)
304304 @type use_udp: bool
305 @param keyalgorithm: The TSIG algorithm to use; defaults to
306 dns.tsig.default_algorithm
307 @type keyalgorithm: string
305308 """
306309
307310 if isinstance(zone, (str, unicode)):
314317 '. . %u 0 0 0 0' % serial)
315318 q.authority.append(rrset)
316319 if not keyring is None:
317 q.use_tsig(keyring, keyname)
320 q.use_tsig(keyring, keyname, algorithm=keyalgorithm)
318321 wire = q.to_wire()
319322 if af is None:
320323 try:
331334 source = (source, source_port, 0, 0)
332335 if use_udp:
333336 if rdtype != dns.rdatatype.IXFR:
334 raise ValueError, 'cannot do a UDP AXFR'
337 raise ValueError('cannot do a UDP AXFR')
335338 s = socket.socket(af, socket.SOCK_DGRAM, 0)
336339 else:
337340 s = socket.socket(af, socket.SOCK_STREAM, 0)
341 s.setblocking(0)
338342 if source is not None:
339343 s.bind(source)
340344 expiration = _compute_expiration(lifetime)
382386 raise dns.exception.FormError
383387 rrset = r.answer[0]
384388 if rrset.rdtype != dns.rdatatype.SOA:
385 raise dns.exception.FormError, "first RRset is not an SOA"
389 raise dns.exception.FormError("first RRset is not an SOA")
386390 answer_index = 1
387391 soa_rrset = rrset.copy()
388392 if rdtype == dns.rdatatype.IXFR:
399403 #
400404 for rrset in r.answer[answer_index:]:
401405 if done:
402 raise dns.exception.FormError, "answers after final SOA"
406 raise dns.exception.FormError("answers after final SOA")
403407 if rrset.rdtype == dns.rdatatype.SOA and rrset.name == oname:
404408 if expecting_SOA:
405409 if rrset[0].serial != serial:
406 raise dns.exception.FormError, \
407 "IXFR base serial mismatch"
410 raise dns.exception.FormError("IXFR base serial mismatch")
408411 expecting_SOA = False
409412 elif rdtype == dns.rdatatype.IXFR:
410413 delete_mode = not delete_mode
419422 rdtype = dns.rdatatype.AXFR
420423 expecting_SOA = False
421424 if done and q.keyring and not r.had_tsig:
422 raise dns.exception.FormError, "missing TSIG"
425 raise dns.exception.FormError("missing TSIG")
423426 yield r
424427 s.close()
0 # Copyright (C) 2001-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
5757
5858 def from_text(text):
5959 """Convert text into an rcode.
60
60
6161 @param text: the texual rcode
6262 @type text: string
6363 @raises UnknownRcode: the rcode is unknown
8686
8787 value = (flags & 0x000f) | ((ednsflags >> 20) & 0xff0)
8888 if value < 0 or value > 4095:
89 raise ValueError, 'rcode must be >= 0 and <= 4095'
89 raise ValueError('rcode must be >= 0 and <= 4095')
9090 return value
9191
9292 def to_flags(value):
9999 """
100100
101101 if value < 0 or value > 4095:
102 raise ValueError, 'rcode must be >= 0 and <= 4095'
102 raise ValueError('rcode must be >= 0 and <= 4095')
103103 v = value & 0xf
104104 ev = long(value & 0xff0) << 20
105105 return (v, ev)
111111 @type value: int
112112 @rtype: string
113113 """
114
114
115115 text = _by_value.get(value)
116116 if text is None:
117117 text = str(value)
0 # Copyright (C) 2001-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
318318 return r'\# %d ' % len(self.data) + _hexify(self.data)
319319
320320 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
321 if tok.get_string() != r'\#':
322 raise dns.exception.SyntaxError, \
323 r'generic rdata does not start with \#'
321 token = tok.get()
322 if not token.is_identifier() or token.value != '\#':
323 raise dns.exception.SyntaxError(r'generic rdata does not start with \#')
324324 length = tok.get_int()
325325 chunks = []
326326 while 1:
327 (ttype, value) = tok.get()
328 if ttype == dns.tokenizer.EOL or ttype == dns.tokenizer.EOF:
327 token = tok.get()
328 if token.is_eol_or_eof():
329329 break
330 chunks.append(value)
330 chunks.append(token.value)
331331 hex = ''.join(chunks)
332332 data = hex.decode('hex_codec')
333333 if len(data) != length:
334 raise dns.exception.SyntaxError, \
335 'generic rdata hex data has wrong length'
334 raise dns.exception.SyntaxError('generic rdata hex data has wrong length')
336335 return cls(rdclass, rdtype, data)
337336
338337 from_text = classmethod(from_text)
414413 # peek at first token
415414 token = tok.get()
416415 tok.unget(token)
417 if token[0] == dns.tokenizer.IDENTIFIER and \
418 token[1] == r'\#':
416 if token.is_identifier() and \
417 token.value == r'\#':
419418 #
420419 # Known type using the generic syntax. Extract the
421420 # wire form from the generic syntax, and then run
0 # Copyright (C) 2001-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
7676 @raises dns.rdataclass.UnknownRdataClass: the class is unknown
7777 @raises ValueError: the rdata class value is not >= 0 and <= 65535
7878 """
79
79
8080 value = _by_text.get(text.upper())
8181 if value is None:
8282 match = _unknown_class_pattern.match(text)
8484 raise UnknownRdataclass
8585 value = int(match.group(1))
8686 if value < 0 or value > 65535:
87 raise ValueError, "class must be between >= 0 and <= 65535"
87 raise ValueError("class must be between >= 0 and <= 65535")
8888 return value
8989
9090 def to_text(value):
9696 """
9797
9898 if value < 0 or value > 65535:
99 raise ValueError, "class must be between >= 0 and <= 65535"
99 raise ValueError("class must be between >= 0 and <= 65535")
100100 text = _by_value.get(value)
101101 if text is None:
102102 text = 'CLASS' + `value`
107107 @param rdclass: the rdata class
108108 @type rdclass: int
109109 @rtype: bool"""
110
110
111111 if _metaclasses.has_key(rdclass):
112112 return True
113113 return False
0 # Copyright (C) 2001-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
5757 """
5858
5959 __slots__ = ['rdclass', 'rdtype', 'covers', 'ttl']
60
60
6161 def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE):
6262 """Create a new rdataset of the specified class and type.
6363
6464 @see: the description of the class instance variables for the
6565 meaning of I{rdclass} and I{rdtype}"""
66
66
6767 super(Rdataset, self).__init__()
6868 self.rdclass = rdclass
6969 self.rdtype = rdtype
8484 to the specified TTL.
8585 @param ttl: The TTL
8686 @type ttl: int"""
87
87
8888 if len(self) == 0:
8989 self.ttl = ttl
9090 elif ttl < self.ttl:
9595
9696 If the optional I{ttl} parameter is supplied, then
9797 self.update_ttl(ttl) will be called prior to adding the rdata.
98
98
9999 @param rd: The rdata
100100 @type rd: dns.rdata.Rdata object
101101 @param ttl: The TTL
102102 @type ttl: int"""
103
103
104104 #
105105 # If we're adding a signature, do some special handling to
106106 # check that the signature covers the same type as the
135135
136136 @param other: The rdataset from which to update
137137 @type other: dns.rdataset.Rdataset object"""
138
138
139139 self.update_ttl(other.ttl)
140140 super(Rdataset, self).update(other)
141141
146146 ctext = '(' + dns.rdatatype.to_text(self.covers) + ')'
147147 return '<DNS ' + dns.rdataclass.to_text(self.rdclass) + ' ' + \
148148 dns.rdatatype.to_text(self.rdtype) + ctext + ' rdataset>'
149
149
150150 def __str__(self):
151151 return self.to_text()
152152
154154 """Two rdatasets are equal if they have the same class, type, and
155155 covers, and contain the same rdata.
156156 @rtype: bool"""
157
157
158158 if not isinstance(other, Rdataset):
159159 return False
160160 if self.rdclass != other.rdclass or \
162162 self.covers != other.covers:
163163 return False
164164 return super(Rdataset, self).__eq__(other)
165
165
166166 def __ne__(self, other):
167167 return not self.__eq__(other)
168168
176176
177177 Any additional keyword arguments are passed on to the rdata
178178 to_text() method.
179
179
180180 @param name: If name is not None, emit a RRs with I{name} as
181181 the owner name.
182182 @type name: dns.name.Name object
263263 file.write(stuff)
264264 file.seek(0, 2)
265265 return len(self)
266
266
267267 def match(self, rdclass, rdtype, covers):
268268 """Returns True if this rdataset matches the specified class, type,
269269 and covers"""
290290 rd = dns.rdata.from_text(r.rdclass, r.rdtype, t)
291291 r.add(rd)
292292 return r
293
293
294294 def from_text(rdclass, rdtype, ttl, *text_rdatas):
295295 """Create an rdataset with the specified class, type, and TTL, and with
296296 the specified rdatas in text format.
297
297
298298 @rtype: dns.rdataset.Rdataset object
299299 """
300300
303303 def from_rdata_list(ttl, rdatas):
304304 """Create an rdataset with the specified TTL, and with
305305 the specified list of rdata objects.
306
306
307307 @rtype: dns.rdataset.Rdataset object
308308 """
309309
310310 if len(rdatas) == 0:
311 raise ValueError, "rdata list must not be empty"
311 raise ValueError("rdata list must not be empty")
312312 r = None
313313 for rd in rdatas:
314314 if r is None:
317317 first_time = False
318318 r.add(rd)
319319 return r
320
320
321321 def from_rdata(ttl, *rdatas):
322322 """Create an rdataset with the specified TTL, and with
323323 the specified rdata objects.
324
324
325325 @rtype: dns.rdataset.Rdataset object
326326 """
327327
0 # Copyright (C) 2001-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
193193 raise UnknownRdatatype
194194 value = int(match.group(1))
195195 if value < 0 or value > 65535:
196 raise ValueError, "type must be between >= 0 and <= 65535"
196 raise ValueError("type must be between >= 0 and <= 65535")
197197 return value
198198
199199 def to_text(value):
204204 @rtype: string"""
205205
206206 if value < 0 or value > 65535:
207 raise ValueError, "type must be between >= 0 and <= 65535"
207 raise ValueError("type must be between >= 0 and <= 65535")
208208 text = _by_value.get(value)
209209 if text is None:
210210 text = 'TYPE' + `value`
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
6262 @see: RFC 2538"""
6363
6464 __slots__ = ['certificate_type', 'key_tag', 'algorithm', 'certificate']
65
65
6666 def __init__(self, rdclass, rdtype, certificate_type, key_tag, algorithm,
6767 certificate):
6868 super(CERT, self).__init__(rdclass, rdtype)
7676 return "%s %d %s %s" % (certificate_type, self.key_tag,
7777 dns.dnssec.algorithm_to_text(self.algorithm),
7878 dns.rdata._base64ify(self.certificate))
79
79
8080 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
8181 certificate_type = _ctype_from_text(tok.get_string())
8282 key_tag = tok.get_uint16()
8383 algorithm = dns.dnssec.algorithm_from_text(tok.get_string())
8484 if algorithm < 0 or algorithm > 255:
85 raise dns.exception.SyntaxError, "bad algorithm type"
85 raise dns.exception.SyntaxError("bad algorithm type")
8686 chunks = []
8787 while 1:
88 t = tok.get()
89 if t[0] == dns.tokenizer.EOL or t[0] == dns.tokenizer.EOF:
88 t = tok.get().unescape()
89 if t.is_eol_or_eof():
9090 break
91 if t[0] != dns.tokenizer.IDENTIFIER:
91 if not t.is_identifier():
9292 raise dns.exception.SyntaxError
93 chunks.append(t[1])
93 chunks.append(t.value)
9494 b64 = ''.join(chunks)
9595 certificate = b64.decode('base64_codec')
9696 return cls(rdclass, rdtype, certificate_type, key_tag,
9797 algorithm, certificate)
98
98
9999 from_text = classmethod(from_text)
100100
101101 def to_wire(self, file, compress = None, origin = None):
103103 self.algorithm)
104104 file.write(prefix)
105105 file.write(self.certificate)
106
106
107107 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
108108 prefix = wire[current : current + 5]
109109 current += 5
126126 other.to_wire(f)
127127 wire2 = f.getvalue()
128128 f.close()
129
129
130130 return cmp(wire1, wire2)
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2004-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2009 Nominum, Inc.
0 # Copyright (C) 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
5757 algorithm = tok.get_uint8()
5858 hit = tok.get_string().decode('hex-codec')
5959 if len(hit) > 255:
60 raise dns.exception.SyntaxError, "HIT too long"
60 raise dns.exception.SyntaxError("HIT too long")
6161 key = tok.get_string().decode('base64-codec')
6262 servers = []
6363 while 1:
64 (ttype, value) = tok.get()
65 if ttype == dns.tokenizer.EOL or ttype == dns.tokenizer.EOF:
64 token = tok.get()
65 if token.is_eol_or_eof():
6666 break
67 server = dns.name.from_text(value, origin)
67 server = dns.name.from_text(token.value, origin)
6868 server.choose_relativity(origin, relativize)
6969 servers.append(server)
7070 return cls(rdclass, rdtype, hit, algorithm, key, servers)
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
2626 @see: RFC 1183"""
2727
2828 __slots__ = ['address', 'subaddress']
29
29
3030 def __init__(self, rdclass, rdtype, address, subaddress):
3131 super(ISDN, self).__init__(rdclass, rdtype)
3232 self.address = address
3838 dns.rdata._escapify(self.subaddress))
3939 else:
4040 return '"%s"' % dns.rdata._escapify(self.address)
41
41
4242 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
4343 address = tok.get_string()
4444 t = tok.get()
45 if t[0] != dns.tokenizer.EOL and t[0] != dns.tokenizer.EOF:
45 if not t.is_eol_or_eof():
4646 tok.unget(t)
4747 subaddress = tok.get_string()
4848 else:
5050 subaddress = ''
5151 tok.get_eol()
5252 return cls(rdclass, rdtype, address, subaddress)
53
53
5454 from_text = classmethod(from_text)
5555
5656 def to_wire(self, file, compress = None, origin = None):
6565 byte = chr(l)
6666 file.write(byte)
6767 file.write(self.subaddress)
68
68
6969 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
7070 l = ord(wire[current])
7171 current += 1
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
2828 exp = i - 1
2929 break
3030 if exp is None or exp < 0:
31 raise dns.exception.SyntaxError, "%s value out of bounds" % desc
31 raise dns.exception.SyntaxError("%s value out of bounds" % desc)
3232 return exp
3333
3434 def _float_to_tuple(what):
6868 def _decode_size(what, desc):
6969 exponent = what & 0x0F
7070 if exponent > 9:
71 raise dns.exception.SyntaxError, "bad %s exponent" % desc
71 raise dns.exception.SyntaxError("bad %s exponent" % desc)
7272 base = (what & 0xF0) >> 4
7373 if base > 9:
74 raise dns.exception.SyntaxError, "bad %s base" % desc
74 raise dns.exception.SyntaxError("bad %s base" % desc)
7575 return long(base) * pow(10, exponent)
7676
7777 class LOC(dns.rdata.Rdata):
9595
9696 __slots__ = ['latitude', 'longitude', 'altitude', 'size',
9797 'horizontal_precision', 'vertical_precision']
98
98
9999 def __init__(self, rdclass, rdtype, latitude, longitude, altitude,
100100 size=1.0, hprec=10000.0, vprec=10.0):
101101 """Initialize a LOC record instance.
104104 of integers specifying (degrees, minutes, seconds, milliseconds),
105105 or they may be floating point values specifying the number of
106106 degrees. The other parameters are floats."""
107
107
108108 super(LOC, self).__init__(rdclass, rdtype)
109109 if isinstance(latitude, int) or isinstance(latitude, long):
110110 latitude = float(latitude)
147147 self.vertical_precision / 100.0
148148 )
149149 return text
150
150
151151 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
152152 latitude = [0, 0, 0, 0]
153153 longitude = [0, 0, 0, 0]
154154 size = 1.0
155155 hprec = 10000.0
156156 vprec = 10.0
157
157
158158 latitude[0] = tok.get_int()
159159 t = tok.get_string()
160160 if t.isdigit():
163163 if '.' in t:
164164 (seconds, milliseconds) = t.split('.')
165165 if not seconds.isdigit():
166 raise dns.exception.SyntaxError, \
167 'bad latitude seconds value'
166 raise dns.exception.SyntaxError('bad latitude seconds value')
168167 latitude[2] = int(seconds)
169168 if latitude[2] >= 60:
170 raise dns.exception.SyntaxError, \
171 'latitude seconds >= 60'
169 raise dns.exception.SyntaxError('latitude seconds >= 60')
172170 l = len(milliseconds)
173171 if l == 0 or l > 3 or not milliseconds.isdigit():
174 raise dns.exception.SyntaxError, \
175 'bad latitude milliseconds value'
172 raise dns.exception.SyntaxError('bad latitude milliseconds value')
176173 if l == 1:
177174 m = 100
178175 elif l == 2:
187184 if t == 'S':
188185 latitude[0] *= -1
189186 elif t != 'N':
190 raise dns.exception.SyntaxError, 'bad latitude hemisphere value'
187 raise dns.exception.SyntaxError('bad latitude hemisphere value')
191188
192189 longitude[0] = tok.get_int()
193190 t = tok.get_string()
197194 if '.' in t:
198195 (seconds, milliseconds) = t.split('.')
199196 if not seconds.isdigit():
200 raise dns.exception.SyntaxError, \
201 'bad longitude seconds value'
197 raise dns.exception.SyntaxError('bad longitude seconds value')
202198 longitude[2] = int(seconds)
203199 if longitude[2] >= 60:
204 raise dns.exception.SyntaxError, \
205 'longitude seconds >= 60'
200 raise dns.exception.SyntaxError('longitude seconds >= 60')
206201 l = len(milliseconds)
207202 if l == 0 or l > 3 or not milliseconds.isdigit():
208 raise dns.exception.SyntaxError, \
209 'bad longitude milliseconds value'
203 raise dns.exception.SyntaxError('bad longitude milliseconds value')
210204 if l == 1:
211205 m = 100
212206 elif l == 2:
221215 if t == 'W':
222216 longitude[0] *= -1
223217 elif t != 'E':
224 raise dns.exception.SyntaxError, 'bad longitude hemisphere value'
218 raise dns.exception.SyntaxError('bad longitude hemisphere value')
225219
226220 t = tok.get_string()
227221 if t[-1] == 'm':
228222 t = t[0 : -1]
229223 altitude = float(t) * 100.0 # m -> cm
230224
231 (ttype, value) = tok.get()
232 if ttype != dns.tokenizer.EOL and ttype != dns.tokenizer.EOF:
225 token = tok.get().unescape()
226 if not token.is_eol_or_eof():
227 value = token.value
233228 if value[-1] == 'm':
234229 value = value[0 : -1]
235230 size = float(value) * 100.0 # m -> cm
236 (ttype, value) = tok.get()
237 if ttype != dns.tokenizer.EOL and ttype != dns.tokenizer.EOF:
231 token = tok.get().unescape()
232 if not token.is_eol_or_eof():
233 value = token.value
238234 if value[-1] == 'm':
239235 value = value[0 : -1]
240236 hprec = float(value) * 100.0 # m -> cm
241 (ttype, value) = tok.get()
242 if ttype != dns.tokenizer.EOL and ttype != dns.tokenizer.EOF:
237 token = tok.get().unescape()
238 if not token.is_eol_or_eof():
239 value = token.value
243240 if value[-1] == 'm':
244241 value = value[0 : -1]
245242 vprec = float(value) * 100.0 # m -> cm
246 (ttype, value) = tok.get()
247 if ttype != dns.tokenizer.EOL and \
248 ttype != dns.tokenizer.EOF:
249 raise dns.exception.SyntaxError, \
250 "expected EOL or EOF"
243 tok.get_eol()
251244
252245 return cls(rdclass, rdtype, latitude, longitude, altitude,
253246 size, hprec, vprec)
254
247
255248 from_text = classmethod(from_text)
256249
257250 def to_wire(self, file, compress = None, origin = None):
261254 else:
262255 sign = 1
263256 degrees = long(self.latitude[0])
264 milliseconds = (degrees * 3600000 +
257 milliseconds = (degrees * 3600000 +
265258 self.latitude[1] * 60000 +
266259 self.latitude[2] * 1000 +
267260 self.latitude[3]) * sign
272265 else:
273266 sign = 1
274267 degrees = long(self.longitude[0])
275 milliseconds = (degrees * 3600000 +
268 milliseconds = (degrees * 3600000 +
276269 self.longitude[1] * 60000 +
277270 self.longitude[2] * 1000 +
278271 self.longitude[3]) * sign
284277 wire = struct.pack("!BBBBIII", 0, size, hprec, vprec, latitude,
285278 longitude, altitude)
286279 file.write(wire)
287
280
288281 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
289282 (version, size, hprec, vprec, latitude, longitude, altitude) = \
290283 struct.unpack("!BBBBIII", wire[current : current + rdlen])
293286 else:
294287 latitude = -1 * float(0x80000000L - latitude) / 3600000
295288 if latitude < -90.0 or latitude > 90.0:
296 raise dns.exception.FormError, "bad latitude"
289 raise dns.exception.FormError("bad latitude")
297290 if longitude > 0x80000000L:
298291 longitude = float(longitude - 0x80000000L) / 3600000
299292 else:
300293 longitude = -1 * float(0x80000000L - longitude) / 3600000
301294 if longitude < -180.0 or longitude > 180.0:
302 raise dns.exception.FormError, "bad longitude"
295 raise dns.exception.FormError("bad longitude")
303296 altitude = float(altitude) - 10000000.0
304297 size = _decode_size(size, "size")
305298 hprec = _decode_size(hprec, "horizontal precision")
318311 other.to_wire(f)
319312 wire2 = f.getvalue()
320313 f.close()
321
314
322315 return cmp(wire1, wire2)
323316
324317 def _get_float_latitude(self):
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2004-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
2828 @type windows: list of (window number, string) tuples"""
2929
3030 __slots__ = ['next', 'windows']
31
31
3232 def __init__(self, rdclass, rdtype, next, windows):
3333 super(NSEC, self).__init__(rdclass, rdtype)
3434 self.next = next
4747 i * 8 + j))
4848 text += (' ' + ' '.join(bits))
4949 return '%s%s' % (next, text)
50
50
5151 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
5252 next = tok.get_name()
5353 next = next.choose_relativity(origin, relativize)
5454 rdtypes = []
5555 while 1:
56 (ttype, value) = tok.get()
57 if ttype == dns.tokenizer.EOL or ttype == dns.tokenizer.EOF:
56 token = tok.get().unescape()
57 if token.is_eol_or_eof():
5858 break
59 nrdtype = dns.rdatatype.from_text(value)
59 nrdtype = dns.rdatatype.from_text(token.value)
6060 if nrdtype == 0:
61 raise dns.exception.SyntaxError, "NSEC with bit 0"
61 raise dns.exception.SyntaxError("NSEC with bit 0")
6262 if nrdtype > 65535:
63 raise dns.exception.SyntaxError, "NSEC with bit > 65535"
63 raise dns.exception.SyntaxError("NSEC with bit > 65535")
6464 rdtypes.append(nrdtype)
6565 rdtypes.sort()
6666 window = 0
8484 bitmap[byte] = chr(ord(bitmap[byte]) | (0x80 >> bit))
8585 windows.append((window, ''.join(bitmap[0:octets])))
8686 return cls(rdclass, rdtype, next, windows)
87
87
8888 from_text = classmethod(from_text)
8989
9090 def to_wire(self, file, compress = None, origin = None):
9393 file.write(chr(window))
9494 file.write(chr(len(bitmap)))
9595 file.write(bitmap)
96
96
9797 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
9898 (next, cused) = dns.name.from_wire(wire[: current + rdlen], current)
9999 current += cused
101101 windows = []
102102 while rdlen > 0:
103103 if rdlen < 3:
104 raise dns.exception.FormError, "NSEC too short"
104 raise dns.exception.FormError("NSEC too short")
105105 window = ord(wire[current])
106106 octets = ord(wire[current + 1])
107107 if octets == 0 or octets > 32:
108 raise dns.exception.FormError, "bad NSEC octets"
108 raise dns.exception.FormError("bad NSEC octets")
109109 current += 2
110110 rdlen -= 2
111111 if rdlen < octets:
112 raise dns.exception.FormError, "bad NSEC bitmap length"
112 raise dns.exception.FormError("bad NSEC bitmap length")
113113 bitmap = wire[current : current + octets]
114114 current += octets
115115 rdlen -= octets
122122
123123 def choose_relativity(self, origin = None, relativize = True):
124124 self.next = self.next.choose_relativity(origin, relativize)
125
125
126126 def _cmp(self, other):
127127 v = cmp(self.next, other.next)
128128 if v == 0:
0 # Copyright (C) 2004-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
9292 next = base64.b32decode(next)
9393 rdtypes = []
9494 while 1:
95 (ttype, value) = tok.get()
96 if ttype == dns.tokenizer.EOL or ttype == dns.tokenizer.EOF:
95 token = tok.get().unescape()
96 if token.is_eol_or_eof():
9797 break
98 nrdtype = dns.rdatatype.from_text(value)
98 nrdtype = dns.rdatatype.from_text(token.value)
9999 if nrdtype == 0:
100 raise dns.exception.SyntaxError, "NSEC3 with bit 0"
100 raise dns.exception.SyntaxError("NSEC3 with bit 0")
101101 if nrdtype > 65535:
102 raise dns.exception.SyntaxError, "NSEC3 with bit > 65535"
102 raise dns.exception.SyntaxError("NSEC3 with bit > 65535")
103103 rdtypes.append(nrdtype)
104104 rdtypes.sort()
105105 window = 0
156156 windows = []
157157 while rdlen > 0:
158158 if rdlen < 3:
159 raise dns.exception.FormError, "NSEC3 too short"
159 raise dns.exception.FormError("NSEC3 too short")
160160 window = ord(wire[current])
161161 octets = ord(wire[current + 1])
162162 if octets == 0 or octets > 32:
163 raise dns.exception.FormError, "bad NSEC3 octets"
163 raise dns.exception.FormError("bad NSEC3 octets")
164164 current += 2
165165 rdlen -= 2
166166 if rdlen < octets:
167 raise dns.exception.FormError, "bad NSEC3 bitmap length"
167 raise dns.exception.FormError("bad NSEC3 bitmap length")
168168 bitmap = wire[current : current + octets]
169169 current += octets
170170 rdlen -= octets
0 # Copyright (C) 2004-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
5252 '\x00', '\x00', '\x00', '\x00',
5353 '\x00', '\x00', '\x00', '\x00' ]
5454 while 1:
55 (ttype, value) = tok.get()
56 if ttype == dns.tokenizer.EOL or ttype == dns.tokenizer.EOF:
55 token = tok.get().unescape()
56 if token.is_eol_or_eof():
5757 break
58 if value.isdigit():
59 nrdtype = int(value)
58 if token.value.isdigit():
59 nrdtype = int(token.value)
6060 else:
61 nrdtype = dns.rdatatype.from_text(value)
61 nrdtype = dns.rdatatype.from_text(token.value)
6262 if nrdtype == 0:
63 raise dns.exception.SyntaxError, "NXT with bit 0"
63 raise dns.exception.SyntaxError("NXT with bit 0")
6464 if nrdtype > 127:
65 raise dns.exception.SyntaxError, "NXT with bit > 127"
65 raise dns.exception.SyntaxError("NXT with bit > 127")
6666 i = nrdtype // 8
6767 bitmap[i] = chr(ord(bitmap[i]) | (0x80 >> (nrdtype % 8)))
6868 bitmap = dns.rdata._truncate_bitmap(bitmap)
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2004-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2006, 2007, 2009 Nominum, Inc.
0 # Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2005-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2005-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
2424 @type address: string (in the standard "dotted quad" format)"""
2525
2626 __slots__ = ['address']
27
27
2828 def __init__(self, rdclass, rdtype, address):
2929 super(A, self).__init__(rdclass, rdtype)
3030 # check that it's OK
3333
3434 def to_text(self, origin=None, relativize=True, **kw):
3535 return self.address
36
36
3737 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
38 (ttype, address) = tok.get()
39 if ttype != dns.tokenizer.IDENTIFIER:
40 raise dns.exception.SyntaxError
41 t = tok.get_eol()
38 address = tok.get_identifier()
39 tok.get_eol()
4240 return cls(rdclass, rdtype, address)
43
41
4442 from_text = classmethod(from_text)
4543
4644 def to_wire(self, file, compress = None, origin = None):
4745 file.write(dns.ipv4.inet_aton(self.address))
48
46
4947 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
5048 address = dns.ipv4.inet_ntoa(wire[current : current + rdlen])
5149 return cls(rdclass, rdtype, address)
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
2424 @type address: string (in the standard IPv6 format)"""
2525
2626 __slots__ = ['address']
27
27
2828 def __init__(self, rdclass, rdtype, address):
2929 super(AAAA, self).__init__(rdclass, rdtype)
3030 # check that it's OK
3333
3434 def to_text(self, origin=None, relativize=True, **kw):
3535 return self.address
36
36
3737 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
38 (ttype, address) = tok.get()
39 if ttype != dns.tokenizer.IDENTIFIER:
40 raise dns.exception.SyntaxError
41 t = tok.get_eol()
38 address = tok.get_identifier()
39 tok.get_eol()
4240 return cls(rdclass, rdtype, address)
43
41
4442 from_text = classmethod(from_text)
4543
4644 def to_wire(self, file, compress = None, origin = None):
4745 file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.address))
48
46
4947 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
5048 address = dns.inet.inet_ntop(dns.inet.AF_INET6,
5149 wire[current : current + rdlen])
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
2222
2323 class APLItem(object):
2424 """An APL list item.
25
25
2626 @ivar family: the address family (IANA address family registry)
2727 @type family: int
2828 @ivar negation: is this item negated?
3434 """
3535
3636 __slots__ = ['family', 'negation', 'address', 'prefix']
37
37
3838 def __init__(self, family, negation, address, prefix):
3939 self.family = family
4040 self.negation = negation
7979 @see: RFC 3123"""
8080
8181 __slots__ = ['items']
82
82
8383 def __init__(self, rdclass, rdtype, items):
8484 super(APL, self).__init__(rdclass, rdtype)
8585 self.items = items
8686
8787 def to_text(self, origin=None, relativize=True, **kw):
8888 return ' '.join(map(lambda x: str(x), self.items))
89
89
9090 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
9191 items = []
9292 while 1:
93 (ttype, item) = tok.get()
94 if ttype == dns.tokenizer.EOL or ttype == dns.tokenizer.EOF:
93 token = tok.get().unescape()
94 if token.is_eol_or_eof():
9595 break
96 item = token.value
9697 if item[0] == '!':
9798 negation = True
9899 item = item[1:]
106107 items.append(item)
107108
108109 return cls(rdclass, rdtype, items)
109
110
110111 from_text = classmethod(from_text)
111112
112113 def to_wire(self, file, compress = None, origin = None):
113114 for item in self.items:
114115 item.to_wire(file)
115
116
116117 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
117118 items = []
118119 while 1:
164165 other.to_wire(f)
165166 wire2 = f.getvalue()
166167 f.close()
167
168
168169 return cmp(wire1, wire2)
0 # Copyright (C) 2006, 2007, 2009 Nominum, Inc.
0 # Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
2323 @see: RFC 4701"""
2424
2525 __slots__ = ['data']
26
26
2727 def __init__(self, rdclass, rdtype, data):
2828 super(DHCID, self).__init__(rdclass, rdtype)
2929 self.data = data
3434 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
3535 chunks = []
3636 while 1:
37 t = tok.get()
38 if t[0] == dns.tokenizer.EOL or t[0] == dns.tokenizer.EOF:
37 t = tok.get().unescape()
38 if t.is_eol_or_eof():
3939 break
40 if t[0] != dns.tokenizer.IDENTIFIER:
40 if not t.is_identifier():
4141 raise dns.exception.SyntaxError
42 chunks.append(t[1])
42 chunks.append(t.value)
4343 b64 = ''.join(chunks)
4444 data = b64.decode('base64_codec')
4545 return cls(rdclass, rdtype, data)
46
46
4747 from_text = classmethod(from_text)
4848
4949 def to_wire(self, file, compress = None, origin = None):
5050 file.write(self.data)
51
51
5252 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
5353 data = wire[current : current + rdlen]
5454 return cls(rdclass, rdtype, data)
5656 from_wire = classmethod(from_wire)
5757
5858 def _cmp(self, other):
59 return cmp(self.data, other.data)
59 return cmp(self.data, other.data)
0 # Copyright (C) 2006, 2007, 2009 Nominum, Inc.
0 # Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
4141 super(IPSECKEY, self).__init__(rdclass, rdtype)
4242 if gateway_type == 0:
4343 if gateway != '.' and not gateway is None:
44 raise SyntaxError, 'invalid gateway for gateway type 0'
44 raise SyntaxError('invalid gateway for gateway type 0')
4545 gateway = None
4646 elif gateway_type == 1:
4747 # check that it's OK
5252 elif gateway_type == 3:
5353 pass
5454 else:
55 raise SyntaxError, \
56 'invalid IPSECKEY gateway type: %d' % gateway_type
55 raise SyntaxError('invalid IPSECKEY gateway type: %d' % gateway_type)
5756 self.precedence = precedence
5857 self.gateway_type = gateway_type
5958 self.algorithm = algorithm
7069 elif self.gateway_type == 3:
7170 gateway = str(self.gateway.choose_relativity(origin, relativize))
7271 else:
73 raise ValueError, 'invalid gateway type'
72 raise ValueError('invalid gateway type')
7473 return '%d %d %d %s %s' % (self.precedence, self.gateway_type,
7574 self.algorithm, gateway,
7675 dns.rdata._base64ify(self.key))
8584 gateway = tok.get_string()
8685 chunks = []
8786 while 1:
88 t = tok.get()
89 if t[0] == dns.tokenizer.EOL or t[0] == dns.tokenizer.EOF:
87 t = tok.get().unescape()
88 if t.is_eol_or_eof():
9089 break
91 if t[0] != dns.tokenizer.IDENTIFIER:
90 if not t.is_identifier():
9291 raise dns.exception.SyntaxError
93 chunks.append(t[1])
92 chunks.append(t.value)
9493 b64 = ''.join(chunks)
9594 key = b64.decode('base64_codec')
9695 return cls(rdclass, rdtype, precedence, gateway_type, algorithm,
9796 gateway, key)
98
97
9998 from_text = classmethod(from_text)
10099
101100 def to_wire(self, file, compress = None, origin = None):
111110 elif self.gateway_type == 3:
112111 self.gateway.to_wire(file, None, origin)
113112 else:
114 raise ValueError, 'invalid gateway type'
113 raise ValueError('invalid gateway type')
115114 file.write(self.key)
116
115
117116 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
118117 if rdlen < 3:
119118 raise dns.exception.FormError
139138 current += cused
140139 rdlen -= cused
141140 else:
142 raise dns.exception.FormError, 'invalid IPSECKEY gateway type'
141 raise dns.exception.FormError('invalid IPSECKEY gateway type')
143142 key = wire[current : current + rdlen]
144143 return cls(rdclass, rdtype, header[0], gateway_type, header[2],
145144 gateway, key)
155154 other.to_wire(f)
156155 wire2 = f.getvalue()
157156 f.close()
158
157
159158 return cmp(wire1, wire2)
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
2424 @see: RFC 1706"""
2525
2626 __slots__ = ['address']
27
27
2828 def __init__(self, rdclass, rdtype, address):
2929 super(NSAP, self).__init__(rdclass, rdtype)
3030 self.address = address
3131
3232 def to_text(self, origin=None, relativize=True, **kw):
3333 return "0x%s" % self.address.encode('hex_codec')
34
34
3535 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
3636 address = tok.get_string()
3737 t = tok.get_eol()
3838 if address[0:2] != '0x':
39 raise dns.exception.SyntaxError, 'string does not start with 0x'
39 raise dns.exception.SyntaxError('string does not start with 0x')
4040 address = address[2:].replace('.', '')
4141 if len(address) % 2 != 0:
42 raise dns.exception.SyntaxError, 'hexstring has odd length'
42 raise dns.exception.SyntaxError('hexstring has odd length')
4343 address = address.decode('hex_codec')
4444 return cls(rdclass, rdtype, address)
45
45
4646 from_text = classmethod(from_text)
4747
4848 def to_wire(self, file, compress = None, origin = None):
4949 file.write(self.address)
50
50
5151 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
5252 address = wire[current : current + rdlen]
5353 return cls(rdclass, rdtype, address)
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
3232 @see: RFC 2782"""
3333
3434 __slots__ = ['priority', 'weight', 'port', 'target']
35
35
3636 def __init__(self, rdclass, rdtype, priority, weight, port, target):
3737 super(SRV, self).__init__(rdclass, rdtype)
3838 self.priority = priority
4444 target = self.target.choose_relativity(origin, relativize)
4545 return '%d %d %d %s' % (self.priority, self.weight, self.port,
4646 target)
47
47
4848 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
4949 priority = tok.get_uint16()
5050 weight = tok.get_uint16()
5353 target = target.choose_relativity(origin, relativize)
5454 tok.get_eol()
5555 return cls(rdclass, rdtype, priority, weight, port, target)
56
56
5757 from_text = classmethod(from_text)
5858
5959 def to_wire(self, file, compress = None, origin = None):
6060 three_ints = struct.pack("!HHH", self.priority, self.weight, self.port)
6161 file.write(three_ints)
6262 self.target.to_wire(file, compress, origin)
63
63
6464 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
6565 (priority, weight, port) = struct.unpack('!HHH',
6666 wire[current : current + 6])
8181
8282 def _cmp(self, other):
8383 sp = struct.pack("!HHH", self.priority, self.weight, self.port)
84 op = struct.pack("!HHH", other.priority, self.weight, self.port)
84 op = struct.pack("!HHH", other.priority, other.weight, other.port)
8585 v = cmp(sp, op)
8686 if v == 0:
8787 v = cmp(self.target, other.target)
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
3333 @see: RFC 1035"""
3434
3535 __slots__ = ['address', 'protocol', 'bitmap']
36
36
3737 def __init__(self, rdclass, rdtype, address, protocol, bitmap):
3838 super(WKS, self).__init__(rdclass, rdtype)
3939 self.address = address
4949 bits.append(str(i * 8 + j))
5050 text = ' '.join(bits)
5151 return '%s %d %s' % (self.address, self.protocol, text)
52
52
5353 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
5454 address = tok.get_string()
5555 protocol = tok.get_string()
5959 protocol = socket.getprotobyname(protocol)
6060 bitmap = []
6161 while 1:
62 (ttype, value) = tok.get()
63 if ttype == dns.tokenizer.EOL or ttype == dns.tokenizer.EOF:
62 token = tok.get().unescape()
63 if token.is_eol_or_eof():
6464 break
65 if value.isdigit():
66 serv = int(value)
65 if token.value.isdigit():
66 serv = int(token.value)
6767 else:
6868 if protocol != _proto_udp and protocol != _proto_tcp:
69 raise NotImplementedError, "protocol must be TCP or UDP"
69 raise NotImplementedError("protocol must be TCP or UDP")
7070 if protocol == _proto_udp:
7171 protocol_text = "udp"
7272 else:
7373 protocol_text = "tcp"
74 serv = socket.getservbyname(value, protocol_text)
74 serv = socket.getservbyname(token.value, protocol_text)
7575 i = serv // 8
7676 l = len(bitmap)
7777 if l < i + 1:
8080 bitmap[i] = chr(ord(bitmap[i]) | (0x80 >> (serv % 8)))
8181 bitmap = dns.rdata._truncate_bitmap(bitmap)
8282 return cls(rdclass, rdtype, address, protocol, bitmap)
83
83
8484 from_text = classmethod(from_text)
8585
8686 def to_wire(self, file, compress = None, origin = None):
8888 protocol = struct.pack('!B', self.protocol)
8989 file.write(protocol)
9090 file.write(self.bitmap)
91
91
9292 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
9393 address = dns.ipv4.inet_ntoa(wire[current : current + 4])
9494 protocol, = struct.unpack('!B', wire[current + 4 : current + 5])
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
5252 digest_type = tok.get_uint8()
5353 chunks = []
5454 while 1:
55 t = tok.get()
56 if t[0] == dns.tokenizer.EOL or t[0] == dns.tokenizer.EOF:
55 t = tok.get().unescape()
56 if t.is_eol_or_eof():
5757 break
58 if t[0] != dns.tokenizer.IDENTIFIER:
58 if not t.is_identifier():
5959 raise dns.exception.SyntaxError
60 chunks.append(t[1])
60 chunks.append(t.value)
6161 digest = ''.join(chunks)
6262 digest = digest.decode('hex_codec')
6363 return cls(rdclass, rdtype, key_tag, algorithm, digest_type,
0 # Copyright (C) 2004-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
6060 'IPSEC' : 4,
6161 'ALL' : 255,
6262 }
63
63
6464 class KEYBase(dns.rdata.Rdata):
6565 """KEY-like record base
6666
7474 @type key: string"""
7575
7676 __slots__ = ['flags', 'protocol', 'algorithm', 'key']
77
77
7878 def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key):
7979 super(KEYBase, self).__init__(rdclass, rdtype)
8080 self.flags = flags
9696 for flag in flag_names:
9797 v = _flags_from_text.get(flag)
9898 if v is None:
99 raise dns.exception.SyntaxError, 'unknown flag %s' % flag
99 raise dns.exception.SyntaxError('unknown flag %s' % flag)
100100 flags &= ~v[1]
101101 flags |= v[0]
102102 protocol = tok.get_string()
105105 else:
106106 protocol = _protocol_from_text.get(protocol)
107107 if protocol is None:
108 raise dns.exception.SyntaxError, \
109 'unknown protocol %s' % protocol
110
108 raise dns.exception.SyntaxError('unknown protocol %s' % protocol)
109
111110 algorithm = dns.dnssec.algorithm_from_text(tok.get_string())
112111 chunks = []
113112 while 1:
114 t = tok.get()
115 if t[0] == dns.tokenizer.EOL or t[0] == dns.tokenizer.EOF:
113 t = tok.get().unescape()
114 if t.is_eol_or_eof():
116115 break
117 if t[0] != dns.tokenizer.IDENTIFIER:
116 if not t.is_identifier():
118117 raise dns.exception.SyntaxError
119 chunks.append(t[1])
118 chunks.append(t.value)
120119 b64 = ''.join(chunks)
121120 key = b64.decode('base64_codec')
122121 return cls(rdclass, rdtype, flags, protocol, algorithm, key)
123
122
124123 from_text = classmethod(from_text)
125124
126125 def to_wire(self, file, compress = None, origin = None):
127126 header = struct.pack("!HBB", self.flags, self.protocol, self.algorithm)
128127 file.write(header)
129128 file.write(self.key)
130
129
131130 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
132131 if rdlen < 4:
133132 raise dns.exception.FormError
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2004-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
3939
4040 def posixtime_to_sigtime(what):
4141 return time.strftime('%Y%m%d%H%M%S', time.gmtime(what))
42
42
4343 class SIGBase(dns.rdata.Rdata):
4444 """SIG-like record base
4545
6565 __slots__ = ['type_covered', 'algorithm', 'labels', 'original_ttl',
6666 'expiration', 'inception', 'key_tag', 'signer',
6767 'signature']
68
68
6969 def __init__(self, rdclass, rdtype, type_covered, algorithm, labels,
7070 original_ttl, expiration, inception, key_tag, signer,
7171 signature):
8282
8383 def covers(self):
8484 return self.type_covered
85
85
8686 def to_text(self, origin=None, relativize=True, **kw):
8787 return '%s %d %d %d %s %s %d %s %s' % (
8888 dns.rdatatype.to_text(self.type_covered),
108108 signer = signer.choose_relativity(origin, relativize)
109109 chunks = []
110110 while 1:
111 t = tok.get()
112 if t[0] == dns.tokenizer.EOL or t[0] == dns.tokenizer.EOF:
111 t = tok.get().unescape()
112 if t.is_eol_or_eof():
113113 break
114 if t[0] != dns.tokenizer.IDENTIFIER:
114 if not t.is_identifier():
115115 raise dns.exception.SyntaxError
116 chunks.append(t[1])
116 chunks.append(t.value)
117117 b64 = ''.join(chunks)
118118 signature = b64.decode('base64_codec')
119119 return cls(rdclass, rdtype, type_covered, algorithm, labels,
120120 original_ttl, expiration, inception, key_tag, signer,
121121 signature)
122
122
123123 from_text = classmethod(from_text)
124124
125125 def to_wire(self, file, compress = None, origin = None):
130130 file.write(header)
131131 self.signer.to_wire(file, None, origin)
132132 file.write(self.signature)
133
133
134134 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
135135 header = struct.unpack('!HBBIIIH', wire[current : current + 18])
136136 current += 18
149149
150150 def choose_relativity(self, origin = None, relativize = True):
151151 self.signer = self.signer.choose_relativity(origin, relativize)
152
152
153153 def _cmp(self, other):
154154 hs = struct.pack('!HBBIIIH', self.type_covered,
155155 self.algorithm, self.labels,
0 # Copyright (C) 2006, 2007, 2009 Nominum, Inc.
0 # Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
2626 @see: RFC 1035"""
2727
2828 __slots__ = ['strings']
29
29
3030 def __init__(self, rdclass, rdtype, strings):
3131 super(TXTBase, self).__init__(rdclass, rdtype)
3232 if isinstance(strings, str):
4040 txt += '%s"%s"' % (prefix, dns.rdata._escapify(s))
4141 prefix = ' '
4242 return txt
43
43
4444 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
4545 strings = []
4646 while 1:
47 (ttype, s) = tok.get()
48 if ttype == dns.tokenizer.EOL or ttype == dns.tokenizer.EOF:
47 token = tok.get().unescape()
48 if token.is_eol_or_eof():
4949 break
50 if ttype != dns.tokenizer.QUOTED_STRING and \
51 ttype != dns.tokenizer.IDENTIFIER:
52 raise dns.exception.SyntaxError, "expected a string"
53 if len(s) > 255:
54 raise dns.exception.SyntaxError, "string too long"
55 strings.append(s)
50 if not (token.is_quoted_string() or token.is_identifier()):
51 raise dns.exception.SyntaxError("expected a string")
52 if len(token.value) > 255:
53 raise dns.exception.SyntaxError("string too long")
54 strings.append(token.value)
5655 if len(strings) == 0:
5756 raise dns.exception.UnexpectedEnd
5857 return cls(rdclass, rdtype, strings)
59
58
6059 from_text = classmethod(from_text)
6160
6261 def to_wire(self, file, compress = None, origin = None):
6665 byte = chr(l)
6766 file.write(byte)
6867 file.write(s)
69
68
7069 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
7170 strings = []
7271 while rdlen > 0:
0 # Copyright (C) 2001-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
249249 self.counts[ADDITIONAL] += 1
250250
251251 def add_tsig(self, keyname, secret, fudge, id, tsig_error, other_data,
252 request_mac):
252 request_mac, algorithm=dns.tsig.default_algorithm):
253253 """Add a TSIG signature to the message.
254254
255255 @param keyname: the TSIG key name
256256 @type keyname: dns.name.Name object
257257 @param secret: the secret to use
258258 @type secret: string
259 @param fudge: TSIG time fudge; default is 300 seconds.
259 @param fudge: TSIG time fudge
260260 @type fudge: int
261261 @param id: the message id to encode in the tsig signature
262262 @type id: int
266266 @type other_data: string
267267 @param request_mac: This message is a response to the request which
268268 had the specified MAC.
269 @param algorithm: the TSIG algorithm to use
269270 @type request_mac: string
270271 """
271272
272273 self._set_section(ADDITIONAL)
273274 before = self.output.tell()
274275 s = self.output.getvalue()
275 (tsig_rdata, self.mac, ctx) = dns.tsig.hmac_md5(s,
276 keyname,
277 secret,
278 int(time.time()),
279 fudge,
280 id,
281 tsig_error,
282 other_data,
283 request_mac)
276 (tsig_rdata, self.mac, ctx) = dns.tsig.sign(s,
277 keyname,
278 secret,
279 int(time.time()),
280 fudge,
281 id,
282 tsig_error,
283 other_data,
284 request_mac,
285 algorithm=algorithm)
284286 keyname.to_wire(self.output, self.compress, self.origin)
285287 self.output.write(struct.pack('!HHIH', dns.rdatatype.TSIG,
286288 dns.rdataclass.ANY, 0, 0))
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
136136 elif attr == 'rdtype':
137137 return self.rrset.rdtype
138138 else:
139 raise AttributeError, attr
139 raise AttributeError(attr)
140140
141141 def __len__(self):
142142 return len(self.rrset)
168168 since the epoch.)
169169 @type next_cleaning: float
170170 """
171
171
172172 def __init__(self, cleaning_interval=300.0):
173173 """Initialize a DNS cache.
174174
176176 cleanings. The default is 300.0
177177 @type cleaning_interval: float.
178178 """
179
179
180180 self.data = {}
181181 self.cleaning_interval = cleaning_interval
182182 self.next_cleaning = time.time() + self.cleaning_interval
183183
184184 def maybe_clean(self):
185185 """Clean the cache if it's time to do so."""
186
186
187187 now = time.time()
188188 if self.next_cleaning <= now:
189189 keys_to_delete = []
194194 del self.data[k]
195195 now = time.time()
196196 self.next_cleaning = now + self.cleaning_interval
197
197
198198 def get(self, key):
199199 """Get the answer associated with I{key}. Returns None if
200200 no answer is cached for the key.
203203 query name, rdtype, and rdclass.
204204 @rtype: dns.resolver.Answer object or None
205205 """
206
206
207207 self.maybe_clean()
208208 v = self.data.get(key)
209209 if v is None or v.expiration <= time.time():
218218 @param value: The answer being cached
219219 @type value: dns.resolver.Answer object
220220 """
221
221
222222 self.maybe_clean()
223223 self.data[key] = value
224224
231231 @param key: the key to flush
232232 @type key: (dns.name.Name, int, int) tuple or None
233233 """
234
234
235235 if not key is None:
236236 if self.data.has_key(key):
237237 del self.data[key]
264264 @type keyring: dict
265265 @ivar keyname: The TSIG keyname to use. The default is None.
266266 @type keyname: dns.name.Name object
267 @ivar keyalgorithm: The TSIG key algorithm to use. The default is
268 dns.tsig.default_algorithm.
269 @type keyalgorithm: string
267270 @ivar edns: The EDNS level to use. The default is -1, no Edns.
268271 @type edns: int
269272 @ivar ednsflags: The EDNS flags
306309 self.lifetime = 30.0
307310 self.keyring = None
308311 self.keyname = None
312 self.keyalgorithm = dns.tsig.default_algorithm
309313 self.edns = -1
310314 self.ednsflags = 0
311315 self.payload = 0
544548 of the appropriate type, or strings that can be converted into objects
545549 of the appropriate type. E.g. For I{rdtype} the integer 2 and the
546550 the string 'NS' both mean to query for records with DNS rdata type NS.
547
551
548552 @param qname: the query name
549553 @type qname: dns.name.Name object or string
550554 @param rdtype: the query type
561565 @raises NoAnswer: the response did not contain an answer
562566 @raises NoNameservers: no non-broken nameservers are available to
563567 answer the question."""
564
568
565569 if isinstance(qname, (str, unicode)):
566570 qname = dns.name.from_text(qname, None)
567571 if isinstance(rdtype, str):
588592 return answer
589593 request = dns.message.make_query(qname, rdtype, rdclass)
590594 if not self.keyname is None:
591 request.use_tsig(self.keyring, self.keyname)
595 request.use_tsig(self.keyring, self.keyname, self.keyalgorithm)
592596 request.use_edns(self.edns, self.ednsflags, self.payload)
593597 response = None
594598 #
669673 self.cache.put((qname, rdtype, rdclass), answer)
670674 return answer
671675
672 def use_tsig(self, keyring, keyname=None):
676 def use_tsig(self, keyring, keyname=None,
677 algorithm=dns.tsig.default_algorithm):
673678 """Add a TSIG signature to the query.
674679
675680 @param keyring: The TSIG keyring to use; defaults to None.
679684 but a keyname is not, then the key used will be the first key in the
680685 keyring. Note that the order of keys in a dictionary is not defined,
681686 so applications should supply a keyname when a keyring is used, unless
682 they know the keyring contains only one key."""
687 they know the keyring contains only one key.
688 @param algorithm: The TSIG key algorithm to use. The default
689 is dns.tsig.default_algorithm.
690 @type algorithm: string"""
683691 self.keyring = keyring
684692 if keyname is None:
685693 self.keyname = self.keyring.keys()[0]
686694 else:
687695 self.keyname = keyname
696 self.keyalgorithm = algorithm
688697
689698 def use_edns(self, edns, ednsflags, payload):
690699 """Configure Edns.
739748 if resolver is None:
740749 resolver = get_default_resolver()
741750 if not name.is_absolute():
742 raise NotAbsolute, name
751 raise NotAbsolute(name)
743752 while 1:
744753 try:
745754 answer = resolver.query(name, dns.rdatatype.SOA, rdclass, tcp)
0 # Copyright (C) 2006, 2007, 2009 Nominum, Inc.
0 # Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
7171 # run through inet_aton() to check syntax and make pretty.
7272 return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text))
7373 else:
74 raise dns.exception.SyntaxError, 'unknown reverse-map address family'
74 raise dns.exception.SyntaxError('unknown reverse-map address family')
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
3030 """
3131
3232 __slots__ = ['name', 'deleting']
33
33
3434 def __init__(self, name, rdclass, rdtype, covers=dns.rdatatype.NONE,
3535 deleting=None):
3636 """Create a new RRset."""
6464 def __eq__(self, other):
6565 """Two RRsets are equal if they have the same name and the same
6666 rdataset
67
67
6868 @rtype: bool"""
6969 if not isinstance(other, RRset):
7070 return False
8181 if self.name != name or self.deleting != deleting:
8282 return False
8383 return True
84
84
8585 def to_text(self, origin=None, relativize=True, **kw):
8686 """Convert the RRset into DNS master file format.
8787
9191
9292 Any additional keyword arguments are passed on to the rdata
9393 to_text() method.
94
94
9595 @param origin: The origin for relative names, or None.
9696 @type origin: dns.name.Name object
9797 @param relativize: True if names should names be relativized
120120
121121 @rtype: dns.rrset.RRset object
122122 """
123
123
124124 if isinstance(name, (str, unicode)):
125125 name = dns.name.from_text(name, None)
126126 if isinstance(rdclass, str):
133133 rd = dns.rdata.from_text(r.rdclass, r.rdtype, t)
134134 r.add(rd)
135135 return r
136
136
137137 def from_text(name, ttl, rdclass, rdtype, *text_rdatas):
138138 """Create an RRset with the specified name, TTL, class, and type and with
139139 the specified rdatas in text format.
154154 name = dns.name.from_text(name, None)
155155
156156 if len(rdatas) == 0:
157 raise ValueError, "rdata list must not be empty"
157 raise ValueError("rdata list must not be empty")
158158 r = None
159159 for rd in rdatas:
160160 if r is None:
163163 first_time = False
164164 r.add(rd)
165165 return r
166
166
167167 def from_rdata(name, ttl, *rdatas):
168168 """Create an RRset with the specified name and TTL, and with
169169 the specified rdata objects.
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
2525 @type items: list"""
2626
2727 __slots__ = ['items']
28
28
2929 def __init__(self, items=None):
3030 """Initialize the set.
3131
3232 @param items: the initial set of items
3333 @type items: any iterable or None
3434 """
35
35
3636 self.items = []
3737 if not items is None:
3838 for item in items:
4040
4141 def __repr__(self):
4242 return "dns.simpleset.Set(%s)" % repr(self.items)
43
43
4444 def add(self, item):
4545 """Add an item to the set."""
4646 if not item in self.items:
6969 return new instances (e.g. union) once, and keep using them in
7070 subclasses.
7171 """
72
72
7373 cls = self.__class__
7474 obj = cls.__new__(cls)
7575 obj.items = list(self.items)
7878 def __copy__(self):
7979 """Make a (shallow) copy of the set."""
8080 return self._clone()
81
81
8282 def copy(self):
8383 """Make a (shallow) copy of the set."""
8484 return self._clone()
9090 @type other: Set object
9191 """
9292 if not isinstance(other, Set):
93 raise ValueError, 'other must be a Set instance'
93 raise ValueError('other must be a Set instance')
9494 if self is other:
9595 return
9696 for item in other.items:
103103 @type other: Set object
104104 """
105105 if not isinstance(other, Set):
106 raise ValueError, 'other must be a Set instance'
106 raise ValueError('other must be a Set instance')
107107 if self is other:
108108 return
109109 # we make a copy of the list so that we can remove items from
119119 @type other: Set object
120120 """
121121 if not isinstance(other, Set):
122 raise ValueError, 'other must be a Set instance'
122 raise ValueError('other must be a Set instance')
123123 if self is other:
124124 self.items = []
125125 else:
133133 @type other: Set object
134134 @rtype: the same type as I{self}
135135 """
136
136
137137 obj = self._clone()
138138 obj.union_update(other)
139139 return obj
240240
241241 @rtype: bool
242242 """
243
244 if not isinstance(other, Set):
245 raise ValueError, 'other must be a Set instance'
243
244 if not isinstance(other, Set):
245 raise ValueError('other must be a Set instance')
246246 for item in self.items:
247247 if not item in other.items:
248248 return False
255255 """
256256
257257 if not isinstance(other, Set):
258 raise ValueError, 'other must be a Set instance'
258 raise ValueError('other must be a Set instance')
259259 for item in other.items:
260260 if not item in self.items:
261261 return False
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
4444 """Raised when an attempt is made to unget a token when the unget
4545 buffer is full."""
4646 pass
47
47
48 class Token(object):
49 """A DNS master file format token.
50
51 @ivar ttype: The token type
52 @type ttype: int
53 @ivar value: The token value
54 @type value: string
55 @ivar has_escape: Does the token value contain escapes?
56 @type has_escape: bool
57 """
58
59 def __init__(self, ttype, value='', has_escape=False):
60 """Initialize a token instance.
61
62 @param ttype: The token type
63 @type ttype: int
64 @ivar value: The token value
65 @type value: string
66 @ivar has_escape: Does the token value contain escapes?
67 @type has_escape: bool
68 """
69 self.ttype = ttype
70 self.value = value
71 self.has_escape = has_escape
72
73 def is_eof(self):
74 return self.ttype == EOF
75
76 def is_eol(self):
77 return self.ttype == EOL
78
79 def is_whitespace(self):
80 return self.ttype == WHITESPACE
81
82 def is_identifier(self):
83 return self.ttype == IDENTIFIER
84
85 def is_quoted_string(self):
86 return self.ttype == QUOTED_STRING
87
88 def is_comment(self):
89 return self.ttype == COMMENT
90
91 def is_delimiter(self):
92 return self.ttype == DELIMITER
93
94 def is_eol_or_eof(self):
95 return (self.ttype == EOL or self.ttype == EOF)
96
97 def __eq__(self, other):
98 if not isinstance(other, Token):
99 return False
100 return (self.ttype == other.ttype and
101 self.value == other.value)
102
103 def __ne__(self, other):
104 if not isinstance(other, Token):
105 return True
106 return (self.ttype != other.ttype or
107 self.value != other.value)
108
109 def __str__(self):
110 return '%d "%s"' % (self.ttype, self.value)
111
112 def unescape(self):
113 if not self.has_escape:
114 return self
115 unescaped = ''
116 l = len(self.value)
117 i = 0
118 while i < l:
119 c = self.value[i]
120 i += 1
121 if c == '\\':
122 if i >= l:
123 raise dns.exception.UnexpectedEnd
124 c = self.value[i]
125 i += 1
126 if c.isdigit():
127 if i >= l:
128 raise dns.exception.UnexpectedEnd
129 c2 = self.value[i]
130 i += 1
131 if i >= l:
132 raise dns.exception.UnexpectedEnd
133 c3 = self.value[i]
134 i += 1
135 if not (c2.isdigit() and c3.isdigit()):
136 raise dns.exception.SyntaxError
137 c = chr(int(c) * 100 + int(c2) * 10 + int(c3))
138 unescaped += c
139 return Token(self.ttype, unescaped)
140
141 # compatibility for old-style tuple tokens
142
143 def __len__(self):
144 return 2
145
146 def __iter__(self):
147 return iter((self.ttype, self.value))
148
149 def __getitem__(self, i):
150 if i == 0:
151 return self.ttype
152 elif i == 1:
153 return self.value
154 else:
155 raise IndexError
156
48157 class Tokenizer(object):
49158 """A DNS master file format tokenizer.
50159
51160 A token is a (type, value) tuple, where I{type} is an int, and
52161 I{value} is a string. The valid types are EOF, EOL, WHITESPACE,
53162 IDENTIFIER, QUOTED_STRING, COMMENT, and DELIMITER.
54
163
55164 @ivar file: The file to tokenize
56165 @type file: file
57166 @ivar ungotten_char: The most recently ungotten character, or None.
74183 @ivar filename: A filename that will be returned by the L{where} method.
75184 @type filename: string
76185 """
77
186
78187 def __init__(self, f=sys.stdin, filename=None):
79188 """Initialize a tokenizer instance.
80189
86195 will return.
87196 @type filename: string
88197 """
89
198
90199 if isinstance(f, str):
91200 f = cStringIO.StringIO(f)
92201 if filename is None:
111220 """Read a character from input.
112221 @rtype: string
113222 """
114
223
115224 if self.ungotten_char is None:
116225 if self.eof:
117226 c = ''
132241 @rtype: (string, int) tuple. The first item is the filename of
133242 the input, the second is the current line number.
134243 """
135
244
136245 return (self.filename, self.line_number)
137
246
138247 def _unget_char(self, c):
139248 """Unget a character.
140249
141250 The unget buffer for characters is only one character large; it is
142251 an error to try to unget a character when the unget buffer is not
143252 empty.
144
253
145254 @param c: the character to unget
146255 @type c: string
147256 @raises UngetBufferFull: there is already an ungotten char
148257 """
149
258
150259 if not self.ungotten_char is None:
151260 raise UngetBufferFull
152261 self.ungotten_char = c
161270
162271 @rtype: int
163272 """
164
273
165274 skipped = 0
166275 while True:
167276 c = self._get_char()
180289 @param want_comment: If True, return a COMMENT token if the
181290 first token read is a comment. The default is False.
182291 @type want_comment: bool
183 @rtype: (int, string) tuple
292 @rtype: Token object
184293 @raises dns.exception.UnexpectedEnd: input ended prematurely
185294 @raises dns.exception.SyntaxError: input was badly formed
186295 """
187
296
188297 if not self.ungotten_token is None:
189298 token = self.ungotten_token
190299 self.ungotten_token = None
191 if token[0] == WHITESPACE:
300 if token.is_whitespace():
192301 if want_leading:
193302 return token
194 elif token[0] == COMMENT:
303 elif token.is_comment():
195304 if want_comment:
196305 return token
197306 else:
198307 return token
199308 skipped = self.skip_whitespace()
200309 if want_leading and skipped > 0:
201 return (WHITESPACE, ' ')
310 return Token(WHITESPACE, ' ')
202311 token = ''
203312 ttype = IDENTIFIER
313 has_escape = False
204314 while True:
205315 c = self._get_char()
206316 if c == '' or c in self.delimiters:
229339 self.skip_whitespace()
230340 continue
231341 elif c == '\n':
232 return (EOL, '\n')
342 return Token(EOL, '\n')
233343 elif c == ';':
234344 while 1:
235345 c = self._get_char()
238348 token += c
239349 if want_comment:
240350 self._unget_char(c)
241 return (COMMENT, token)
351 return Token(COMMENT, token)
242352 elif c == '':
243353 if self.multiline:
244 raise dns.exception.SyntaxError, \
245 'unbalanced parentheses'
246 return (EOF, '')
354 raise dns.exception.SyntaxError('unbalanced parentheses')
355 return Token(EOF)
247356 elif self.multiline:
248357 self.skip_whitespace()
249358 token = ''
250359 continue
251360 else:
252 return (EOL, '\n')
361 return Token(EOL, '\n')
253362 else:
254363 # This code exists in case we ever want a
255364 # delimiter to be returned. It never produces
275384 raise dns.exception.SyntaxError
276385 c = chr(int(c) * 100 + int(c2) * 10 + int(c3))
277386 elif c == '\n':
278 raise dns.exception.SyntaxError, 'newline in quoted string'
387 raise dns.exception.SyntaxError('newline in quoted string')
279388 elif c == '\\':
280389 #
281 # Treat \ followed by a delimiter as the
282 # delimiter, otherwise leave it alone.
390 # It's an escape. Put it and the next character into
391 # the token; it will be checked later for goodness.
283392 #
393 token += c
394 has_escape = True
284395 c = self._get_char()
285 if c == '' or not c in self.delimiters:
286 self._unget_char(c)
287 c = '\\'
396 if c == '' or c == '\n':
397 raise dns.exception.UnexpectedEnd
288398 token += c
289399 if token == '' and ttype != QUOTED_STRING:
290400 if self.multiline:
291 raise dns.exception.SyntaxError, 'unbalanced parentheses'
401 raise dns.exception.SyntaxError('unbalanced parentheses')
292402 ttype = EOF
293 return (ttype, token)
403 return Token(ttype, token, has_escape)
294404
295405 def unget(self, token):
296406 """Unget a token.
298408 The unget buffer for tokens is only one token large; it is
299409 an error to try to unget a token when the unget buffer is not
300410 empty.
301
411
302412 @param token: the token to unget
303 @type token: (int, string) token tuple
413 @type token: Token object
304414 @raises UngetBufferFull: there is already an ungotten token
305415 """
306416
312422 """Return the next item in an iteration.
313423 @rtype: (int, string)
314424 """
315
425
316426 token = self.get()
317 if token[0] == EOF:
427 if token.is_eof():
318428 raise StopIteration
319429 return token
320430
325435
326436 def get_int(self):
327437 """Read the next token and interpret it as an integer.
328
438
329439 @raises dns.exception.SyntaxError:
330440 @rtype: int
331441 """
332
333 (ttype, value) = self.get()
334 if ttype != IDENTIFIER:
335 raise dns.exception.SyntaxError, 'expecting an identifier'
336 if not value.isdigit():
337 raise dns.exception.SyntaxError, 'expecting an integer'
338 return int(value)
442
443 token = self.get().unescape()
444 if not token.is_identifier():
445 raise dns.exception.SyntaxError('expecting an identifier')
446 if not token.value.isdigit():
447 raise dns.exception.SyntaxError('expecting an integer')
448 return int(token.value)
339449
340450 def get_uint8(self):
341451 """Read the next token and interpret it as an 8-bit unsigned
342452 integer.
343
453
344454 @raises dns.exception.SyntaxError:
345455 @rtype: int
346456 """
347
457
348458 value = self.get_int()
349459 if value < 0 or value > 255:
350 raise dns.exception.SyntaxError, \
351 '%d is not an unsigned 8-bit integer' % value
460 raise dns.exception.SyntaxError('%d is not an unsigned 8-bit integer' % value)
352461 return value
353462
354463 def get_uint16(self):
355464 """Read the next token and interpret it as a 16-bit unsigned
356465 integer.
357
466
358467 @raises dns.exception.SyntaxError:
359468 @rtype: int
360469 """
361
470
362471 value = self.get_int()
363472 if value < 0 or value > 65535:
364 raise dns.exception.SyntaxError, \
365 '%d is not an unsigned 16-bit integer' % value
473 raise dns.exception.SyntaxError('%d is not an unsigned 16-bit integer' % value)
366474 return value
367475
368476 def get_uint32(self):
369477 """Read the next token and interpret it as a 32-bit unsigned
370478 integer.
371
479
372480 @raises dns.exception.SyntaxError:
373481 @rtype: int
374482 """
375
376 (ttype, value) = self.get()
377 if ttype != IDENTIFIER:
378 raise dns.exception.SyntaxError, 'expecting an identifier'
379 if not value.isdigit():
380 raise dns.exception.SyntaxError, 'expecting an integer'
381 value = long(value)
483
484 token = self.get().unescape()
485 if not token.is_identifier():
486 raise dns.exception.SyntaxError('expecting an identifier')
487 if not token.value.isdigit():
488 raise dns.exception.SyntaxError('expecting an integer')
489 value = long(token.value)
382490 if value < 0 or value > 4294967296L:
383 raise dns.exception.SyntaxError, \
384 '%d is not an unsigned 32-bit integer' % value
491 raise dns.exception.SyntaxError('%d is not an unsigned 32-bit integer' % value)
385492 return value
386493
387494 def get_string(self, origin=None):
388495 """Read the next token and interpret it as a string.
389
496
390497 @raises dns.exception.SyntaxError:
391498 @rtype: string
392499 """
393
394 (ttype, t) = self.get()
395 if ttype != IDENTIFIER and ttype != QUOTED_STRING:
396 raise dns.exception.SyntaxError, 'expecting a string'
397 return t
500
501 token = self.get().unescape()
502 if not (token.is_identifier() or token.is_quoted_string()):
503 raise dns.exception.SyntaxError('expecting a string')
504 return token.value
505
506 def get_identifier(self, origin=None):
507 """Read the next token and raise an exception if it is not an identifier.
508
509 @raises dns.exception.SyntaxError:
510 @rtype: string
511 """
512
513 token = self.get().unescape()
514 if not token.is_identifier():
515 raise dns.exception.SyntaxError('expecting an identifier')
516 return token.value
398517
399518 def get_name(self, origin=None):
400519 """Read the next token and interpret it as a DNS name.
401
520
402521 @raises dns.exception.SyntaxError:
403522 @rtype: dns.name.Name object"""
404
405 (ttype, t) = self.get()
406 if ttype != IDENTIFIER:
407 raise dns.exception.SyntaxError, 'expecting an identifier'
408 return dns.name.from_text(t, origin)
523
524 token = self.get()
525 if not token.is_identifier():
526 raise dns.exception.SyntaxError('expecting an identifier')
527 return dns.name.from_text(token.value, origin)
409528
410529 def get_eol(self):
411530 """Read the next token and raise an exception if it isn't EOL or
414533 @raises dns.exception.SyntaxError:
415534 @rtype: string
416535 """
417
418 (ttype, t) = self.get()
419 if ttype != EOL and ttype != EOF:
420 raise dns.exception.SyntaxError, \
421 'expected EOL or EOF, got %d "%s"' % (ttype, t)
422 return t
536
537 token = self.get()
538 if not token.is_eol_or_eof():
539 raise dns.exception.SyntaxError('expected EOL or EOF, got %d "%s"' % (token.ttype, token.value))
540 return token.value
423541
424542 def get_ttl(self):
425 (ttype, t) = self.get()
426 if ttype != IDENTIFIER:
427 raise dns.exception.SyntaxError, 'expecting an identifier'
428 return dns.ttl.from_text(t)
543 token = self.get().unescape()
544 if not token.is_identifier():
545 raise dns.exception.SyntaxError('expecting an identifier')
546 return dns.ttl.from_text(token.value)
0 # Copyright (C) 2001-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
4949 """Raised if the peer didn't like amount of truncation in the TSIG we sent"""
5050 pass
5151
52 _alg_name = dns.name.from_text('HMAC-MD5.SIG-ALG.REG.INT.').to_digestable()
52 default_algorithm = "HMAC-MD5.SIG-ALG.REG.INT"
5353
5454 BADSIG = 16
5555 BADKEY = 17
5656 BADTIME = 18
5757 BADTRUNC = 22
5858
59 def hmac_md5(wire, keyname, secret, time, fudge, original_id, error,
60 other_data, request_mac, ctx=None, multi=False, first=True):
61 """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC-MD5 TSIG rdata
62 for the input parameters, the HMAC-MD5 MAC calculated by applying the
59 def sign(wire, keyname, secret, time, fudge, original_id, error,
60 other_data, request_mac, ctx=None, multi=False, first=True,
61 algorithm=default_algorithm):
62 """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata
63 for the input parameters, the HMAC MAC calculated by applying the
6364 TSIG signature algorithm, and the TSIG digest context.
6465 @rtype: (string, string, hmac.HMAC object)
6566 @raises ValueError: I{other_data} is too long
67 @raises NotImplementedError: I{algorithm} is not supported
6668 """
6769
70 (algorithm_name, digestmod) = get_algorithm(algorithm)
6871 if first:
69 ctx = hmac.new(secret)
72 ctx = hmac.new(secret, digestmod=digestmod)
7073 ml = len(request_mac)
7174 if ml > 0:
7275 ctx.update(struct.pack('!H', ml))
8285 upper_time = (long_time >> 32) & 0xffffL
8386 lower_time = long_time & 0xffffffffL
8487 time_mac = struct.pack('!HIH', upper_time, lower_time, fudge)
85 pre_mac = _alg_name + time_mac
88 pre_mac = algorithm_name + time_mac
8689 ol = len(other_data)
8790 if ol > 65535:
88 raise ValueError, 'TSIG Other Data is > 65535 bytes'
91 raise ValueError('TSIG Other Data is > 65535 bytes')
8992 post_mac = struct.pack('!HH', error, ol) + other_data
9093 if first:
9194 ctx.update(pre_mac)
103106 else:
104107 ctx = None
105108 return (tsig_rdata, mac, ctx)
109
110 def hmac_md5(wire, keyname, secret, time, fudge, original_id, error,
111 other_data, request_mac, ctx=None, multi=False, first=True,
112 algorithm=default_algorithm):
113 return sign(wire, keyname, secret, time, fudge, original_id, error,
114 other_data, request_mac, ctx, multi, first, algorithm)
106115
107116 def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata,
108117 tsig_rdlen, ctx=None, multi=False, first=True):
145154 elif error == BADTRUNC:
146155 raise PeerBadTruncation
147156 else:
148 raise PeerError, 'unknown TSIG error code %d' % error
157 raise PeerError('unknown TSIG error code %d' % error)
149158 time_low = time - fudge
150159 time_high = time + fudge
151160 if now < time_low or now > time_high:
152161 raise BadTime
153 (junk, our_mac, ctx) = hmac_md5(new_wire, keyname, secret, time, fudge,
154 original_id, error, other_data,
155 request_mac, ctx, multi, first)
162 (junk, our_mac, ctx) = sign(new_wire, keyname, secret, time, fudge,
163 original_id, error, other_data,
164 request_mac, ctx, multi, first, aname)
156165 if (our_mac != mac):
157166 raise BadSignature
158167 return ctx
168
169 def get_algorithm(algorithm):
170 """Returns the wire format string and the hash module to use for the
171 specified TSIG algorithm
172
173 @rtype: (string, hash constructor)
174 @raises NotImplementedError: I{algorithm} is not supported
175 """
176
177 hashes = {}
178 try:
179 import hashlib
180 hashes[dns.name.from_text('hmac-sha224')] = hashlib.sha224
181 hashes[dns.name.from_text('hmac-sha256')] = hashlib.sha256
182 hashes[dns.name.from_text('hmac-sha384')] = hashlib.sha384
183 hashes[dns.name.from_text('hmac-sha512')] = hashlib.sha512
184 hashes[dns.name.from_text('hmac-sha1')] = hashlib.sha1
185 hashes[dns.name.from_text('HMAC-MD5.SIG-ALG.REG.INT')] = hashlib.md5
186
187 import sys
188 if sys.hexversion < 0x02050000:
189 # hashlib doesn't conform to PEP 247: API for
190 # Cryptographic Hash Functions, which hmac before python
191 # 2.5 requires, so add the necessary items.
192 class HashlibWrapper:
193 def __init__(self, basehash):
194 self.basehash = basehash
195 self.digest_size = self.basehash().digest_size
196
197 def new(self, *args, **kwargs):
198 return self.basehash(*args, **kwargs)
199
200 for name in hashes:
201 hashes[name] = HashlibWrapper(hashes[name])
202
203 except ImportError:
204 import md5, sha
205 hashes[dns.name.from_text('HMAC-MD5.SIG-ALG.REG.INT')] = md5.md5
206 hashes[dns.name.from_text('hmac-sha1')] = sha.sha
207
208 if isinstance(algorithm, (str, unicode)):
209 algorithm = dns.name.from_text(algorithm)
210
211 if algorithm in hashes:
212 return (algorithm.to_digestable(), hashes[algorithm])
213
214 raise NotImplementedError("TSIG algorithm " + str(algorithm) +
215 " is not supported")
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
1818
1919 class BadTTL(dns.exception.SyntaxError):
2020 pass
21
21
2222 def from_text(text):
2323 """Convert the text form of a TTL to an integer.
2424
2929 @raises dns.ttl.BadTTL: the TTL is not well-formed
3030 @rtype: int
3131 """
32
32
3333 if text.isdigit():
3434 total = long(text)
3535 else:
5454 elif c == 's':
5555 total += current
5656 else:
57 raise BadTTL, "unknown unit '%s'" % c
57 raise BadTTL("unknown unit '%s'" % c)
5858 current = 0
5959 if not current == 0:
60 raise BadTTL, "trailing integer"
60 raise BadTTL("trailing integer")
6161 if total < 0L or total > 2147483647L:
62 raise BadTTL, "TTL should be between 0 and 2^31 - 1 (inclusive)"
62 raise BadTTL("TTL should be between 0 and 2^31 - 1 (inclusive)")
6363 return total
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
2323
2424 class Update(dns.message.Message):
2525 def __init__(self, zone, rdclass=dns.rdataclass.IN, keyring=None,
26 keyname=None):
26 keyname=None, keyalgorithm=dns.tsig.default_algorithm):
2727 """Initialize a new DNS Update object.
28
28
2929 @param zone: The zone which is being updated.
3030 @type zone: A dns.name.Name or string
3131 @param rdclass: The class of the zone; defaults to dns.rdataclass.IN.
4040 so applications should supply a keyname when a keyring is used, unless
4141 they know the keyring contains only one key.
4242 @type keyname: dns.name.Name or string
43 @param keyalgorithm: The TSIG algorithm to use; defaults to
44 dns.tsig.default_algorithm
45 @type keyalgorithm: string
4346 """
4447 super(Update, self).__init__()
4548 self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE)
5255 self.find_rrset(self.question, self.origin, rdclass, dns.rdatatype.SOA,
5356 create=True, force_unique=True)
5457 if not keyring is None:
55 self.use_tsig(keyring, keyname)
58 self.use_tsig(keyring, keyname, keyalgorithm)
5659
5760 def _add_rr(self, name, ttl, rd, deleting=None, section=None):
5861 """Add a single RR to the update section."""
7174 argument is the section to add to. The third argument
7275 is always a name. The other arguments can be:
7376
74 - rdataset...
77 - rdataset...
7578
7679 - ttl, rdata...
7780
108111 """Add records. The first argument is always a name. The other
109112 arguments can be:
110113
111 - rdataset...
114 - rdataset...
112115
113116 - ttl, rdata...
114117
119122 """Delete records. The first argument is always a name. The other
120123 arguments can be:
121124
122 - I{nothing}
123
124 - rdataset...
125 - I{nothing}
126
127 - rdataset...
125128
126129 - rdata...
127130
161164 def replace(self, name, *args):
162165 """Replace records. The first argument is always a name. The other
163166 arguments can be:
164
165 - rdataset...
167
168 - rdataset...
166169
167170 - ttl, rdata...
168171
170173
171174 Note that if you want to replace the entire node, you should do
172175 a delete of the name followed by one or more calls to add."""
173
176
174177 self._add(True, self.authority, name, *args)
175178
176179 def present(self, name, *args):
178181 or specific rdataset) exists as a prerequisite to the
179182 execution of the update. The first argument is always a name.
180183 The other arguments can be:
181
182 - rdataset...
184
185 - rdataset...
183186
184187 - rdata...
185188
186189 - rdtype, string..."""
187
190
188191 if isinstance(name, (str, unicode)):
189192 name = dns.name.from_text(name, None)
190193 if len(args) == 0:
195198 elif isinstance(args[0], dns.rdataset.Rdataset) or \
196199 isinstance(args[0], dns.rdata.Rdata) or \
197200 len(args) > 1:
198 if len(args) > 1:
201 if not isinstance(args[0], dns.rdataset.Rdataset):
199202 # Add a 0 TTL
200203 args = list(args)
201204 args.insert(0, 0)
217220 name = dns.name.from_text(name, None)
218221 if rdtype is None:
219222 rrset = self.find_rrset(self.answer, name,
220 dns.rdataclass.NONE, dns.rdatatype.ANY,
223 dns.rdataclass.NONE, dns.rdatatype.ANY,
221224 dns.rdatatype.NONE, None,
222225 True, True)
223226 else:
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
1515 """dnspython release version information."""
1616
1717 MAJOR = 1
18 MINOR = 7
19 MICRO = 1
18 MINOR = 8
19 MICRO = 0
2020 RELEASELEVEL = 0x0f
2121 SERIAL = 0
2222
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
5353 dns.name.Name object, or it may be a string. In the either case,
5454 if the name is relative it is treated as relative to the origin of
5555 the zone.
56
56
5757 @ivar rdclass: The zone's rdata class; the default is class IN.
5858 @type rdclass: int
5959 @ivar origin: The origin of the zone.
7070 node_factory = dns.node.Node
7171
7272 __slots__ = ['rdclass', 'origin', 'nodes', 'relativize']
73
73
7474 def __init__(self, origin, rdclass=dns.rdataclass.IN, relativize=True):
7575 """Initialize a zone object.
7676
8989 nodes.
9090 @rtype: bool
9191 """
92
92
9393 if not isinstance(other, Zone):
9494 return False
9595 if self.rdclass != other.rdclass or \
102102 """Are two zones not equal?
103103 @rtype: bool
104104 """
105
105
106106 return not self.__eq__(other)
107107
108108 def _validate_name(self, name):
109109 if isinstance(name, (str, unicode)):
110110 name = dns.name.from_text(name, None)
111111 elif not isinstance(name, dns.name.Name):
112 raise KeyError, \
113 "name parameter must be convertable to a DNS name"
112 raise KeyError("name parameter must be convertable to a DNS name")
114113 if name.is_absolute():
115114 if not name.is_subdomain(self.origin):
116 raise KeyError, \
117 "name parameter must be a subdomain of the zone origin"
115 raise KeyError("name parameter must be a subdomain of the zone origin")
118116 if self.relativize:
119117 name = name.relativize(self.origin)
120118 return name
121
119
122120 def __getitem__(self, key):
123121 key = self._validate_name(key)
124122 return self.nodes[key]
169167 @raises KeyError: the name is not known and create was not specified.
170168 @rtype: dns.node.Node object
171169 """
172
170
173171 name = self._validate_name(name)
174172 node = self.nodes.get(name)
175173 if node is None:
185183 This method is like L{find_node}, except it returns None instead
186184 of raising an exception if the node does not exist and creation
187185 has not been requested.
188
186
189187 @param name: the name of the node to find
190188 @type name: dns.name.Name object or string
191189 @param create: should the node be created if it doesn't exist?
204202
205203 It is not an error if the node does not exist.
206204 """
207
205
208206 name = self._validate_name(name)
209207 if self.nodes.has_key(name):
210208 del self.nodes[name]
211
209
212210 def find_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE,
213211 create=False):
214212 """Look for rdata with the specified name and type in the zone,
220218
221219 The rdataset returned is not a copy; changes to it will change
222220 the zone.
223
221
224222 KeyError is raised if the name or type are not found.
225223 Use L{get_rdataset} if you want to have None returned instead.
226224
256254
257255 The rdataset returned is not a copy; changes to it will change
258256 the zone.
259
257
260258 None is returned if the name or type are not found.
261259 Use L{find_rdataset} if you want to have KeyError raised instead.
262260
313311
314312 def replace_rdataset(self, name, replacement):
315313 """Replace an rdataset at name.
316
314
317315 It is not an error if there is no rdataset matching I{replacement}.
318316
319317 Ownership of the I{replacement} object is transferred to the zone;
329327 """
330328
331329 if replacement.rdclass != self.rdclass:
332 raise ValueError, 'replacement.rdclass != zone.rdclass'
330 raise ValueError('replacement.rdclass != zone.rdclass')
333331 node = self.find_node(name, True)
334332 node.replace_rdataset(replacement)
335333
340338 The I{name}, I{rdtype}, and I{covers} parameters may be
341339 strings, in which case they will be converted to their proper
342340 type.
343
341
344342 This method is less efficient than the similar
345343 L{find_rdataset} because it creates an RRset instead of
346344 returning the matching rdataset. It may be more convenient
380378 The I{name}, I{rdtype}, and I{covers} parameters may be
381379 strings, in which case they will be converted to their proper
382380 type.
383
381
384382 This method is less efficient than the similar L{get_rdataset}
385383 because it creates an RRset instead of returning the matching
386384 rdataset. It may be more convenient for some uses since it
419417 @param covers: the covered type (defaults to None)
420418 @type covers: int or string
421419 """
422
420
423421 if isinstance(rdtype, str):
424422 rdtype = dns.rdatatype.from_text(rdtype)
425423 if isinstance(covers, str):
442440 @param covers: the covered type (defaults to None)
443441 @type covers: int or string
444442 """
445
443
446444 if isinstance(rdtype, str):
447445 rdtype = dns.rdatatype.from_text(rdtype)
448446 if isinstance(covers, str):
456454
457455 def to_file(self, f, sorted=True, relativize=True, nl=None):
458456 """Write a zone to a file.
459
457
460458 @param f: file or string. If I{f} is a string, it is treated
461459 as the name of a file to open.
462460 @param sorted: if True, the file will be written with the
565563
566564 def _eat_line(self):
567565 while 1:
568 (ttype, t) = self.tok.get()
569 if ttype == dns.tokenizer.EOL or ttype == dns.tokenizer.EOF:
566 token = self.tok.get()
567 if token.is_eol_or_eof():
570568 break
571
569
572570 def _rr_line(self):
573571 """Process one line from a DNS master file."""
574572 # Name
575573 if self.current_origin is None:
576574 raise UnknownOrigin
577575 token = self.tok.get(want_leading = True)
578 if token[0] != dns.tokenizer.WHITESPACE:
579 self.last_name = dns.name.from_text(token[1], self.current_origin)
576 if not token.is_whitespace():
577 self.last_name = dns.name.from_text(token.value, self.current_origin)
580578 else:
581579 token = self.tok.get()
582 if token[0] == dns.tokenizer.EOL or \
583 token[0] == dns.tokenizer.EOF:
580 if token.is_eol_or_eof():
584581 # treat leading WS followed by EOL/EOF as if they were EOL/EOF.
585582 return
586583 self.tok.unget(token)
591588 if self.relativize:
592589 name = name.relativize(self.zone.origin)
593590 token = self.tok.get()
594 if token[0] != dns.tokenizer.IDENTIFIER:
591 if not token.is_identifier():
595592 raise dns.exception.SyntaxError
596593 # TTL
597594 try:
598 ttl = dns.ttl.from_text(token[1])
595 ttl = dns.ttl.from_text(token.value)
599596 token = self.tok.get()
600 if token[0] != dns.tokenizer.IDENTIFIER:
597 if not token.is_identifier():
601598 raise dns.exception.SyntaxError
602599 except dns.ttl.BadTTL:
603600 ttl = self.ttl
604601 # Class
605602 try:
606 rdclass = dns.rdataclass.from_text(token[1])
603 rdclass = dns.rdataclass.from_text(token.value)
607604 token = self.tok.get()
608 if token[0] != dns.tokenizer.IDENTIFIER:
605 if not token.is_identifier():
609606 raise dns.exception.SyntaxError
610607 except dns.exception.SyntaxError:
611608 raise dns.exception.SyntaxError
612609 except:
613610 rdclass = self.zone.rdclass
614611 if rdclass != self.zone.rdclass:
615 raise dns.exception.SyntaxError, "RR class is not zone's class"
612 raise dns.exception.SyntaxError("RR class is not zone's class")
616613 # Type
617614 try:
618 rdtype = dns.rdatatype.from_text(token[1])
615 rdtype = dns.rdatatype.from_text(token.value)
619616 except:
620 raise dns.exception.SyntaxError, \
621 "unknown rdatatype '%s'" % token[1]
617 raise dns.exception.SyntaxError("unknown rdatatype '%s'" % token.value)
622618 n = self.zone.nodes.get(name)
623619 if n is None:
624620 n = self.zone.node_factory()
629625 except dns.exception.SyntaxError:
630626 # Catch and reraise.
631627 (ty, va) = sys.exc_info()[:2]
632 raise ty, va
628 raise va
633629 except:
634630 # All exceptions that occur in the processing of rdata
635631 # are treated as syntax errors. This is not strictly
636632 # correct, but it is correct almost all of the time.
637633 # We convert them to syntax errors so that we can emit
638634 # helpful filename:line info.
639
640635 (ty, va) = sys.exc_info()[:2]
641 raise dns.exception.SyntaxError, \
642 "caught exception %s: %s" % (str(ty), str(va))
636 raise dns.exception.SyntaxError("caught exception %s: %s" % (str(ty), str(va)))
643637
644638 rd.choose_relativity(self.zone.origin, self.relativize)
645639 covers = rd.covers()
655649
656650 try:
657651 while 1:
658 token = self.tok.get(True, True)
659 if token[0] == dns.tokenizer.EOF:
652 token = self.tok.get(True, True).unescape()
653 if token.is_eof():
660654 if not self.current_file is None:
661655 self.current_file.close()
662656 if len(self.saved_state) > 0:
667661 self.ttl) = self.saved_state.pop(-1)
668662 continue
669663 break
670 elif token[0] == dns.tokenizer.EOL:
664 elif token.is_eol():
671665 continue
672 elif token[0] == dns.tokenizer.COMMENT:
666 elif token.is_comment():
673667 self.tok.get_eol()
674668 continue
675 elif token[1][0] == '$':
676 u = token[1].upper()
669 elif token.value[0] == '$':
670 u = token.value.upper()
677671 if u == '$TTL':
678672 token = self.tok.get()
679 if token[0] != dns.tokenizer.IDENTIFIER:
680 raise dns.exception.SyntaxError, "bad $TTL"
681 self.ttl = dns.ttl.from_text(token[1])
673 if not token.is_identifier():
674 raise dns.exception.SyntaxError("bad $TTL")
675 self.ttl = dns.ttl.from_text(token.value)
682676 self.tok.get_eol()
683677 elif u == '$ORIGIN':
684678 self.current_origin = self.tok.get_name()
687681 self.zone.origin = self.current_origin
688682 elif u == '$INCLUDE' and self.allow_include:
689683 token = self.tok.get()
690 if token[0] != dns.tokenizer.QUOTED_STRING:
691 raise dns.exception.SyntaxError, \
692 "bad filename in $INCLUDE"
693 filename = token[1]
684 if not token.is_quoted_string():
685 raise dns.exception.SyntaxError("bad filename in $INCLUDE")
686 filename = token.value
694687 token = self.tok.get()
695 if token[0] == dns.tokenizer.IDENTIFIER:
696 new_origin = dns.name.from_text(token[1], \
697 self.current_origin)
688 if token.is_identifier():
689 new_origin = dns.name.from_text(token.value, \
690 self.current_origin)
698691 self.tok.get_eol()
699 elif token[0] != dns.tokenizer.EOL and \
700 token[0] != dns.tokenizer.EOF:
701 raise dns.exception.SyntaxError, \
702 "bad origin in $INCLUDE"
692 elif not token.is_eol_or_eof():
693 raise dns.exception.SyntaxError("bad origin in $INCLUDE")
703694 else:
704695 new_origin = self.current_origin
705696 self.saved_state.append((self.tok,
712703 filename)
713704 self.current_origin = new_origin
714705 else:
715 raise dns.exception.SyntaxError, \
716 "Unknown master file directive '" + u + "'"
706 raise dns.exception.SyntaxError("Unknown master file directive '" + u + "'")
717707 continue
718708 self.tok.unget(token)
719709 self._rr_line()
721711 (filename, line_number) = self.tok.where()
722712 if detail is None:
723713 detail = "syntax error"
724 raise dns.exception.SyntaxError, \
725 "%s:%d: %s" % (filename, line_number, detail)
726
714 raise dns.exception.SyntaxError("%s:%d: %s" % (filename, line_number, detail))
715
727716 # Now that we're done reading, do some basic checking of the zone.
728717 if self.check_origin:
729718 self.zone.check_origin()
818807 if filename is None:
819808 filename = '<file>'
820809 want_close = False
821
810
822811 try:
823812 z = from_text(f, origin, rdclass, relativize, zone_factory,
824813 filename, allow_include, check_origin)
829818
830819 def from_xfr(xfr, zone_factory=Zone, relativize=True):
831820 """Convert the output of a zone transfer generator into a zone object.
832
821
833822 @param xfr: The xfr generator
834823 @type xfr: generator of dns.message.Message objects
835824 @param relativize: should names be relativized? The default is True.
840829 @raises dns.zone.NoNS: No NS RRset was found at the zone origin
841830 @rtype: dns.zone.Zone object
842831 """
843
832
844833 z = None
845834 for r in xfr:
846835 if z is None:
00 #!/usr/bin/env python
11 #
2 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
2 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
33 #
44 # Permission to use, copy, modify, and distribute this software and its
55 # documentation for any purpose with or without fee is hereby granted,
1717 import sys
1818 from distutils.core import setup
1919
20 version = '1.7.1'
20 version = '1.8.0'
2121
2222 kwargs = {
2323 'name' : 'dnspython',
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2006, 2007, 2009 Nominum, Inc.
0 # Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
1717 import dns.exception
1818 import dns.tokenizer
1919
20 Token = dns.tokenizer.Token
21
2022 class TokenizerTestCase(unittest.TestCase):
21
23
2224 def testQuotedString1(self):
2325 tok = dns.tokenizer.Tokenizer(r'"foo"')
24 (ttype, value) = tok.get()
25 self.failUnless(ttype == dns.tokenizer.QUOTED_STRING and
26 value == 'foo')
26 token = tok.get()
27 self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, 'foo'))
2728
2829 def testQuotedString2(self):
2930 tok = dns.tokenizer.Tokenizer(r'""')
30 (ttype, value) = tok.get()
31 self.failUnless(ttype == dns.tokenizer.QUOTED_STRING and
32 value == '')
31 token = tok.get()
32 self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, ''))
3333
3434 def testQuotedString3(self):
3535 tok = dns.tokenizer.Tokenizer(r'"\"foo\""')
36 (ttype, value) = tok.get()
37 self.failUnless(ttype == dns.tokenizer.QUOTED_STRING and
38 value == '"foo"')
36 token = tok.get()
37 self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, '"foo"'))
3938
4039 def testQuotedString4(self):
4140 tok = dns.tokenizer.Tokenizer(r'"foo\010bar"')
42 (ttype, value) = tok.get()
43 self.failUnless(ttype == dns.tokenizer.QUOTED_STRING and
44 value == 'foo\x0abar')
41 token = tok.get()
42 self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, 'foo\x0abar'))
4543
4644 def testQuotedString5(self):
4745 def bad():
4846 tok = dns.tokenizer.Tokenizer(r'"foo')
49 (ttype, value) = tok.get()
47 token = tok.get()
5048 self.failUnlessRaises(dns.exception.UnexpectedEnd, bad)
5149
5250 def testQuotedString6(self):
5351 def bad():
5452 tok = dns.tokenizer.Tokenizer(r'"foo\01')
55 (ttype, value) = tok.get()
53 token = tok.get()
5654 self.failUnlessRaises(dns.exception.SyntaxError, bad)
5755
5856 def testQuotedString7(self):
5957 def bad():
6058 tok = dns.tokenizer.Tokenizer('"foo\nbar"')
61 (ttype, value) = tok.get()
59 token = tok.get()
6260 self.failUnlessRaises(dns.exception.SyntaxError, bad)
6361
6462 def testEmpty1(self):
6563 tok = dns.tokenizer.Tokenizer('')
66 (ttype, value) = tok.get()
67 self.failUnless(ttype == dns.tokenizer.EOF)
64 token = tok.get()
65 self.failUnless(token.is_eof())
6866
6967 def testEmpty2(self):
7068 tok = dns.tokenizer.Tokenizer('')
71 (ttype1, value1) = tok.get()
72 (ttype2, value2) = tok.get()
73 self.failUnless(ttype1 == dns.tokenizer.EOF and
74 ttype2 == dns.tokenizer.EOF)
69 token1 = tok.get()
70 token2 = tok.get()
71 self.failUnless(token1.is_eof() and token2.is_eof())
7572
7673 def testEOL(self):
7774 tok = dns.tokenizer.Tokenizer('\n')
78 (ttype1, value1) = tok.get()
79 (ttype2, value2) = tok.get()
80 self.failUnless(ttype1 == dns.tokenizer.EOL and
81 ttype2 == dns.tokenizer.EOF)
75 token1 = tok.get()
76 token2 = tok.get()
77 self.failUnless(token1.is_eol() and token2.is_eof())
8278
8379 def testWS1(self):
8480 tok = dns.tokenizer.Tokenizer(' \n')
85 (ttype1, value1) = tok.get()
86 self.failUnless(ttype1 == dns.tokenizer.EOL)
81 token1 = tok.get()
82 self.failUnless(token1.is_eol())
8783
8884 def testWS2(self):
8985 tok = dns.tokenizer.Tokenizer(' \n')
90 (ttype1, value1) = tok.get(want_leading=True)
91 self.failUnless(ttype1 == dns.tokenizer.WHITESPACE)
86 token1 = tok.get(want_leading=True)
87 self.failUnless(token1.is_whitespace())
9288
9389 def testComment1(self):
9490 tok = dns.tokenizer.Tokenizer(' ;foo\n')
95 (ttype1, value1) = tok.get()
96 self.failUnless(ttype1 == dns.tokenizer.EOL)
91 token1 = tok.get()
92 self.failUnless(token1.is_eol())
9793
9894 def testComment2(self):
9995 tok = dns.tokenizer.Tokenizer(' ;foo\n')
100 (ttype1, value1) = tok.get(want_comment = True)
101 (ttype2, value2) = tok.get()
102 self.failUnless(ttype1 == dns.tokenizer.COMMENT and
103 value1 == 'foo' and
104 ttype2 == dns.tokenizer.EOL)
96 token1 = tok.get(want_comment = True)
97 token2 = tok.get()
98 self.failUnless(token1 == Token(dns.tokenizer.COMMENT, 'foo') and
99 token2.is_eol())
105100
106101 def testComment3(self):
107102 tok = dns.tokenizer.Tokenizer(' ;foo bar\n')
108 (ttype1, value1) = tok.get(want_comment = True)
109 (ttype2, value2) = tok.get()
110 self.failUnless(ttype1 == dns.tokenizer.COMMENT and
111 value1 == 'foo bar' and
112 ttype2 == dns.tokenizer.EOL)
103 token1 = tok.get(want_comment = True)
104 token2 = tok.get()
105 self.failUnless(token1 == Token(dns.tokenizer.COMMENT, 'foo bar') and
106 token2.is_eol())
113107
114108 def testMultiline1(self):
115109 tok = dns.tokenizer.Tokenizer('( foo\n\n bar\n)')
116110 tokens = list(iter(tok))
117 self.failUnless(tokens == [(dns.tokenizer.IDENTIFIER, 'foo'),
118 (dns.tokenizer.IDENTIFIER, 'bar')])
111 self.failUnless(tokens == [Token(dns.tokenizer.IDENTIFIER, 'foo'),
112 Token(dns.tokenizer.IDENTIFIER, 'bar')])
119113
120114 def testMultiline2(self):
121115 tok = dns.tokenizer.Tokenizer('( foo\n\n bar\n)\n')
122116 tokens = list(iter(tok))
123 self.failUnless(tokens == [(dns.tokenizer.IDENTIFIER, 'foo'),
124 (dns.tokenizer.IDENTIFIER, 'bar'),
125 (dns.tokenizer.EOL, '\n')])
117 self.failUnless(tokens == [Token(dns.tokenizer.IDENTIFIER, 'foo'),
118 Token(dns.tokenizer.IDENTIFIER, 'bar'),
119 Token(dns.tokenizer.EOL, '\n')])
126120 def testMultiline3(self):
127121 def bad():
128122 tok = dns.tokenizer.Tokenizer('foo)')
140134 t1 = tok.get()
141135 tok.unget(t1)
142136 t2 = tok.get()
143 self.failUnless(t1 == t2 and t1 == (dns.tokenizer.IDENTIFIER, 'foo'))
137 self.failUnless(t1 == t2 and t1.ttype == dns.tokenizer.IDENTIFIER and \
138 t1.value == 'foo')
144139
145140 def testUnget2(self):
146141 def bad():
163158 def testEscapedDelimiter1(self):
164159 tok = dns.tokenizer.Tokenizer(r'ch\ ld')
165160 t = tok.get()
166 self.failUnless(t == (dns.tokenizer.IDENTIFIER, r'ch ld'))
161 self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\ ld')
167162
168163 def testEscapedDelimiter2(self):
169 tok = dns.tokenizer.Tokenizer(r'ch\0ld')
164 tok = dns.tokenizer.Tokenizer(r'ch\032ld')
170165 t = tok.get()
171 self.failUnless(t == (dns.tokenizer.IDENTIFIER, r'ch\0ld'))
166 self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\032ld')
167
168 def testEscapedDelimiter3(self):
169 tok = dns.tokenizer.Tokenizer(r'ch\ild')
170 t = tok.get()
171 self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\ild')
172
173 def testEscapedDelimiter1u(self):
174 tok = dns.tokenizer.Tokenizer(r'ch\ ld')
175 t = tok.get().unescape()
176 self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch ld')
177
178 def testEscapedDelimiter2u(self):
179 tok = dns.tokenizer.Tokenizer(r'ch\032ld')
180 t = tok.get().unescape()
181 self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == 'ch ld')
182
183 def testEscapedDelimiter3u(self):
184 tok = dns.tokenizer.Tokenizer(r'ch\ild')
185 t = tok.get().unescape()
186 self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'child')
172187
173188 if __name__ == '__main__':
174189 unittest.main()
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
0 # Copyright (C) 2003-2007, 2009 Nominum, Inc.
0 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,