Imported Upstream version 1.8.0
SVN-Git Migration
8 years ago
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 | ||
0 | 54 | 2009-06-19 Bob Halley <halley@dnspython.org> |
1 | 55 | |
2 | 56 | * (Version 1.7.1 released) |
0 | 0 | Metadata-Version: 1.1 |
1 | 1 | Name: dnspython |
2 | Version: 1.7.1 | |
2 | Version: 1.8.0 | |
3 | 3 | Summary: DNS toolkit |
4 | 4 | Home-page: http://www.dnspython.org |
5 | 5 | Author: Bob Halley |
6 | 6 | Author-email: halley@dnspython.org |
7 | 7 | 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 | |
9 | 9 | Description: dnspython is a DNS toolkit for Python. It supports almost all |
10 | 10 | record types. It can be used for queries, zone transfers, and dynamic |
11 | 11 | updates. It supports TSIG authenticated messages and EDNS0. |
21 | 21 | |
22 | 22 | ABOUT THIS RELEASE |
23 | 23 | |
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. | |
25 | 58 | |
26 | 59 | New since 1.7.0: |
27 | 60 |
21 | 21 | RSASHA1 = 5 |
22 | 22 | DSANSEC3SHA1 = 6 |
23 | 23 | RSASHA1NSEC3SHA1 = 7 |
24 | RSASHA256 = 8 | |
25 | RSASHA512 = 10 | |
24 | 26 | INDIRECT = 252 |
25 | 27 | PRIVATEDNS = 253 |
26 | 28 | PRIVATEOID = 254 |
30 | 32 | 'DH' : DH, |
31 | 33 | 'DSA' : DSA, |
32 | 34 | 'ECC' : ECC, |
35 | 'RSASHA1' : RSASHA1, | |
36 | 'DSANSEC3SHA1' : DSANSEC3SHA1, | |
37 | 'RSASHA1NSEC3SHA1' : RSASHA1NSEC3SHA1, | |
38 | 'RSASHA256' : RSASHA256, | |
39 | 'RSASHA512' : RSASHA512, | |
33 | 40 | 'INDIRECT' : INDIRECT, |
34 | 41 | 'PRIVATEDNS' : PRIVATEDNS, |
35 | 42 | 'PRIVATEOID' : PRIVATEOID, |
53 | 53 | name = name.relativize(origin) |
54 | 54 | dlabels = [d for d in name.labels if (d.isdigit() and len(d) == 1)] |
55 | 55 | 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') | |
57 | 57 | dlabels.reverse() |
58 | 58 | text = ''.join(dlabels) |
59 | 59 | if want_plus_prefix: |
72 | 72 | domain = dns.name.from_text(domain) |
73 | 73 | qname = dns.e164.from_e164(number, domain) |
74 | 74 | try: |
75 | return dns.resolver.query(qname, 'NAPTR') | |
75 | return resolver.query(qname, 'NAPTR') | |
76 | 76 | except dns.resolver.NXDOMAIN: |
77 | 77 | pass |
78 | 78 | raise dns.resolver.NXDOMAIN |
12 | 12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
13 | 13 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 14 | |
15 | import os | |
15 | 16 | import time |
17 | try: | |
18 | import threading as _threading | |
19 | except ImportError: | |
20 | import dummy_threading as _threading | |
16 | 21 | |
17 | 22 | class EntropyPool(object): |
18 | 23 | def __init__(self, seed=None): |
19 | 24 | self.pool_index = 0 |
20 | 25 | self.digest = None |
21 | 26 | 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() | |
31 | 28 | try: |
32 | 29 | import hashlib |
33 | 30 | self.hash = hashlib.sha1() |
42 | 39 | self.hash = md5.new() |
43 | 40 | self.hash_len = 16 |
44 | 41 | 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 | |
46 | 47 | |
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) | |
56 | 79 | |
57 | 80 | 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() | |
65 | 93 | return value |
66 | 94 | |
67 | 95 | def random_16(self): |
73 | 101 | def random_between(self, first, last): |
74 | 102 | size = last - first + 1 |
75 | 103 | if size > 4294967296L: |
76 | raise ValueError, 'too big' | |
104 | raise ValueError('too big') | |
77 | 105 | if size > 65536: |
78 | 106 | rand = self.random_32 |
79 | 107 | max = 4294967295L |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
31 | 31 | """ |
32 | 32 | |
33 | 33 | if len(address) != 16: |
34 | raise ValueError, "IPv6 addresses are 16 bytes long" | |
34 | raise ValueError("IPv6 addresses are 16 bytes long") | |
35 | 35 | hex = address.encode('hex_codec') |
36 | 36 | chunks = [] |
37 | 37 | i = 0 |
94 | 94 | |
95 | 95 | def inet_aton(text): |
96 | 96 | """Convert a text format IPv6 address into network format. |
97 | ||
97 | ||
98 | 98 | @param text: the textual address |
99 | 99 | @type text: string |
100 | 100 | @rtype: string |
101 | 101 | @raises dns.exception.SyntaxError: the text was not properly formatted |
102 | 102 | """ |
103 | ||
103 | ||
104 | 104 | # |
105 | 105 | # Our aim here is not something fast; we just want something that works. |
106 | 106 | # |
0 | # Copyright (C) 2001-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
91 | 91 | @type keyring: dict |
92 | 92 | @ivar keyname: The TSIG keyname to use. The default is None. |
93 | 93 | @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 | |
94 | 97 | @ivar request_mac: The TSIG MAC of the request message associated with |
95 | 98 | this message; used when validating TSIG signatures. @see: RFC 2845 for |
96 | 99 | more information on TSIG fields. |
148 | 151 | self.request_payload = 0 |
149 | 152 | self.keyring = None |
150 | 153 | self.keyname = None |
154 | self.keyalgorithm = dns.tsig.default_algorithm | |
151 | 155 | self.request_mac = '' |
152 | 156 | self.other_data = '' |
153 | 157 | self.tsig_error = 0 |
285 | 289 | elif section is self.additional: |
286 | 290 | return 3 |
287 | 291 | else: |
288 | raise ValueError, 'unknown section' | |
292 | raise ValueError('unknown section') | |
289 | 293 | |
290 | 294 | def find_rrset(self, section, name, rdclass, rdtype, |
291 | 295 | covers=dns.rdatatype.NONE, deleting=None, create=False, |
409 | 413 | if not self.keyname is None: |
410 | 414 | r.add_tsig(self.keyname, self.keyring[self.keyname], |
411 | 415 | 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) | |
413 | 418 | self.mac = r.mac |
414 | 419 | return r.get_wire() |
415 | 420 | |
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): | |
418 | 424 | """When sending, a TSIG signature using the specified keyring |
419 | 425 | and keyname should be added. |
420 | 426 | |
435 | 441 | @type tsig_error: int |
436 | 442 | @param other_data: TSIG other data. |
437 | 443 | @type other_data: string |
444 | @param algorithm: The TSIG algorithm to use; defaults to | |
445 | dns.tsig.default_algorithm | |
438 | 446 | """ |
439 | 447 | |
440 | 448 | self.keyring = keyring |
444 | 452 | if isinstance(keyname, (str, unicode)): |
445 | 453 | keyname = dns.name.from_text(keyname) |
446 | 454 | self.keyname = keyname |
455 | self.keyalgorithm = algorithm | |
447 | 456 | self.fudge = fudge |
448 | 457 | if original_id is None: |
449 | 458 | self.original_id = self.id |
643 | 652 | i == (count - 1)): |
644 | 653 | raise BadTSIG |
645 | 654 | if self.message.keyring is None: |
646 | raise UnknownTSIGKey, 'got signed message without keyring' | |
655 | raise UnknownTSIGKey('got signed message without keyring') | |
647 | 656 | secret = self.message.keyring.get(absolute_name) |
648 | 657 | if secret is None: |
649 | raise UnknownTSIGKey, "key '%s' unknown" % name | |
658 | raise UnknownTSIGKey("key '%s' unknown" % name) | |
650 | 659 | self.message.tsig_ctx = \ |
651 | 660 | dns.tsig.validate(self.wire, |
652 | 661 | absolute_name, |
793 | 802 | def _header_line(self, section): |
794 | 803 | """Process one line from the text format header section.""" |
795 | 804 | |
796 | (ttype, what) = self.tok.get() | |
805 | token = self.tok.get() | |
806 | what = token.value | |
797 | 807 | if what == 'id': |
798 | 808 | self.message.id = self.tok.get_int() |
799 | 809 | elif what == 'flags': |
800 | 810 | while True: |
801 | 811 | token = self.tok.get() |
802 | if token[0] != dns.tokenizer.IDENTIFIER: | |
812 | if not token.is_identifier(): | |
803 | 813 | self.tok.unget(token) |
804 | 814 | break |
805 | 815 | self.message.flags = self.message.flags | \ |
806 | dns.flags.from_text(token[1]) | |
816 | dns.flags.from_text(token.value) | |
807 | 817 | if dns.opcode.is_update(self.message.flags): |
808 | 818 | self.updating = True |
809 | 819 | elif what == 'edns': |
815 | 825 | self.message.edns = 0 |
816 | 826 | while True: |
817 | 827 | token = self.tok.get() |
818 | if token[0] != dns.tokenizer.IDENTIFIER: | |
828 | if not token.is_identifier(): | |
819 | 829 | self.tok.unget(token) |
820 | 830 | break |
821 | 831 | self.message.ednsflags = self.message.ednsflags | \ |
822 | dns.flags.edns_from_text(token[1]) | |
832 | dns.flags.edns_from_text(token.value) | |
823 | 833 | elif what == 'payload': |
824 | 834 | self.message.payload = self.tok.get_int() |
825 | 835 | if self.message.edns < 0: |
839 | 849 | """Process one line from the text format question section.""" |
840 | 850 | |
841 | 851 | 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) | |
844 | 854 | name = self.last_name |
845 | 855 | token = self.tok.get() |
846 | if token[0] != dns.tokenizer.IDENTIFIER: | |
856 | if not token.is_identifier(): | |
847 | 857 | raise dns.exception.SyntaxError |
848 | 858 | # Class |
849 | 859 | try: |
850 | rdclass = dns.rdataclass.from_text(token[1]) | |
860 | rdclass = dns.rdataclass.from_text(token.value) | |
851 | 861 | token = self.tok.get() |
852 | if token[0] != dns.tokenizer.IDENTIFIER: | |
862 | if not token.is_identifier(): | |
853 | 863 | raise dns.exception.SyntaxError |
854 | 864 | except dns.exception.SyntaxError: |
855 | 865 | raise dns.exception.SyntaxError |
856 | 866 | except: |
857 | 867 | rdclass = dns.rdataclass.IN |
858 | 868 | # Type |
859 | rdtype = dns.rdatatype.from_text(token[1]) | |
869 | rdtype = dns.rdatatype.from_text(token.value) | |
860 | 870 | self.message.find_rrset(self.message.question, name, |
861 | 871 | rdclass, rdtype, create=True, |
862 | 872 | force_unique=True) |
872 | 882 | deleting = None |
873 | 883 | # Name |
874 | 884 | 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) | |
877 | 887 | name = self.last_name |
878 | 888 | token = self.tok.get() |
879 | if token[0] != dns.tokenizer.IDENTIFIER: | |
889 | if not token.is_identifier(): | |
880 | 890 | raise dns.exception.SyntaxError |
881 | 891 | # TTL |
882 | 892 | try: |
883 | ttl = int(token[1], 0) | |
893 | ttl = int(token.value, 0) | |
884 | 894 | token = self.tok.get() |
885 | if token[0] != dns.tokenizer.IDENTIFIER: | |
895 | if not token.is_identifier(): | |
886 | 896 | raise dns.exception.SyntaxError |
887 | 897 | except dns.exception.SyntaxError: |
888 | 898 | raise dns.exception.SyntaxError |
890 | 900 | ttl = 0 |
891 | 901 | # Class |
892 | 902 | try: |
893 | rdclass = dns.rdataclass.from_text(token[1]) | |
903 | rdclass = dns.rdataclass.from_text(token.value) | |
894 | 904 | token = self.tok.get() |
895 | if token[0] != dns.tokenizer.IDENTIFIER: | |
905 | if not token.is_identifier(): | |
896 | 906 | raise dns.exception.SyntaxError |
897 | 907 | if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE: |
898 | 908 | deleting = rdclass |
902 | 912 | except: |
903 | 913 | rdclass = dns.rdataclass.IN |
904 | 914 | # Type |
905 | rdtype = dns.rdatatype.from_text(token[1]) | |
915 | rdtype = dns.rdatatype.from_text(token.value) | |
906 | 916 | 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(): | |
908 | 918 | self.tok.unget(token) |
909 | 919 | rd = dns.rdata.from_text(rdclass, rdtype, self.tok, None) |
910 | 920 | covers = rd.covers() |
925 | 935 | section = None |
926 | 936 | while 1: |
927 | 937 | 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(): | |
929 | 939 | break |
930 | if token[0] == dns.tokenizer.COMMENT: | |
931 | u = token[1].upper() | |
940 | if token.is_comment(): | |
941 | u = token.value.upper() | |
932 | 942 | if u == 'HEADER': |
933 | 943 | line_method = self._header_line |
934 | 944 | elif u == 'QUESTION' or u == 'ZONE': |
1056 | 1066 | @rtype: dns.message.Message object""" |
1057 | 1067 | |
1058 | 1068 | 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') | |
1060 | 1070 | response = dns.message.Message(query.id) |
1061 | 1071 | response.flags = dns.flags.QR | (query.flags & dns.flags.RD) |
1062 | 1072 | if recursion_available: |
0 | # Copyright (C) 2001-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
106 | 106 | @raises LabelTooLong: an individual label is too long |
107 | 107 | @raises EmptyLabel: a label is empty (i.e. the root label) and appears |
108 | 108 | in a position other than the end of the label sequence""" |
109 | ||
109 | ||
110 | 110 | l = len(labels) |
111 | 111 | total = 0 |
112 | 112 | i = -1 |
129 | 129 | |
130 | 130 | The dns.name.Name class represents a DNS name as a tuple of labels. |
131 | 131 | Instances of the class are immutable. |
132 | ||
132 | ||
133 | 133 | @ivar labels: The tuple of labels in the name. Each label is a string of |
134 | 134 | up to 63 octets.""" |
135 | 135 | |
136 | 136 | __slots__ = ['labels'] |
137 | ||
137 | ||
138 | 138 | def __init__(self, labels): |
139 | 139 | """Initialize a domain name from a list of labels. |
140 | 140 | @param labels: the labels |
141 | 141 | @type labels: any iterable whose values are strings |
142 | 142 | """ |
143 | ||
143 | ||
144 | 144 | super(Name, self).__setattr__('labels', tuple(labels)) |
145 | 145 | _validate_labels(self.labels) |
146 | 146 | |
147 | 147 | def __setattr__(self, name, value): |
148 | raise TypeError, "object doesn't support attribute assignment" | |
148 | raise TypeError("object doesn't support attribute assignment") | |
149 | 149 | |
150 | 150 | def is_absolute(self): |
151 | 151 | """Is the most significant label of this name the root label? |
152 | 152 | @rtype: bool |
153 | 153 | """ |
154 | ||
154 | ||
155 | 155 | return len(self.labels) > 0 and self.labels[-1] == '' |
156 | 156 | |
157 | 157 | def is_wild(self): |
158 | 158 | """Is this name wild? (I.e. Is the least significant label '*'?) |
159 | 159 | @rtype: bool |
160 | 160 | """ |
161 | ||
161 | ||
162 | 162 | return len(self.labels) > 0 and self.labels[0] == '*' |
163 | 163 | |
164 | 164 | def __hash__(self): |
165 | 165 | """Return a case-insensitive hash of the name. |
166 | 166 | @rtype: int |
167 | 167 | """ |
168 | ||
168 | ||
169 | 169 | h = 0L |
170 | 170 | for label in self.labels: |
171 | 171 | for c in label: |
184 | 184 | 0 if self == other. A relative name is always less than an |
185 | 185 | absolute name. If both names have the same relativity, then |
186 | 186 | the DNSSEC order relation is used to order them. |
187 | ||
187 | ||
188 | 188 | I{nlabels} is the number of significant labels that the two names |
189 | 189 | have in common. |
190 | 190 | """ |
239 | 239 | The notion of subdomain includes equality. |
240 | 240 | @rtype: bool |
241 | 241 | """ |
242 | ||
242 | ||
243 | 243 | (nr, o, nl) = self.fullcompare(other) |
244 | 244 | if nr == NAMERELN_SUBDOMAIN or nr == NAMERELN_EQUAL: |
245 | 245 | return True |
251 | 251 | The notion of subdomain includes equality. |
252 | 252 | @rtype: bool |
253 | 253 | """ |
254 | ||
254 | ||
255 | 255 | (nr, o, nl) = self.fullcompare(other) |
256 | 256 | if nr == NAMERELN_SUPERDOMAIN or nr == NAMERELN_EQUAL: |
257 | 257 | return True |
262 | 262 | DNSSEC canonical form. |
263 | 263 | @rtype: dns.name.Name object |
264 | 264 | """ |
265 | ||
265 | ||
266 | 266 | return Name([x.lower() for x in self.labels]) |
267 | 267 | |
268 | 268 | def __eq__(self, other): |
303 | 303 | |
304 | 304 | def __repr__(self): |
305 | 305 | return '<DNS name ' + self.__str__() + '>' |
306 | ||
306 | ||
307 | 307 | def __str__(self): |
308 | 308 | return self.to_text(False) |
309 | 309 | |
313 | 313 | root label) for absolute names. The default is False. |
314 | 314 | @rtype: string |
315 | 315 | """ |
316 | ||
316 | ||
317 | 317 | if len(self.labels) == 0: |
318 | 318 | return '@' |
319 | 319 | if len(self.labels) == 1 and self.labels[0] == '': |
334 | 334 | root label) for absolute names. The default is False. |
335 | 335 | @rtype: string |
336 | 336 | """ |
337 | ||
337 | ||
338 | 338 | if len(self.labels) == 0: |
339 | 339 | return u'@' |
340 | 340 | if len(self.labels) == 1 and self.labels[0] == '': |
350 | 350 | """Convert name to a format suitable for digesting in hashes. |
351 | 351 | |
352 | 352 | The name is canonicalized and converted to uncompressed wire format. |
353 | ||
353 | ||
354 | 354 | @param origin: If the name is relative and origin is not None, then |
355 | 355 | origin will be appended to it. |
356 | 356 | @type origin: dns.name.Name object |
359 | 359 | if it is missing, then this exception is raised |
360 | 360 | @rtype: string |
361 | 361 | """ |
362 | ||
362 | ||
363 | 363 | if not self.is_absolute(): |
364 | 364 | if origin is None or not origin.is_absolute(): |
365 | 365 | raise NeedAbsoluteNameOrOrigin |
369 | 369 | labels = self.labels |
370 | 370 | dlabels = ["%s%s" % (chr(len(x)), x.lower()) for x in labels] |
371 | 371 | return ''.join(dlabels) |
372 | ||
372 | ||
373 | 373 | def to_wire(self, file = None, compress = None, origin = None): |
374 | 374 | """Convert name to wire format, possibly compressing it. |
375 | 375 | |
430 | 430 | """The length of the name (in labels). |
431 | 431 | @rtype: int |
432 | 432 | """ |
433 | ||
433 | ||
434 | 434 | return len(self.labels) |
435 | 435 | |
436 | 436 | def __getitem__(self, index): |
455 | 455 | @returns: the tuple (prefix, suffix) |
456 | 456 | @rtype: tuple |
457 | 457 | """ |
458 | ||
458 | ||
459 | 459 | l = len(self.labels) |
460 | 460 | if depth == 0: |
461 | 461 | return (self, dns.name.empty) |
462 | 462 | elif depth == l: |
463 | 463 | return (dns.name.empty, self) |
464 | 464 | 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') | |
467 | 466 | return (Name(self[: -depth]), Name(self[-depth :])) |
468 | 467 | |
469 | 468 | def concatenate(self, other): |
472 | 471 | @raises AbsoluteConcatenation: self is absolute and other is |
473 | 472 | not the empty name |
474 | 473 | """ |
475 | ||
474 | ||
476 | 475 | if self.is_absolute() and len(other) > 0: |
477 | 476 | raise AbsoluteConcatenation |
478 | 477 | labels = list(self.labels) |
484 | 483 | relative to origin. Otherwise return self. |
485 | 484 | @rtype: dns.name.Name object |
486 | 485 | """ |
487 | ||
486 | ||
488 | 487 | if not origin is None and self.is_subdomain(origin): |
489 | 488 | return Name(self[: -len(origin)]) |
490 | 489 | else: |
495 | 494 | concatenation of self and origin. Otherwise return self. |
496 | 495 | @rtype: dns.name.Name object |
497 | 496 | """ |
498 | ||
497 | ||
499 | 498 | if not self.is_absolute(): |
500 | 499 | return self.concatenate(origin) |
501 | 500 | else: |
508 | 507 | false the name is derelativized. |
509 | 508 | @rtype: dns.name.Name object |
510 | 509 | """ |
511 | ||
510 | ||
512 | 511 | if origin: |
513 | 512 | if relativize: |
514 | 513 | return self.relativize(origin) |
526 | 525 | if self == root or self == empty: |
527 | 526 | raise NoParent |
528 | 527 | return Name(self.labels[1:]) |
529 | ||
528 | ||
530 | 529 | root = Name(['']) |
531 | 530 | empty = Name([]) |
532 | 531 | |
534 | 533 | """Convert unicode text into a Name object. |
535 | 534 | |
536 | 535 | Lables are encoded in IDN ACE form. |
537 | ||
536 | ||
538 | 537 | @rtype: dns.name.Name object |
539 | 538 | """ |
540 | 539 | |
541 | 540 | 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") | |
543 | 542 | 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") | |
545 | 544 | labels = [] |
546 | 545 | label = u'' |
547 | 546 | escaping = False |
601 | 600 | if isinstance(text, unicode) and sys.hexversion >= 0x02030000: |
602 | 601 | return from_unicode(text, origin) |
603 | 602 | else: |
604 | raise ValueError, "input to from_text() must be a string" | |
603 | raise ValueError("input to from_text() must be a string") | |
605 | 604 | 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") | |
607 | 606 | labels = [] |
608 | 607 | label = '' |
609 | 608 | escaping = False |
669 | 668 | """ |
670 | 669 | |
671 | 670 | 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") | |
673 | 672 | labels = [] |
674 | 673 | biggest_pointer = current |
675 | 674 | hops = 0 |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
30 | 30 | |
31 | 31 | def __setitem__(self, key, value): |
32 | 32 | 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') | |
34 | 34 | depth = len(key) |
35 | 35 | if depth > self.max_depth: |
36 | 36 | self.max_depth = depth |
38 | 38 | |
39 | 39 | def get_deepest_match(self, name): |
40 | 40 | """Find the deepest match to I{name} in the dictionary. |
41 | ||
41 | ||
42 | 42 | The deepest match is the longest name in the dictionary which is |
43 | 43 | a superdomain of I{name}. |
44 | 44 | |
46 | 46 | @type name: dns.name.Name object |
47 | 47 | @rtype: (key, value) tuple |
48 | 48 | """ |
49 | ||
49 | ||
50 | 50 | depth = len(name) |
51 | 51 | if depth > self.max_depth: |
52 | 52 | depth = self.max_depth |
0 | # Copyright (C) 2001-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
139 | 139 | from_address[1:] == destination[1:]): |
140 | 140 | break |
141 | 141 | 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)) | |
145 | 145 | finally: |
146 | 146 | s.close() |
147 | 147 | r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, |
185 | 185 | if v[0] != errno.EINPROGRESS and \ |
186 | 186 | v[0] != errno.EWOULDBLOCK and \ |
187 | 187 | v[0] != errno.EALREADY: |
188 | raise ty, v | |
188 | raise v | |
189 | 189 | |
190 | 190 | def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, |
191 | 191 | one_rr_per_rrset=False): |
257 | 257 | def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN, |
258 | 258 | timeout=None, port=53, keyring=None, keyname=None, relativize=True, |
259 | 259 | af=None, lifetime=None, source=None, source_port=0, serial=0, |
260 | use_udp=False): | |
260 | use_udp=False, keyalgorithm=dns.tsig.default_algorithm): | |
261 | 261 | """Return a generator for the responses to a zone transfer. |
262 | 262 | |
263 | 263 | @param where: where to send the message |
302 | 302 | @type serial: int |
303 | 303 | @param use_udp: Use UDP (only meaningful for IXFR) |
304 | 304 | @type use_udp: bool |
305 | @param keyalgorithm: The TSIG algorithm to use; defaults to | |
306 | dns.tsig.default_algorithm | |
307 | @type keyalgorithm: string | |
305 | 308 | """ |
306 | 309 | |
307 | 310 | if isinstance(zone, (str, unicode)): |
314 | 317 | '. . %u 0 0 0 0' % serial) |
315 | 318 | q.authority.append(rrset) |
316 | 319 | if not keyring is None: |
317 | q.use_tsig(keyring, keyname) | |
320 | q.use_tsig(keyring, keyname, algorithm=keyalgorithm) | |
318 | 321 | wire = q.to_wire() |
319 | 322 | if af is None: |
320 | 323 | try: |
331 | 334 | source = (source, source_port, 0, 0) |
332 | 335 | if use_udp: |
333 | 336 | if rdtype != dns.rdatatype.IXFR: |
334 | raise ValueError, 'cannot do a UDP AXFR' | |
337 | raise ValueError('cannot do a UDP AXFR') | |
335 | 338 | s = socket.socket(af, socket.SOCK_DGRAM, 0) |
336 | 339 | else: |
337 | 340 | s = socket.socket(af, socket.SOCK_STREAM, 0) |
341 | s.setblocking(0) | |
338 | 342 | if source is not None: |
339 | 343 | s.bind(source) |
340 | 344 | expiration = _compute_expiration(lifetime) |
382 | 386 | raise dns.exception.FormError |
383 | 387 | rrset = r.answer[0] |
384 | 388 | 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") | |
386 | 390 | answer_index = 1 |
387 | 391 | soa_rrset = rrset.copy() |
388 | 392 | if rdtype == dns.rdatatype.IXFR: |
399 | 403 | # |
400 | 404 | for rrset in r.answer[answer_index:]: |
401 | 405 | if done: |
402 | raise dns.exception.FormError, "answers after final SOA" | |
406 | raise dns.exception.FormError("answers after final SOA") | |
403 | 407 | if rrset.rdtype == dns.rdatatype.SOA and rrset.name == oname: |
404 | 408 | if expecting_SOA: |
405 | 409 | if rrset[0].serial != serial: |
406 | raise dns.exception.FormError, \ | |
407 | "IXFR base serial mismatch" | |
410 | raise dns.exception.FormError("IXFR base serial mismatch") | |
408 | 411 | expecting_SOA = False |
409 | 412 | elif rdtype == dns.rdatatype.IXFR: |
410 | 413 | delete_mode = not delete_mode |
419 | 422 | rdtype = dns.rdatatype.AXFR |
420 | 423 | expecting_SOA = False |
421 | 424 | if done and q.keyring and not r.had_tsig: |
422 | raise dns.exception.FormError, "missing TSIG" | |
425 | raise dns.exception.FormError("missing TSIG") | |
423 | 426 | yield r |
424 | 427 | s.close() |
0 | # Copyright (C) 2001-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
57 | 57 | |
58 | 58 | def from_text(text): |
59 | 59 | """Convert text into an rcode. |
60 | ||
60 | ||
61 | 61 | @param text: the texual rcode |
62 | 62 | @type text: string |
63 | 63 | @raises UnknownRcode: the rcode is unknown |
86 | 86 | |
87 | 87 | value = (flags & 0x000f) | ((ednsflags >> 20) & 0xff0) |
88 | 88 | if value < 0 or value > 4095: |
89 | raise ValueError, 'rcode must be >= 0 and <= 4095' | |
89 | raise ValueError('rcode must be >= 0 and <= 4095') | |
90 | 90 | return value |
91 | 91 | |
92 | 92 | def to_flags(value): |
99 | 99 | """ |
100 | 100 | |
101 | 101 | if value < 0 or value > 4095: |
102 | raise ValueError, 'rcode must be >= 0 and <= 4095' | |
102 | raise ValueError('rcode must be >= 0 and <= 4095') | |
103 | 103 | v = value & 0xf |
104 | 104 | ev = long(value & 0xff0) << 20 |
105 | 105 | return (v, ev) |
111 | 111 | @type value: int |
112 | 112 | @rtype: string |
113 | 113 | """ |
114 | ||
114 | ||
115 | 115 | text = _by_value.get(value) |
116 | 116 | if text is None: |
117 | 117 | text = str(value) |
0 | # Copyright (C) 2001-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
318 | 318 | return r'\# %d ' % len(self.data) + _hexify(self.data) |
319 | 319 | |
320 | 320 | 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 \#') | |
324 | 324 | length = tok.get_int() |
325 | 325 | chunks = [] |
326 | 326 | 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(): | |
329 | 329 | break |
330 | chunks.append(value) | |
330 | chunks.append(token.value) | |
331 | 331 | hex = ''.join(chunks) |
332 | 332 | data = hex.decode('hex_codec') |
333 | 333 | 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') | |
336 | 335 | return cls(rdclass, rdtype, data) |
337 | 336 | |
338 | 337 | from_text = classmethod(from_text) |
414 | 413 | # peek at first token |
415 | 414 | token = tok.get() |
416 | 415 | 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'\#': | |
419 | 418 | # |
420 | 419 | # Known type using the generic syntax. Extract the |
421 | 420 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
76 | 76 | @raises dns.rdataclass.UnknownRdataClass: the class is unknown |
77 | 77 | @raises ValueError: the rdata class value is not >= 0 and <= 65535 |
78 | 78 | """ |
79 | ||
79 | ||
80 | 80 | value = _by_text.get(text.upper()) |
81 | 81 | if value is None: |
82 | 82 | match = _unknown_class_pattern.match(text) |
84 | 84 | raise UnknownRdataclass |
85 | 85 | value = int(match.group(1)) |
86 | 86 | 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") | |
88 | 88 | return value |
89 | 89 | |
90 | 90 | def to_text(value): |
96 | 96 | """ |
97 | 97 | |
98 | 98 | 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") | |
100 | 100 | text = _by_value.get(value) |
101 | 101 | if text is None: |
102 | 102 | text = 'CLASS' + `value` |
107 | 107 | @param rdclass: the rdata class |
108 | 108 | @type rdclass: int |
109 | 109 | @rtype: bool""" |
110 | ||
110 | ||
111 | 111 | if _metaclasses.has_key(rdclass): |
112 | 112 | return True |
113 | 113 | return False |
0 | # Copyright (C) 2001-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
57 | 57 | """ |
58 | 58 | |
59 | 59 | __slots__ = ['rdclass', 'rdtype', 'covers', 'ttl'] |
60 | ||
60 | ||
61 | 61 | def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE): |
62 | 62 | """Create a new rdataset of the specified class and type. |
63 | 63 | |
64 | 64 | @see: the description of the class instance variables for the |
65 | 65 | meaning of I{rdclass} and I{rdtype}""" |
66 | ||
66 | ||
67 | 67 | super(Rdataset, self).__init__() |
68 | 68 | self.rdclass = rdclass |
69 | 69 | self.rdtype = rdtype |
84 | 84 | to the specified TTL. |
85 | 85 | @param ttl: The TTL |
86 | 86 | @type ttl: int""" |
87 | ||
87 | ||
88 | 88 | if len(self) == 0: |
89 | 89 | self.ttl = ttl |
90 | 90 | elif ttl < self.ttl: |
95 | 95 | |
96 | 96 | If the optional I{ttl} parameter is supplied, then |
97 | 97 | self.update_ttl(ttl) will be called prior to adding the rdata. |
98 | ||
98 | ||
99 | 99 | @param rd: The rdata |
100 | 100 | @type rd: dns.rdata.Rdata object |
101 | 101 | @param ttl: The TTL |
102 | 102 | @type ttl: int""" |
103 | ||
103 | ||
104 | 104 | # |
105 | 105 | # If we're adding a signature, do some special handling to |
106 | 106 | # check that the signature covers the same type as the |
135 | 135 | |
136 | 136 | @param other: The rdataset from which to update |
137 | 137 | @type other: dns.rdataset.Rdataset object""" |
138 | ||
138 | ||
139 | 139 | self.update_ttl(other.ttl) |
140 | 140 | super(Rdataset, self).update(other) |
141 | 141 | |
146 | 146 | ctext = '(' + dns.rdatatype.to_text(self.covers) + ')' |
147 | 147 | return '<DNS ' + dns.rdataclass.to_text(self.rdclass) + ' ' + \ |
148 | 148 | dns.rdatatype.to_text(self.rdtype) + ctext + ' rdataset>' |
149 | ||
149 | ||
150 | 150 | def __str__(self): |
151 | 151 | return self.to_text() |
152 | 152 | |
154 | 154 | """Two rdatasets are equal if they have the same class, type, and |
155 | 155 | covers, and contain the same rdata. |
156 | 156 | @rtype: bool""" |
157 | ||
157 | ||
158 | 158 | if not isinstance(other, Rdataset): |
159 | 159 | return False |
160 | 160 | if self.rdclass != other.rdclass or \ |
162 | 162 | self.covers != other.covers: |
163 | 163 | return False |
164 | 164 | return super(Rdataset, self).__eq__(other) |
165 | ||
165 | ||
166 | 166 | def __ne__(self, other): |
167 | 167 | return not self.__eq__(other) |
168 | 168 | |
176 | 176 | |
177 | 177 | Any additional keyword arguments are passed on to the rdata |
178 | 178 | to_text() method. |
179 | ||
179 | ||
180 | 180 | @param name: If name is not None, emit a RRs with I{name} as |
181 | 181 | the owner name. |
182 | 182 | @type name: dns.name.Name object |
263 | 263 | file.write(stuff) |
264 | 264 | file.seek(0, 2) |
265 | 265 | return len(self) |
266 | ||
266 | ||
267 | 267 | def match(self, rdclass, rdtype, covers): |
268 | 268 | """Returns True if this rdataset matches the specified class, type, |
269 | 269 | and covers""" |
290 | 290 | rd = dns.rdata.from_text(r.rdclass, r.rdtype, t) |
291 | 291 | r.add(rd) |
292 | 292 | return r |
293 | ||
293 | ||
294 | 294 | def from_text(rdclass, rdtype, ttl, *text_rdatas): |
295 | 295 | """Create an rdataset with the specified class, type, and TTL, and with |
296 | 296 | the specified rdatas in text format. |
297 | ||
297 | ||
298 | 298 | @rtype: dns.rdataset.Rdataset object |
299 | 299 | """ |
300 | 300 | |
303 | 303 | def from_rdata_list(ttl, rdatas): |
304 | 304 | """Create an rdataset with the specified TTL, and with |
305 | 305 | the specified list of rdata objects. |
306 | ||
306 | ||
307 | 307 | @rtype: dns.rdataset.Rdataset object |
308 | 308 | """ |
309 | 309 | |
310 | 310 | if len(rdatas) == 0: |
311 | raise ValueError, "rdata list must not be empty" | |
311 | raise ValueError("rdata list must not be empty") | |
312 | 312 | r = None |
313 | 313 | for rd in rdatas: |
314 | 314 | if r is None: |
317 | 317 | first_time = False |
318 | 318 | r.add(rd) |
319 | 319 | return r |
320 | ||
320 | ||
321 | 321 | def from_rdata(ttl, *rdatas): |
322 | 322 | """Create an rdataset with the specified TTL, and with |
323 | 323 | the specified rdata objects. |
324 | ||
324 | ||
325 | 325 | @rtype: dns.rdataset.Rdataset object |
326 | 326 | """ |
327 | 327 |
0 | # Copyright (C) 2001-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
193 | 193 | raise UnknownRdatatype |
194 | 194 | value = int(match.group(1)) |
195 | 195 | 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") | |
197 | 197 | return value |
198 | 198 | |
199 | 199 | def to_text(value): |
204 | 204 | @rtype: string""" |
205 | 205 | |
206 | 206 | 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") | |
208 | 208 | text = _by_value.get(value) |
209 | 209 | if text is None: |
210 | 210 | text = 'TYPE' + `value` |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
62 | 62 | @see: RFC 2538""" |
63 | 63 | |
64 | 64 | __slots__ = ['certificate_type', 'key_tag', 'algorithm', 'certificate'] |
65 | ||
65 | ||
66 | 66 | def __init__(self, rdclass, rdtype, certificate_type, key_tag, algorithm, |
67 | 67 | certificate): |
68 | 68 | super(CERT, self).__init__(rdclass, rdtype) |
76 | 76 | return "%s %d %s %s" % (certificate_type, self.key_tag, |
77 | 77 | dns.dnssec.algorithm_to_text(self.algorithm), |
78 | 78 | dns.rdata._base64ify(self.certificate)) |
79 | ||
79 | ||
80 | 80 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): |
81 | 81 | certificate_type = _ctype_from_text(tok.get_string()) |
82 | 82 | key_tag = tok.get_uint16() |
83 | 83 | algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) |
84 | 84 | if algorithm < 0 or algorithm > 255: |
85 | raise dns.exception.SyntaxError, "bad algorithm type" | |
85 | raise dns.exception.SyntaxError("bad algorithm type") | |
86 | 86 | chunks = [] |
87 | 87 | 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(): | |
90 | 90 | break |
91 | if t[0] != dns.tokenizer.IDENTIFIER: | |
91 | if not t.is_identifier(): | |
92 | 92 | raise dns.exception.SyntaxError |
93 | chunks.append(t[1]) | |
93 | chunks.append(t.value) | |
94 | 94 | b64 = ''.join(chunks) |
95 | 95 | certificate = b64.decode('base64_codec') |
96 | 96 | return cls(rdclass, rdtype, certificate_type, key_tag, |
97 | 97 | algorithm, certificate) |
98 | ||
98 | ||
99 | 99 | from_text = classmethod(from_text) |
100 | 100 | |
101 | 101 | def to_wire(self, file, compress = None, origin = None): |
103 | 103 | self.algorithm) |
104 | 104 | file.write(prefix) |
105 | 105 | file.write(self.certificate) |
106 | ||
106 | ||
107 | 107 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
108 | 108 | prefix = wire[current : current + 5] |
109 | 109 | current += 5 |
126 | 126 | other.to_wire(f) |
127 | 127 | wire2 = f.getvalue() |
128 | 128 | f.close() |
129 | ||
129 | ||
130 | 130 | return cmp(wire1, wire2) |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
0 | # Copyright (C) 2009 Nominum, Inc. | |
0 | # Copyright (C) 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
57 | 57 | algorithm = tok.get_uint8() |
58 | 58 | hit = tok.get_string().decode('hex-codec') |
59 | 59 | if len(hit) > 255: |
60 | raise dns.exception.SyntaxError, "HIT too long" | |
60 | raise dns.exception.SyntaxError("HIT too long") | |
61 | 61 | key = tok.get_string().decode('base64-codec') |
62 | 62 | servers = [] |
63 | 63 | 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(): | |
66 | 66 | break |
67 | server = dns.name.from_text(value, origin) | |
67 | server = dns.name.from_text(token.value, origin) | |
68 | 68 | server.choose_relativity(origin, relativize) |
69 | 69 | servers.append(server) |
70 | 70 | 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
26 | 26 | @see: RFC 1183""" |
27 | 27 | |
28 | 28 | __slots__ = ['address', 'subaddress'] |
29 | ||
29 | ||
30 | 30 | def __init__(self, rdclass, rdtype, address, subaddress): |
31 | 31 | super(ISDN, self).__init__(rdclass, rdtype) |
32 | 32 | self.address = address |
38 | 38 | dns.rdata._escapify(self.subaddress)) |
39 | 39 | else: |
40 | 40 | return '"%s"' % dns.rdata._escapify(self.address) |
41 | ||
41 | ||
42 | 42 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): |
43 | 43 | address = tok.get_string() |
44 | 44 | t = tok.get() |
45 | if t[0] != dns.tokenizer.EOL and t[0] != dns.tokenizer.EOF: | |
45 | if not t.is_eol_or_eof(): | |
46 | 46 | tok.unget(t) |
47 | 47 | subaddress = tok.get_string() |
48 | 48 | else: |
50 | 50 | subaddress = '' |
51 | 51 | tok.get_eol() |
52 | 52 | return cls(rdclass, rdtype, address, subaddress) |
53 | ||
53 | ||
54 | 54 | from_text = classmethod(from_text) |
55 | 55 | |
56 | 56 | def to_wire(self, file, compress = None, origin = None): |
65 | 65 | byte = chr(l) |
66 | 66 | file.write(byte) |
67 | 67 | file.write(self.subaddress) |
68 | ||
68 | ||
69 | 69 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
70 | 70 | l = ord(wire[current]) |
71 | 71 | current += 1 |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
28 | 28 | exp = i - 1 |
29 | 29 | break |
30 | 30 | 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) | |
32 | 32 | return exp |
33 | 33 | |
34 | 34 | def _float_to_tuple(what): |
68 | 68 | def _decode_size(what, desc): |
69 | 69 | exponent = what & 0x0F |
70 | 70 | if exponent > 9: |
71 | raise dns.exception.SyntaxError, "bad %s exponent" % desc | |
71 | raise dns.exception.SyntaxError("bad %s exponent" % desc) | |
72 | 72 | base = (what & 0xF0) >> 4 |
73 | 73 | if base > 9: |
74 | raise dns.exception.SyntaxError, "bad %s base" % desc | |
74 | raise dns.exception.SyntaxError("bad %s base" % desc) | |
75 | 75 | return long(base) * pow(10, exponent) |
76 | 76 | |
77 | 77 | class LOC(dns.rdata.Rdata): |
95 | 95 | |
96 | 96 | __slots__ = ['latitude', 'longitude', 'altitude', 'size', |
97 | 97 | 'horizontal_precision', 'vertical_precision'] |
98 | ||
98 | ||
99 | 99 | def __init__(self, rdclass, rdtype, latitude, longitude, altitude, |
100 | 100 | size=1.0, hprec=10000.0, vprec=10.0): |
101 | 101 | """Initialize a LOC record instance. |
104 | 104 | of integers specifying (degrees, minutes, seconds, milliseconds), |
105 | 105 | or they may be floating point values specifying the number of |
106 | 106 | degrees. The other parameters are floats.""" |
107 | ||
107 | ||
108 | 108 | super(LOC, self).__init__(rdclass, rdtype) |
109 | 109 | if isinstance(latitude, int) or isinstance(latitude, long): |
110 | 110 | latitude = float(latitude) |
147 | 147 | self.vertical_precision / 100.0 |
148 | 148 | ) |
149 | 149 | return text |
150 | ||
150 | ||
151 | 151 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): |
152 | 152 | latitude = [0, 0, 0, 0] |
153 | 153 | longitude = [0, 0, 0, 0] |
154 | 154 | size = 1.0 |
155 | 155 | hprec = 10000.0 |
156 | 156 | vprec = 10.0 |
157 | ||
157 | ||
158 | 158 | latitude[0] = tok.get_int() |
159 | 159 | t = tok.get_string() |
160 | 160 | if t.isdigit(): |
163 | 163 | if '.' in t: |
164 | 164 | (seconds, milliseconds) = t.split('.') |
165 | 165 | if not seconds.isdigit(): |
166 | raise dns.exception.SyntaxError, \ | |
167 | 'bad latitude seconds value' | |
166 | raise dns.exception.SyntaxError('bad latitude seconds value') | |
168 | 167 | latitude[2] = int(seconds) |
169 | 168 | if latitude[2] >= 60: |
170 | raise dns.exception.SyntaxError, \ | |
171 | 'latitude seconds >= 60' | |
169 | raise dns.exception.SyntaxError('latitude seconds >= 60') | |
172 | 170 | l = len(milliseconds) |
173 | 171 | 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') | |
176 | 173 | if l == 1: |
177 | 174 | m = 100 |
178 | 175 | elif l == 2: |
187 | 184 | if t == 'S': |
188 | 185 | latitude[0] *= -1 |
189 | 186 | elif t != 'N': |
190 | raise dns.exception.SyntaxError, 'bad latitude hemisphere value' | |
187 | raise dns.exception.SyntaxError('bad latitude hemisphere value') | |
191 | 188 | |
192 | 189 | longitude[0] = tok.get_int() |
193 | 190 | t = tok.get_string() |
197 | 194 | if '.' in t: |
198 | 195 | (seconds, milliseconds) = t.split('.') |
199 | 196 | if not seconds.isdigit(): |
200 | raise dns.exception.SyntaxError, \ | |
201 | 'bad longitude seconds value' | |
197 | raise dns.exception.SyntaxError('bad longitude seconds value') | |
202 | 198 | longitude[2] = int(seconds) |
203 | 199 | if longitude[2] >= 60: |
204 | raise dns.exception.SyntaxError, \ | |
205 | 'longitude seconds >= 60' | |
200 | raise dns.exception.SyntaxError('longitude seconds >= 60') | |
206 | 201 | l = len(milliseconds) |
207 | 202 | 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') | |
210 | 204 | if l == 1: |
211 | 205 | m = 100 |
212 | 206 | elif l == 2: |
221 | 215 | if t == 'W': |
222 | 216 | longitude[0] *= -1 |
223 | 217 | elif t != 'E': |
224 | raise dns.exception.SyntaxError, 'bad longitude hemisphere value' | |
218 | raise dns.exception.SyntaxError('bad longitude hemisphere value') | |
225 | 219 | |
226 | 220 | t = tok.get_string() |
227 | 221 | if t[-1] == 'm': |
228 | 222 | t = t[0 : -1] |
229 | 223 | altitude = float(t) * 100.0 # m -> cm |
230 | 224 | |
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 | |
233 | 228 | if value[-1] == 'm': |
234 | 229 | value = value[0 : -1] |
235 | 230 | 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 | |
238 | 234 | if value[-1] == 'm': |
239 | 235 | value = value[0 : -1] |
240 | 236 | 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 | |
243 | 240 | if value[-1] == 'm': |
244 | 241 | value = value[0 : -1] |
245 | 242 | 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() | |
251 | 244 | |
252 | 245 | return cls(rdclass, rdtype, latitude, longitude, altitude, |
253 | 246 | size, hprec, vprec) |
254 | ||
247 | ||
255 | 248 | from_text = classmethod(from_text) |
256 | 249 | |
257 | 250 | def to_wire(self, file, compress = None, origin = None): |
261 | 254 | else: |
262 | 255 | sign = 1 |
263 | 256 | degrees = long(self.latitude[0]) |
264 | milliseconds = (degrees * 3600000 + | |
257 | milliseconds = (degrees * 3600000 + | |
265 | 258 | self.latitude[1] * 60000 + |
266 | 259 | self.latitude[2] * 1000 + |
267 | 260 | self.latitude[3]) * sign |
272 | 265 | else: |
273 | 266 | sign = 1 |
274 | 267 | degrees = long(self.longitude[0]) |
275 | milliseconds = (degrees * 3600000 + | |
268 | milliseconds = (degrees * 3600000 + | |
276 | 269 | self.longitude[1] * 60000 + |
277 | 270 | self.longitude[2] * 1000 + |
278 | 271 | self.longitude[3]) * sign |
284 | 277 | wire = struct.pack("!BBBBIII", 0, size, hprec, vprec, latitude, |
285 | 278 | longitude, altitude) |
286 | 279 | file.write(wire) |
287 | ||
280 | ||
288 | 281 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
289 | 282 | (version, size, hprec, vprec, latitude, longitude, altitude) = \ |
290 | 283 | struct.unpack("!BBBBIII", wire[current : current + rdlen]) |
293 | 286 | else: |
294 | 287 | latitude = -1 * float(0x80000000L - latitude) / 3600000 |
295 | 288 | if latitude < -90.0 or latitude > 90.0: |
296 | raise dns.exception.FormError, "bad latitude" | |
289 | raise dns.exception.FormError("bad latitude") | |
297 | 290 | if longitude > 0x80000000L: |
298 | 291 | longitude = float(longitude - 0x80000000L) / 3600000 |
299 | 292 | else: |
300 | 293 | longitude = -1 * float(0x80000000L - longitude) / 3600000 |
301 | 294 | if longitude < -180.0 or longitude > 180.0: |
302 | raise dns.exception.FormError, "bad longitude" | |
295 | raise dns.exception.FormError("bad longitude") | |
303 | 296 | altitude = float(altitude) - 10000000.0 |
304 | 297 | size = _decode_size(size, "size") |
305 | 298 | hprec = _decode_size(hprec, "horizontal precision") |
318 | 311 | other.to_wire(f) |
319 | 312 | wire2 = f.getvalue() |
320 | 313 | f.close() |
321 | ||
314 | ||
322 | 315 | return cmp(wire1, wire2) |
323 | 316 | |
324 | 317 | def _get_float_latitude(self): |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
28 | 28 | @type windows: list of (window number, string) tuples""" |
29 | 29 | |
30 | 30 | __slots__ = ['next', 'windows'] |
31 | ||
31 | ||
32 | 32 | def __init__(self, rdclass, rdtype, next, windows): |
33 | 33 | super(NSEC, self).__init__(rdclass, rdtype) |
34 | 34 | self.next = next |
47 | 47 | i * 8 + j)) |
48 | 48 | text += (' ' + ' '.join(bits)) |
49 | 49 | return '%s%s' % (next, text) |
50 | ||
50 | ||
51 | 51 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): |
52 | 52 | next = tok.get_name() |
53 | 53 | next = next.choose_relativity(origin, relativize) |
54 | 54 | rdtypes = [] |
55 | 55 | 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(): | |
58 | 58 | break |
59 | nrdtype = dns.rdatatype.from_text(value) | |
59 | nrdtype = dns.rdatatype.from_text(token.value) | |
60 | 60 | if nrdtype == 0: |
61 | raise dns.exception.SyntaxError, "NSEC with bit 0" | |
61 | raise dns.exception.SyntaxError("NSEC with bit 0") | |
62 | 62 | if nrdtype > 65535: |
63 | raise dns.exception.SyntaxError, "NSEC with bit > 65535" | |
63 | raise dns.exception.SyntaxError("NSEC with bit > 65535") | |
64 | 64 | rdtypes.append(nrdtype) |
65 | 65 | rdtypes.sort() |
66 | 66 | window = 0 |
84 | 84 | bitmap[byte] = chr(ord(bitmap[byte]) | (0x80 >> bit)) |
85 | 85 | windows.append((window, ''.join(bitmap[0:octets]))) |
86 | 86 | return cls(rdclass, rdtype, next, windows) |
87 | ||
87 | ||
88 | 88 | from_text = classmethod(from_text) |
89 | 89 | |
90 | 90 | def to_wire(self, file, compress = None, origin = None): |
93 | 93 | file.write(chr(window)) |
94 | 94 | file.write(chr(len(bitmap))) |
95 | 95 | file.write(bitmap) |
96 | ||
96 | ||
97 | 97 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
98 | 98 | (next, cused) = dns.name.from_wire(wire[: current + rdlen], current) |
99 | 99 | current += cused |
101 | 101 | windows = [] |
102 | 102 | while rdlen > 0: |
103 | 103 | if rdlen < 3: |
104 | raise dns.exception.FormError, "NSEC too short" | |
104 | raise dns.exception.FormError("NSEC too short") | |
105 | 105 | window = ord(wire[current]) |
106 | 106 | octets = ord(wire[current + 1]) |
107 | 107 | if octets == 0 or octets > 32: |
108 | raise dns.exception.FormError, "bad NSEC octets" | |
108 | raise dns.exception.FormError("bad NSEC octets") | |
109 | 109 | current += 2 |
110 | 110 | rdlen -= 2 |
111 | 111 | if rdlen < octets: |
112 | raise dns.exception.FormError, "bad NSEC bitmap length" | |
112 | raise dns.exception.FormError("bad NSEC bitmap length") | |
113 | 113 | bitmap = wire[current : current + octets] |
114 | 114 | current += octets |
115 | 115 | rdlen -= octets |
122 | 122 | |
123 | 123 | def choose_relativity(self, origin = None, relativize = True): |
124 | 124 | self.next = self.next.choose_relativity(origin, relativize) |
125 | ||
125 | ||
126 | 126 | def _cmp(self, other): |
127 | 127 | v = cmp(self.next, other.next) |
128 | 128 | if v == 0: |
0 | # Copyright (C) 2004-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
92 | 92 | next = base64.b32decode(next) |
93 | 93 | rdtypes = [] |
94 | 94 | 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(): | |
97 | 97 | break |
98 | nrdtype = dns.rdatatype.from_text(value) | |
98 | nrdtype = dns.rdatatype.from_text(token.value) | |
99 | 99 | if nrdtype == 0: |
100 | raise dns.exception.SyntaxError, "NSEC3 with bit 0" | |
100 | raise dns.exception.SyntaxError("NSEC3 with bit 0") | |
101 | 101 | if nrdtype > 65535: |
102 | raise dns.exception.SyntaxError, "NSEC3 with bit > 65535" | |
102 | raise dns.exception.SyntaxError("NSEC3 with bit > 65535") | |
103 | 103 | rdtypes.append(nrdtype) |
104 | 104 | rdtypes.sort() |
105 | 105 | window = 0 |
156 | 156 | windows = [] |
157 | 157 | while rdlen > 0: |
158 | 158 | if rdlen < 3: |
159 | raise dns.exception.FormError, "NSEC3 too short" | |
159 | raise dns.exception.FormError("NSEC3 too short") | |
160 | 160 | window = ord(wire[current]) |
161 | 161 | octets = ord(wire[current + 1]) |
162 | 162 | if octets == 0 or octets > 32: |
163 | raise dns.exception.FormError, "bad NSEC3 octets" | |
163 | raise dns.exception.FormError("bad NSEC3 octets") | |
164 | 164 | current += 2 |
165 | 165 | rdlen -= 2 |
166 | 166 | if rdlen < octets: |
167 | raise dns.exception.FormError, "bad NSEC3 bitmap length" | |
167 | raise dns.exception.FormError("bad NSEC3 bitmap length") | |
168 | 168 | bitmap = wire[current : current + octets] |
169 | 169 | current += octets |
170 | 170 | rdlen -= octets |
0 | # Copyright (C) 2004-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2004-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
52 | 52 | '\x00', '\x00', '\x00', '\x00', |
53 | 53 | '\x00', '\x00', '\x00', '\x00' ] |
54 | 54 | 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(): | |
57 | 57 | break |
58 | if value.isdigit(): | |
59 | nrdtype = int(value) | |
58 | if token.value.isdigit(): | |
59 | nrdtype = int(token.value) | |
60 | 60 | else: |
61 | nrdtype = dns.rdatatype.from_text(value) | |
61 | nrdtype = dns.rdatatype.from_text(token.value) | |
62 | 62 | if nrdtype == 0: |
63 | raise dns.exception.SyntaxError, "NXT with bit 0" | |
63 | raise dns.exception.SyntaxError("NXT with bit 0") | |
64 | 64 | if nrdtype > 127: |
65 | raise dns.exception.SyntaxError, "NXT with bit > 127" | |
65 | raise dns.exception.SyntaxError("NXT with bit > 127") | |
66 | 66 | i = nrdtype // 8 |
67 | 67 | bitmap[i] = chr(ord(bitmap[i]) | (0x80 >> (nrdtype % 8))) |
68 | 68 | bitmap = dns.rdata._truncate_bitmap(bitmap) |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
24 | 24 | @type address: string (in the standard "dotted quad" format)""" |
25 | 25 | |
26 | 26 | __slots__ = ['address'] |
27 | ||
27 | ||
28 | 28 | def __init__(self, rdclass, rdtype, address): |
29 | 29 | super(A, self).__init__(rdclass, rdtype) |
30 | 30 | # check that it's OK |
33 | 33 | |
34 | 34 | def to_text(self, origin=None, relativize=True, **kw): |
35 | 35 | return self.address |
36 | ||
36 | ||
37 | 37 | 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() | |
42 | 40 | return cls(rdclass, rdtype, address) |
43 | ||
41 | ||
44 | 42 | from_text = classmethod(from_text) |
45 | 43 | |
46 | 44 | def to_wire(self, file, compress = None, origin = None): |
47 | 45 | file.write(dns.ipv4.inet_aton(self.address)) |
48 | ||
46 | ||
49 | 47 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
50 | 48 | address = dns.ipv4.inet_ntoa(wire[current : current + rdlen]) |
51 | 49 | return cls(rdclass, rdtype, address) |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
24 | 24 | @type address: string (in the standard IPv6 format)""" |
25 | 25 | |
26 | 26 | __slots__ = ['address'] |
27 | ||
27 | ||
28 | 28 | def __init__(self, rdclass, rdtype, address): |
29 | 29 | super(AAAA, self).__init__(rdclass, rdtype) |
30 | 30 | # check that it's OK |
33 | 33 | |
34 | 34 | def to_text(self, origin=None, relativize=True, **kw): |
35 | 35 | return self.address |
36 | ||
36 | ||
37 | 37 | 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() | |
42 | 40 | return cls(rdclass, rdtype, address) |
43 | ||
41 | ||
44 | 42 | from_text = classmethod(from_text) |
45 | 43 | |
46 | 44 | def to_wire(self, file, compress = None, origin = None): |
47 | 45 | file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.address)) |
48 | ||
46 | ||
49 | 47 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
50 | 48 | address = dns.inet.inet_ntop(dns.inet.AF_INET6, |
51 | 49 | wire[current : current + rdlen]) |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
22 | 22 | |
23 | 23 | class APLItem(object): |
24 | 24 | """An APL list item. |
25 | ||
25 | ||
26 | 26 | @ivar family: the address family (IANA address family registry) |
27 | 27 | @type family: int |
28 | 28 | @ivar negation: is this item negated? |
34 | 34 | """ |
35 | 35 | |
36 | 36 | __slots__ = ['family', 'negation', 'address', 'prefix'] |
37 | ||
37 | ||
38 | 38 | def __init__(self, family, negation, address, prefix): |
39 | 39 | self.family = family |
40 | 40 | self.negation = negation |
79 | 79 | @see: RFC 3123""" |
80 | 80 | |
81 | 81 | __slots__ = ['items'] |
82 | ||
82 | ||
83 | 83 | def __init__(self, rdclass, rdtype, items): |
84 | 84 | super(APL, self).__init__(rdclass, rdtype) |
85 | 85 | self.items = items |
86 | 86 | |
87 | 87 | def to_text(self, origin=None, relativize=True, **kw): |
88 | 88 | return ' '.join(map(lambda x: str(x), self.items)) |
89 | ||
89 | ||
90 | 90 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): |
91 | 91 | items = [] |
92 | 92 | 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(): | |
95 | 95 | break |
96 | item = token.value | |
96 | 97 | if item[0] == '!': |
97 | 98 | negation = True |
98 | 99 | item = item[1:] |
106 | 107 | items.append(item) |
107 | 108 | |
108 | 109 | return cls(rdclass, rdtype, items) |
109 | ||
110 | ||
110 | 111 | from_text = classmethod(from_text) |
111 | 112 | |
112 | 113 | def to_wire(self, file, compress = None, origin = None): |
113 | 114 | for item in self.items: |
114 | 115 | item.to_wire(file) |
115 | ||
116 | ||
116 | 117 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
117 | 118 | items = [] |
118 | 119 | while 1: |
164 | 165 | other.to_wire(f) |
165 | 166 | wire2 = f.getvalue() |
166 | 167 | f.close() |
167 | ||
168 | ||
168 | 169 | return cmp(wire1, wire2) |
0 | # Copyright (C) 2006, 2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
23 | 23 | @see: RFC 4701""" |
24 | 24 | |
25 | 25 | __slots__ = ['data'] |
26 | ||
26 | ||
27 | 27 | def __init__(self, rdclass, rdtype, data): |
28 | 28 | super(DHCID, self).__init__(rdclass, rdtype) |
29 | 29 | self.data = data |
34 | 34 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): |
35 | 35 | chunks = [] |
36 | 36 | 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(): | |
39 | 39 | break |
40 | if t[0] != dns.tokenizer.IDENTIFIER: | |
40 | if not t.is_identifier(): | |
41 | 41 | raise dns.exception.SyntaxError |
42 | chunks.append(t[1]) | |
42 | chunks.append(t.value) | |
43 | 43 | b64 = ''.join(chunks) |
44 | 44 | data = b64.decode('base64_codec') |
45 | 45 | return cls(rdclass, rdtype, data) |
46 | ||
46 | ||
47 | 47 | from_text = classmethod(from_text) |
48 | 48 | |
49 | 49 | def to_wire(self, file, compress = None, origin = None): |
50 | 50 | file.write(self.data) |
51 | ||
51 | ||
52 | 52 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
53 | 53 | data = wire[current : current + rdlen] |
54 | 54 | return cls(rdclass, rdtype, data) |
56 | 56 | from_wire = classmethod(from_wire) |
57 | 57 | |
58 | 58 | 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
41 | 41 | super(IPSECKEY, self).__init__(rdclass, rdtype) |
42 | 42 | if gateway_type == 0: |
43 | 43 | 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') | |
45 | 45 | gateway = None |
46 | 46 | elif gateway_type == 1: |
47 | 47 | # check that it's OK |
52 | 52 | elif gateway_type == 3: |
53 | 53 | pass |
54 | 54 | else: |
55 | raise SyntaxError, \ | |
56 | 'invalid IPSECKEY gateway type: %d' % gateway_type | |
55 | raise SyntaxError('invalid IPSECKEY gateway type: %d' % gateway_type) | |
57 | 56 | self.precedence = precedence |
58 | 57 | self.gateway_type = gateway_type |
59 | 58 | self.algorithm = algorithm |
70 | 69 | elif self.gateway_type == 3: |
71 | 70 | gateway = str(self.gateway.choose_relativity(origin, relativize)) |
72 | 71 | else: |
73 | raise ValueError, 'invalid gateway type' | |
72 | raise ValueError('invalid gateway type') | |
74 | 73 | return '%d %d %d %s %s' % (self.precedence, self.gateway_type, |
75 | 74 | self.algorithm, gateway, |
76 | 75 | dns.rdata._base64ify(self.key)) |
85 | 84 | gateway = tok.get_string() |
86 | 85 | chunks = [] |
87 | 86 | 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(): | |
90 | 89 | break |
91 | if t[0] != dns.tokenizer.IDENTIFIER: | |
90 | if not t.is_identifier(): | |
92 | 91 | raise dns.exception.SyntaxError |
93 | chunks.append(t[1]) | |
92 | chunks.append(t.value) | |
94 | 93 | b64 = ''.join(chunks) |
95 | 94 | key = b64.decode('base64_codec') |
96 | 95 | return cls(rdclass, rdtype, precedence, gateway_type, algorithm, |
97 | 96 | gateway, key) |
98 | ||
97 | ||
99 | 98 | from_text = classmethod(from_text) |
100 | 99 | |
101 | 100 | def to_wire(self, file, compress = None, origin = None): |
111 | 110 | elif self.gateway_type == 3: |
112 | 111 | self.gateway.to_wire(file, None, origin) |
113 | 112 | else: |
114 | raise ValueError, 'invalid gateway type' | |
113 | raise ValueError('invalid gateway type') | |
115 | 114 | file.write(self.key) |
116 | ||
115 | ||
117 | 116 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
118 | 117 | if rdlen < 3: |
119 | 118 | raise dns.exception.FormError |
139 | 138 | current += cused |
140 | 139 | rdlen -= cused |
141 | 140 | else: |
142 | raise dns.exception.FormError, 'invalid IPSECKEY gateway type' | |
141 | raise dns.exception.FormError('invalid IPSECKEY gateway type') | |
143 | 142 | key = wire[current : current + rdlen] |
144 | 143 | return cls(rdclass, rdtype, header[0], gateway_type, header[2], |
145 | 144 | gateway, key) |
155 | 154 | other.to_wire(f) |
156 | 155 | wire2 = f.getvalue() |
157 | 156 | f.close() |
158 | ||
157 | ||
159 | 158 | return cmp(wire1, wire2) |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
24 | 24 | @see: RFC 1706""" |
25 | 25 | |
26 | 26 | __slots__ = ['address'] |
27 | ||
27 | ||
28 | 28 | def __init__(self, rdclass, rdtype, address): |
29 | 29 | super(NSAP, self).__init__(rdclass, rdtype) |
30 | 30 | self.address = address |
31 | 31 | |
32 | 32 | def to_text(self, origin=None, relativize=True, **kw): |
33 | 33 | return "0x%s" % self.address.encode('hex_codec') |
34 | ||
34 | ||
35 | 35 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): |
36 | 36 | address = tok.get_string() |
37 | 37 | t = tok.get_eol() |
38 | 38 | 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') | |
40 | 40 | address = address[2:].replace('.', '') |
41 | 41 | if len(address) % 2 != 0: |
42 | raise dns.exception.SyntaxError, 'hexstring has odd length' | |
42 | raise dns.exception.SyntaxError('hexstring has odd length') | |
43 | 43 | address = address.decode('hex_codec') |
44 | 44 | return cls(rdclass, rdtype, address) |
45 | ||
45 | ||
46 | 46 | from_text = classmethod(from_text) |
47 | 47 | |
48 | 48 | def to_wire(self, file, compress = None, origin = None): |
49 | 49 | file.write(self.address) |
50 | ||
50 | ||
51 | 51 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
52 | 52 | address = wire[current : current + rdlen] |
53 | 53 | return cls(rdclass, rdtype, address) |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
32 | 32 | @see: RFC 2782""" |
33 | 33 | |
34 | 34 | __slots__ = ['priority', 'weight', 'port', 'target'] |
35 | ||
35 | ||
36 | 36 | def __init__(self, rdclass, rdtype, priority, weight, port, target): |
37 | 37 | super(SRV, self).__init__(rdclass, rdtype) |
38 | 38 | self.priority = priority |
44 | 44 | target = self.target.choose_relativity(origin, relativize) |
45 | 45 | return '%d %d %d %s' % (self.priority, self.weight, self.port, |
46 | 46 | target) |
47 | ||
47 | ||
48 | 48 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): |
49 | 49 | priority = tok.get_uint16() |
50 | 50 | weight = tok.get_uint16() |
53 | 53 | target = target.choose_relativity(origin, relativize) |
54 | 54 | tok.get_eol() |
55 | 55 | return cls(rdclass, rdtype, priority, weight, port, target) |
56 | ||
56 | ||
57 | 57 | from_text = classmethod(from_text) |
58 | 58 | |
59 | 59 | def to_wire(self, file, compress = None, origin = None): |
60 | 60 | three_ints = struct.pack("!HHH", self.priority, self.weight, self.port) |
61 | 61 | file.write(three_ints) |
62 | 62 | self.target.to_wire(file, compress, origin) |
63 | ||
63 | ||
64 | 64 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
65 | 65 | (priority, weight, port) = struct.unpack('!HHH', |
66 | 66 | wire[current : current + 6]) |
81 | 81 | |
82 | 82 | def _cmp(self, other): |
83 | 83 | 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) | |
85 | 85 | v = cmp(sp, op) |
86 | 86 | if v == 0: |
87 | 87 | v = cmp(self.target, other.target) |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
33 | 33 | @see: RFC 1035""" |
34 | 34 | |
35 | 35 | __slots__ = ['address', 'protocol', 'bitmap'] |
36 | ||
36 | ||
37 | 37 | def __init__(self, rdclass, rdtype, address, protocol, bitmap): |
38 | 38 | super(WKS, self).__init__(rdclass, rdtype) |
39 | 39 | self.address = address |
49 | 49 | bits.append(str(i * 8 + j)) |
50 | 50 | text = ' '.join(bits) |
51 | 51 | return '%s %d %s' % (self.address, self.protocol, text) |
52 | ||
52 | ||
53 | 53 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): |
54 | 54 | address = tok.get_string() |
55 | 55 | protocol = tok.get_string() |
59 | 59 | protocol = socket.getprotobyname(protocol) |
60 | 60 | bitmap = [] |
61 | 61 | 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(): | |
64 | 64 | break |
65 | if value.isdigit(): | |
66 | serv = int(value) | |
65 | if token.value.isdigit(): | |
66 | serv = int(token.value) | |
67 | 67 | else: |
68 | 68 | 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") | |
70 | 70 | if protocol == _proto_udp: |
71 | 71 | protocol_text = "udp" |
72 | 72 | else: |
73 | 73 | protocol_text = "tcp" |
74 | serv = socket.getservbyname(value, protocol_text) | |
74 | serv = socket.getservbyname(token.value, protocol_text) | |
75 | 75 | i = serv // 8 |
76 | 76 | l = len(bitmap) |
77 | 77 | if l < i + 1: |
80 | 80 | bitmap[i] = chr(ord(bitmap[i]) | (0x80 >> (serv % 8))) |
81 | 81 | bitmap = dns.rdata._truncate_bitmap(bitmap) |
82 | 82 | return cls(rdclass, rdtype, address, protocol, bitmap) |
83 | ||
83 | ||
84 | 84 | from_text = classmethod(from_text) |
85 | 85 | |
86 | 86 | def to_wire(self, file, compress = None, origin = None): |
88 | 88 | protocol = struct.pack('!B', self.protocol) |
89 | 89 | file.write(protocol) |
90 | 90 | file.write(self.bitmap) |
91 | ||
91 | ||
92 | 92 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
93 | 93 | address = dns.ipv4.inet_ntoa(wire[current : current + 4]) |
94 | 94 | 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
52 | 52 | digest_type = tok.get_uint8() |
53 | 53 | chunks = [] |
54 | 54 | 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(): | |
57 | 57 | break |
58 | if t[0] != dns.tokenizer.IDENTIFIER: | |
58 | if not t.is_identifier(): | |
59 | 59 | raise dns.exception.SyntaxError |
60 | chunks.append(t[1]) | |
60 | chunks.append(t.value) | |
61 | 61 | digest = ''.join(chunks) |
62 | 62 | digest = digest.decode('hex_codec') |
63 | 63 | 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
60 | 60 | 'IPSEC' : 4, |
61 | 61 | 'ALL' : 255, |
62 | 62 | } |
63 | ||
63 | ||
64 | 64 | class KEYBase(dns.rdata.Rdata): |
65 | 65 | """KEY-like record base |
66 | 66 | |
74 | 74 | @type key: string""" |
75 | 75 | |
76 | 76 | __slots__ = ['flags', 'protocol', 'algorithm', 'key'] |
77 | ||
77 | ||
78 | 78 | def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key): |
79 | 79 | super(KEYBase, self).__init__(rdclass, rdtype) |
80 | 80 | self.flags = flags |
96 | 96 | for flag in flag_names: |
97 | 97 | v = _flags_from_text.get(flag) |
98 | 98 | if v is None: |
99 | raise dns.exception.SyntaxError, 'unknown flag %s' % flag | |
99 | raise dns.exception.SyntaxError('unknown flag %s' % flag) | |
100 | 100 | flags &= ~v[1] |
101 | 101 | flags |= v[0] |
102 | 102 | protocol = tok.get_string() |
105 | 105 | else: |
106 | 106 | protocol = _protocol_from_text.get(protocol) |
107 | 107 | 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 | ||
111 | 110 | algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) |
112 | 111 | chunks = [] |
113 | 112 | 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(): | |
116 | 115 | break |
117 | if t[0] != dns.tokenizer.IDENTIFIER: | |
116 | if not t.is_identifier(): | |
118 | 117 | raise dns.exception.SyntaxError |
119 | chunks.append(t[1]) | |
118 | chunks.append(t.value) | |
120 | 119 | b64 = ''.join(chunks) |
121 | 120 | key = b64.decode('base64_codec') |
122 | 121 | return cls(rdclass, rdtype, flags, protocol, algorithm, key) |
123 | ||
122 | ||
124 | 123 | from_text = classmethod(from_text) |
125 | 124 | |
126 | 125 | def to_wire(self, file, compress = None, origin = None): |
127 | 126 | header = struct.pack("!HBB", self.flags, self.protocol, self.algorithm) |
128 | 127 | file.write(header) |
129 | 128 | file.write(self.key) |
130 | ||
129 | ||
131 | 130 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
132 | 131 | if rdlen < 4: |
133 | 132 | raise dns.exception.FormError |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
39 | 39 | |
40 | 40 | def posixtime_to_sigtime(what): |
41 | 41 | return time.strftime('%Y%m%d%H%M%S', time.gmtime(what)) |
42 | ||
42 | ||
43 | 43 | class SIGBase(dns.rdata.Rdata): |
44 | 44 | """SIG-like record base |
45 | 45 | |
65 | 65 | __slots__ = ['type_covered', 'algorithm', 'labels', 'original_ttl', |
66 | 66 | 'expiration', 'inception', 'key_tag', 'signer', |
67 | 67 | 'signature'] |
68 | ||
68 | ||
69 | 69 | def __init__(self, rdclass, rdtype, type_covered, algorithm, labels, |
70 | 70 | original_ttl, expiration, inception, key_tag, signer, |
71 | 71 | signature): |
82 | 82 | |
83 | 83 | def covers(self): |
84 | 84 | return self.type_covered |
85 | ||
85 | ||
86 | 86 | def to_text(self, origin=None, relativize=True, **kw): |
87 | 87 | return '%s %d %d %d %s %s %d %s %s' % ( |
88 | 88 | dns.rdatatype.to_text(self.type_covered), |
108 | 108 | signer = signer.choose_relativity(origin, relativize) |
109 | 109 | chunks = [] |
110 | 110 | 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(): | |
113 | 113 | break |
114 | if t[0] != dns.tokenizer.IDENTIFIER: | |
114 | if not t.is_identifier(): | |
115 | 115 | raise dns.exception.SyntaxError |
116 | chunks.append(t[1]) | |
116 | chunks.append(t.value) | |
117 | 117 | b64 = ''.join(chunks) |
118 | 118 | signature = b64.decode('base64_codec') |
119 | 119 | return cls(rdclass, rdtype, type_covered, algorithm, labels, |
120 | 120 | original_ttl, expiration, inception, key_tag, signer, |
121 | 121 | signature) |
122 | ||
122 | ||
123 | 123 | from_text = classmethod(from_text) |
124 | 124 | |
125 | 125 | def to_wire(self, file, compress = None, origin = None): |
130 | 130 | file.write(header) |
131 | 131 | self.signer.to_wire(file, None, origin) |
132 | 132 | file.write(self.signature) |
133 | ||
133 | ||
134 | 134 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
135 | 135 | header = struct.unpack('!HBBIIIH', wire[current : current + 18]) |
136 | 136 | current += 18 |
149 | 149 | |
150 | 150 | def choose_relativity(self, origin = None, relativize = True): |
151 | 151 | self.signer = self.signer.choose_relativity(origin, relativize) |
152 | ||
152 | ||
153 | 153 | def _cmp(self, other): |
154 | 154 | hs = struct.pack('!HBBIIIH', self.type_covered, |
155 | 155 | self.algorithm, self.labels, |
0 | # Copyright (C) 2006, 2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2006, 2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
26 | 26 | @see: RFC 1035""" |
27 | 27 | |
28 | 28 | __slots__ = ['strings'] |
29 | ||
29 | ||
30 | 30 | def __init__(self, rdclass, rdtype, strings): |
31 | 31 | super(TXTBase, self).__init__(rdclass, rdtype) |
32 | 32 | if isinstance(strings, str): |
40 | 40 | txt += '%s"%s"' % (prefix, dns.rdata._escapify(s)) |
41 | 41 | prefix = ' ' |
42 | 42 | return txt |
43 | ||
43 | ||
44 | 44 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): |
45 | 45 | strings = [] |
46 | 46 | 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(): | |
49 | 49 | 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) | |
56 | 55 | if len(strings) == 0: |
57 | 56 | raise dns.exception.UnexpectedEnd |
58 | 57 | return cls(rdclass, rdtype, strings) |
59 | ||
58 | ||
60 | 59 | from_text = classmethod(from_text) |
61 | 60 | |
62 | 61 | def to_wire(self, file, compress = None, origin = None): |
66 | 65 | byte = chr(l) |
67 | 66 | file.write(byte) |
68 | 67 | file.write(s) |
69 | ||
68 | ||
70 | 69 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
71 | 70 | strings = [] |
72 | 71 | while rdlen > 0: |
0 | # Copyright (C) 2001-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
249 | 249 | self.counts[ADDITIONAL] += 1 |
250 | 250 | |
251 | 251 | def add_tsig(self, keyname, secret, fudge, id, tsig_error, other_data, |
252 | request_mac): | |
252 | request_mac, algorithm=dns.tsig.default_algorithm): | |
253 | 253 | """Add a TSIG signature to the message. |
254 | 254 | |
255 | 255 | @param keyname: the TSIG key name |
256 | 256 | @type keyname: dns.name.Name object |
257 | 257 | @param secret: the secret to use |
258 | 258 | @type secret: string |
259 | @param fudge: TSIG time fudge; default is 300 seconds. | |
259 | @param fudge: TSIG time fudge | |
260 | 260 | @type fudge: int |
261 | 261 | @param id: the message id to encode in the tsig signature |
262 | 262 | @type id: int |
266 | 266 | @type other_data: string |
267 | 267 | @param request_mac: This message is a response to the request which |
268 | 268 | had the specified MAC. |
269 | @param algorithm: the TSIG algorithm to use | |
269 | 270 | @type request_mac: string |
270 | 271 | """ |
271 | 272 | |
272 | 273 | self._set_section(ADDITIONAL) |
273 | 274 | before = self.output.tell() |
274 | 275 | 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) | |
284 | 286 | keyname.to_wire(self.output, self.compress, self.origin) |
285 | 287 | self.output.write(struct.pack('!HHIH', dns.rdatatype.TSIG, |
286 | 288 | dns.rdataclass.ANY, 0, 0)) |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
136 | 136 | elif attr == 'rdtype': |
137 | 137 | return self.rrset.rdtype |
138 | 138 | else: |
139 | raise AttributeError, attr | |
139 | raise AttributeError(attr) | |
140 | 140 | |
141 | 141 | def __len__(self): |
142 | 142 | return len(self.rrset) |
168 | 168 | since the epoch.) |
169 | 169 | @type next_cleaning: float |
170 | 170 | """ |
171 | ||
171 | ||
172 | 172 | def __init__(self, cleaning_interval=300.0): |
173 | 173 | """Initialize a DNS cache. |
174 | 174 | |
176 | 176 | cleanings. The default is 300.0 |
177 | 177 | @type cleaning_interval: float. |
178 | 178 | """ |
179 | ||
179 | ||
180 | 180 | self.data = {} |
181 | 181 | self.cleaning_interval = cleaning_interval |
182 | 182 | self.next_cleaning = time.time() + self.cleaning_interval |
183 | 183 | |
184 | 184 | def maybe_clean(self): |
185 | 185 | """Clean the cache if it's time to do so.""" |
186 | ||
186 | ||
187 | 187 | now = time.time() |
188 | 188 | if self.next_cleaning <= now: |
189 | 189 | keys_to_delete = [] |
194 | 194 | del self.data[k] |
195 | 195 | now = time.time() |
196 | 196 | self.next_cleaning = now + self.cleaning_interval |
197 | ||
197 | ||
198 | 198 | def get(self, key): |
199 | 199 | """Get the answer associated with I{key}. Returns None if |
200 | 200 | no answer is cached for the key. |
203 | 203 | query name, rdtype, and rdclass. |
204 | 204 | @rtype: dns.resolver.Answer object or None |
205 | 205 | """ |
206 | ||
206 | ||
207 | 207 | self.maybe_clean() |
208 | 208 | v = self.data.get(key) |
209 | 209 | if v is None or v.expiration <= time.time(): |
218 | 218 | @param value: The answer being cached |
219 | 219 | @type value: dns.resolver.Answer object |
220 | 220 | """ |
221 | ||
221 | ||
222 | 222 | self.maybe_clean() |
223 | 223 | self.data[key] = value |
224 | 224 | |
231 | 231 | @param key: the key to flush |
232 | 232 | @type key: (dns.name.Name, int, int) tuple or None |
233 | 233 | """ |
234 | ||
234 | ||
235 | 235 | if not key is None: |
236 | 236 | if self.data.has_key(key): |
237 | 237 | del self.data[key] |
264 | 264 | @type keyring: dict |
265 | 265 | @ivar keyname: The TSIG keyname to use. The default is None. |
266 | 266 | @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 | |
267 | 270 | @ivar edns: The EDNS level to use. The default is -1, no Edns. |
268 | 271 | @type edns: int |
269 | 272 | @ivar ednsflags: The EDNS flags |
306 | 309 | self.lifetime = 30.0 |
307 | 310 | self.keyring = None |
308 | 311 | self.keyname = None |
312 | self.keyalgorithm = dns.tsig.default_algorithm | |
309 | 313 | self.edns = -1 |
310 | 314 | self.ednsflags = 0 |
311 | 315 | self.payload = 0 |
544 | 548 | of the appropriate type, or strings that can be converted into objects |
545 | 549 | of the appropriate type. E.g. For I{rdtype} the integer 2 and the |
546 | 550 | the string 'NS' both mean to query for records with DNS rdata type NS. |
547 | ||
551 | ||
548 | 552 | @param qname: the query name |
549 | 553 | @type qname: dns.name.Name object or string |
550 | 554 | @param rdtype: the query type |
561 | 565 | @raises NoAnswer: the response did not contain an answer |
562 | 566 | @raises NoNameservers: no non-broken nameservers are available to |
563 | 567 | answer the question.""" |
564 | ||
568 | ||
565 | 569 | if isinstance(qname, (str, unicode)): |
566 | 570 | qname = dns.name.from_text(qname, None) |
567 | 571 | if isinstance(rdtype, str): |
588 | 592 | return answer |
589 | 593 | request = dns.message.make_query(qname, rdtype, rdclass) |
590 | 594 | if not self.keyname is None: |
591 | request.use_tsig(self.keyring, self.keyname) | |
595 | request.use_tsig(self.keyring, self.keyname, self.keyalgorithm) | |
592 | 596 | request.use_edns(self.edns, self.ednsflags, self.payload) |
593 | 597 | response = None |
594 | 598 | # |
669 | 673 | self.cache.put((qname, rdtype, rdclass), answer) |
670 | 674 | return answer |
671 | 675 | |
672 | def use_tsig(self, keyring, keyname=None): | |
676 | def use_tsig(self, keyring, keyname=None, | |
677 | algorithm=dns.tsig.default_algorithm): | |
673 | 678 | """Add a TSIG signature to the query. |
674 | 679 | |
675 | 680 | @param keyring: The TSIG keyring to use; defaults to None. |
679 | 684 | but a keyname is not, then the key used will be the first key in the |
680 | 685 | keyring. Note that the order of keys in a dictionary is not defined, |
681 | 686 | 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""" | |
683 | 691 | self.keyring = keyring |
684 | 692 | if keyname is None: |
685 | 693 | self.keyname = self.keyring.keys()[0] |
686 | 694 | else: |
687 | 695 | self.keyname = keyname |
696 | self.keyalgorithm = algorithm | |
688 | 697 | |
689 | 698 | def use_edns(self, edns, ednsflags, payload): |
690 | 699 | """Configure Edns. |
739 | 748 | if resolver is None: |
740 | 749 | resolver = get_default_resolver() |
741 | 750 | if not name.is_absolute(): |
742 | raise NotAbsolute, name | |
751 | raise NotAbsolute(name) | |
743 | 752 | while 1: |
744 | 753 | try: |
745 | 754 | 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
71 | 71 | # run through inet_aton() to check syntax and make pretty. |
72 | 72 | return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text)) |
73 | 73 | 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
30 | 30 | """ |
31 | 31 | |
32 | 32 | __slots__ = ['name', 'deleting'] |
33 | ||
33 | ||
34 | 34 | def __init__(self, name, rdclass, rdtype, covers=dns.rdatatype.NONE, |
35 | 35 | deleting=None): |
36 | 36 | """Create a new RRset.""" |
64 | 64 | def __eq__(self, other): |
65 | 65 | """Two RRsets are equal if they have the same name and the same |
66 | 66 | rdataset |
67 | ||
67 | ||
68 | 68 | @rtype: bool""" |
69 | 69 | if not isinstance(other, RRset): |
70 | 70 | return False |
81 | 81 | if self.name != name or self.deleting != deleting: |
82 | 82 | return False |
83 | 83 | return True |
84 | ||
84 | ||
85 | 85 | def to_text(self, origin=None, relativize=True, **kw): |
86 | 86 | """Convert the RRset into DNS master file format. |
87 | 87 | |
91 | 91 | |
92 | 92 | Any additional keyword arguments are passed on to the rdata |
93 | 93 | to_text() method. |
94 | ||
94 | ||
95 | 95 | @param origin: The origin for relative names, or None. |
96 | 96 | @type origin: dns.name.Name object |
97 | 97 | @param relativize: True if names should names be relativized |
120 | 120 | |
121 | 121 | @rtype: dns.rrset.RRset object |
122 | 122 | """ |
123 | ||
123 | ||
124 | 124 | if isinstance(name, (str, unicode)): |
125 | 125 | name = dns.name.from_text(name, None) |
126 | 126 | if isinstance(rdclass, str): |
133 | 133 | rd = dns.rdata.from_text(r.rdclass, r.rdtype, t) |
134 | 134 | r.add(rd) |
135 | 135 | return r |
136 | ||
136 | ||
137 | 137 | def from_text(name, ttl, rdclass, rdtype, *text_rdatas): |
138 | 138 | """Create an RRset with the specified name, TTL, class, and type and with |
139 | 139 | the specified rdatas in text format. |
154 | 154 | name = dns.name.from_text(name, None) |
155 | 155 | |
156 | 156 | if len(rdatas) == 0: |
157 | raise ValueError, "rdata list must not be empty" | |
157 | raise ValueError("rdata list must not be empty") | |
158 | 158 | r = None |
159 | 159 | for rd in rdatas: |
160 | 160 | if r is None: |
163 | 163 | first_time = False |
164 | 164 | r.add(rd) |
165 | 165 | return r |
166 | ||
166 | ||
167 | 167 | def from_rdata(name, ttl, *rdatas): |
168 | 168 | """Create an RRset with the specified name and TTL, and with |
169 | 169 | the specified rdata objects. |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
25 | 25 | @type items: list""" |
26 | 26 | |
27 | 27 | __slots__ = ['items'] |
28 | ||
28 | ||
29 | 29 | def __init__(self, items=None): |
30 | 30 | """Initialize the set. |
31 | 31 | |
32 | 32 | @param items: the initial set of items |
33 | 33 | @type items: any iterable or None |
34 | 34 | """ |
35 | ||
35 | ||
36 | 36 | self.items = [] |
37 | 37 | if not items is None: |
38 | 38 | for item in items: |
40 | 40 | |
41 | 41 | def __repr__(self): |
42 | 42 | return "dns.simpleset.Set(%s)" % repr(self.items) |
43 | ||
43 | ||
44 | 44 | def add(self, item): |
45 | 45 | """Add an item to the set.""" |
46 | 46 | if not item in self.items: |
69 | 69 | return new instances (e.g. union) once, and keep using them in |
70 | 70 | subclasses. |
71 | 71 | """ |
72 | ||
72 | ||
73 | 73 | cls = self.__class__ |
74 | 74 | obj = cls.__new__(cls) |
75 | 75 | obj.items = list(self.items) |
78 | 78 | def __copy__(self): |
79 | 79 | """Make a (shallow) copy of the set.""" |
80 | 80 | return self._clone() |
81 | ||
81 | ||
82 | 82 | def copy(self): |
83 | 83 | """Make a (shallow) copy of the set.""" |
84 | 84 | return self._clone() |
90 | 90 | @type other: Set object |
91 | 91 | """ |
92 | 92 | if not isinstance(other, Set): |
93 | raise ValueError, 'other must be a Set instance' | |
93 | raise ValueError('other must be a Set instance') | |
94 | 94 | if self is other: |
95 | 95 | return |
96 | 96 | for item in other.items: |
103 | 103 | @type other: Set object |
104 | 104 | """ |
105 | 105 | if not isinstance(other, Set): |
106 | raise ValueError, 'other must be a Set instance' | |
106 | raise ValueError('other must be a Set instance') | |
107 | 107 | if self is other: |
108 | 108 | return |
109 | 109 | # we make a copy of the list so that we can remove items from |
119 | 119 | @type other: Set object |
120 | 120 | """ |
121 | 121 | if not isinstance(other, Set): |
122 | raise ValueError, 'other must be a Set instance' | |
122 | raise ValueError('other must be a Set instance') | |
123 | 123 | if self is other: |
124 | 124 | self.items = [] |
125 | 125 | else: |
133 | 133 | @type other: Set object |
134 | 134 | @rtype: the same type as I{self} |
135 | 135 | """ |
136 | ||
136 | ||
137 | 137 | obj = self._clone() |
138 | 138 | obj.union_update(other) |
139 | 139 | return obj |
240 | 240 | |
241 | 241 | @rtype: bool |
242 | 242 | """ |
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') | |
246 | 246 | for item in self.items: |
247 | 247 | if not item in other.items: |
248 | 248 | return False |
255 | 255 | """ |
256 | 256 | |
257 | 257 | if not isinstance(other, Set): |
258 | raise ValueError, 'other must be a Set instance' | |
258 | raise ValueError('other must be a Set instance') | |
259 | 259 | for item in other.items: |
260 | 260 | if not item in self.items: |
261 | 261 | return False |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
44 | 44 | """Raised when an attempt is made to unget a token when the unget |
45 | 45 | buffer is full.""" |
46 | 46 | 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 | ||
48 | 157 | class Tokenizer(object): |
49 | 158 | """A DNS master file format tokenizer. |
50 | 159 | |
51 | 160 | A token is a (type, value) tuple, where I{type} is an int, and |
52 | 161 | I{value} is a string. The valid types are EOF, EOL, WHITESPACE, |
53 | 162 | IDENTIFIER, QUOTED_STRING, COMMENT, and DELIMITER. |
54 | ||
163 | ||
55 | 164 | @ivar file: The file to tokenize |
56 | 165 | @type file: file |
57 | 166 | @ivar ungotten_char: The most recently ungotten character, or None. |
74 | 183 | @ivar filename: A filename that will be returned by the L{where} method. |
75 | 184 | @type filename: string |
76 | 185 | """ |
77 | ||
186 | ||
78 | 187 | def __init__(self, f=sys.stdin, filename=None): |
79 | 188 | """Initialize a tokenizer instance. |
80 | 189 | |
86 | 195 | will return. |
87 | 196 | @type filename: string |
88 | 197 | """ |
89 | ||
198 | ||
90 | 199 | if isinstance(f, str): |
91 | 200 | f = cStringIO.StringIO(f) |
92 | 201 | if filename is None: |
111 | 220 | """Read a character from input. |
112 | 221 | @rtype: string |
113 | 222 | """ |
114 | ||
223 | ||
115 | 224 | if self.ungotten_char is None: |
116 | 225 | if self.eof: |
117 | 226 | c = '' |
132 | 241 | @rtype: (string, int) tuple. The first item is the filename of |
133 | 242 | the input, the second is the current line number. |
134 | 243 | """ |
135 | ||
244 | ||
136 | 245 | return (self.filename, self.line_number) |
137 | ||
246 | ||
138 | 247 | def _unget_char(self, c): |
139 | 248 | """Unget a character. |
140 | 249 | |
141 | 250 | The unget buffer for characters is only one character large; it is |
142 | 251 | an error to try to unget a character when the unget buffer is not |
143 | 252 | empty. |
144 | ||
253 | ||
145 | 254 | @param c: the character to unget |
146 | 255 | @type c: string |
147 | 256 | @raises UngetBufferFull: there is already an ungotten char |
148 | 257 | """ |
149 | ||
258 | ||
150 | 259 | if not self.ungotten_char is None: |
151 | 260 | raise UngetBufferFull |
152 | 261 | self.ungotten_char = c |
161 | 270 | |
162 | 271 | @rtype: int |
163 | 272 | """ |
164 | ||
273 | ||
165 | 274 | skipped = 0 |
166 | 275 | while True: |
167 | 276 | c = self._get_char() |
180 | 289 | @param want_comment: If True, return a COMMENT token if the |
181 | 290 | first token read is a comment. The default is False. |
182 | 291 | @type want_comment: bool |
183 | @rtype: (int, string) tuple | |
292 | @rtype: Token object | |
184 | 293 | @raises dns.exception.UnexpectedEnd: input ended prematurely |
185 | 294 | @raises dns.exception.SyntaxError: input was badly formed |
186 | 295 | """ |
187 | ||
296 | ||
188 | 297 | if not self.ungotten_token is None: |
189 | 298 | token = self.ungotten_token |
190 | 299 | self.ungotten_token = None |
191 | if token[0] == WHITESPACE: | |
300 | if token.is_whitespace(): | |
192 | 301 | if want_leading: |
193 | 302 | return token |
194 | elif token[0] == COMMENT: | |
303 | elif token.is_comment(): | |
195 | 304 | if want_comment: |
196 | 305 | return token |
197 | 306 | else: |
198 | 307 | return token |
199 | 308 | skipped = self.skip_whitespace() |
200 | 309 | if want_leading and skipped > 0: |
201 | return (WHITESPACE, ' ') | |
310 | return Token(WHITESPACE, ' ') | |
202 | 311 | token = '' |
203 | 312 | ttype = IDENTIFIER |
313 | has_escape = False | |
204 | 314 | while True: |
205 | 315 | c = self._get_char() |
206 | 316 | if c == '' or c in self.delimiters: |
229 | 339 | self.skip_whitespace() |
230 | 340 | continue |
231 | 341 | elif c == '\n': |
232 | return (EOL, '\n') | |
342 | return Token(EOL, '\n') | |
233 | 343 | elif c == ';': |
234 | 344 | while 1: |
235 | 345 | c = self._get_char() |
238 | 348 | token += c |
239 | 349 | if want_comment: |
240 | 350 | self._unget_char(c) |
241 | return (COMMENT, token) | |
351 | return Token(COMMENT, token) | |
242 | 352 | elif c == '': |
243 | 353 | 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) | |
247 | 356 | elif self.multiline: |
248 | 357 | self.skip_whitespace() |
249 | 358 | token = '' |
250 | 359 | continue |
251 | 360 | else: |
252 | return (EOL, '\n') | |
361 | return Token(EOL, '\n') | |
253 | 362 | else: |
254 | 363 | # This code exists in case we ever want a |
255 | 364 | # delimiter to be returned. It never produces |
275 | 384 | raise dns.exception.SyntaxError |
276 | 385 | c = chr(int(c) * 100 + int(c2) * 10 + int(c3)) |
277 | 386 | elif c == '\n': |
278 | raise dns.exception.SyntaxError, 'newline in quoted string' | |
387 | raise dns.exception.SyntaxError('newline in quoted string') | |
279 | 388 | elif c == '\\': |
280 | 389 | # |
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. | |
283 | 392 | # |
393 | token += c | |
394 | has_escape = True | |
284 | 395 | 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 | |
288 | 398 | token += c |
289 | 399 | if token == '' and ttype != QUOTED_STRING: |
290 | 400 | if self.multiline: |
291 | raise dns.exception.SyntaxError, 'unbalanced parentheses' | |
401 | raise dns.exception.SyntaxError('unbalanced parentheses') | |
292 | 402 | ttype = EOF |
293 | return (ttype, token) | |
403 | return Token(ttype, token, has_escape) | |
294 | 404 | |
295 | 405 | def unget(self, token): |
296 | 406 | """Unget a token. |
298 | 408 | The unget buffer for tokens is only one token large; it is |
299 | 409 | an error to try to unget a token when the unget buffer is not |
300 | 410 | empty. |
301 | ||
411 | ||
302 | 412 | @param token: the token to unget |
303 | @type token: (int, string) token tuple | |
413 | @type token: Token object | |
304 | 414 | @raises UngetBufferFull: there is already an ungotten token |
305 | 415 | """ |
306 | 416 | |
312 | 422 | """Return the next item in an iteration. |
313 | 423 | @rtype: (int, string) |
314 | 424 | """ |
315 | ||
425 | ||
316 | 426 | token = self.get() |
317 | if token[0] == EOF: | |
427 | if token.is_eof(): | |
318 | 428 | raise StopIteration |
319 | 429 | return token |
320 | 430 | |
325 | 435 | |
326 | 436 | def get_int(self): |
327 | 437 | """Read the next token and interpret it as an integer. |
328 | ||
438 | ||
329 | 439 | @raises dns.exception.SyntaxError: |
330 | 440 | @rtype: int |
331 | 441 | """ |
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) | |
339 | 449 | |
340 | 450 | def get_uint8(self): |
341 | 451 | """Read the next token and interpret it as an 8-bit unsigned |
342 | 452 | integer. |
343 | ||
453 | ||
344 | 454 | @raises dns.exception.SyntaxError: |
345 | 455 | @rtype: int |
346 | 456 | """ |
347 | ||
457 | ||
348 | 458 | value = self.get_int() |
349 | 459 | 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) | |
352 | 461 | return value |
353 | 462 | |
354 | 463 | def get_uint16(self): |
355 | 464 | """Read the next token and interpret it as a 16-bit unsigned |
356 | 465 | integer. |
357 | ||
466 | ||
358 | 467 | @raises dns.exception.SyntaxError: |
359 | 468 | @rtype: int |
360 | 469 | """ |
361 | ||
470 | ||
362 | 471 | value = self.get_int() |
363 | 472 | 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) | |
366 | 474 | return value |
367 | 475 | |
368 | 476 | def get_uint32(self): |
369 | 477 | """Read the next token and interpret it as a 32-bit unsigned |
370 | 478 | integer. |
371 | ||
479 | ||
372 | 480 | @raises dns.exception.SyntaxError: |
373 | 481 | @rtype: int |
374 | 482 | """ |
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) | |
382 | 490 | 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) | |
385 | 492 | return value |
386 | 493 | |
387 | 494 | def get_string(self, origin=None): |
388 | 495 | """Read the next token and interpret it as a string. |
389 | ||
496 | ||
390 | 497 | @raises dns.exception.SyntaxError: |
391 | 498 | @rtype: string |
392 | 499 | """ |
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 | |
398 | 517 | |
399 | 518 | def get_name(self, origin=None): |
400 | 519 | """Read the next token and interpret it as a DNS name. |
401 | ||
520 | ||
402 | 521 | @raises dns.exception.SyntaxError: |
403 | 522 | @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) | |
409 | 528 | |
410 | 529 | def get_eol(self): |
411 | 530 | """Read the next token and raise an exception if it isn't EOL or |
414 | 533 | @raises dns.exception.SyntaxError: |
415 | 534 | @rtype: string |
416 | 535 | """ |
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 | |
423 | 541 | |
424 | 542 | 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
49 | 49 | """Raised if the peer didn't like amount of truncation in the TSIG we sent""" |
50 | 50 | pass |
51 | 51 | |
52 | _alg_name = dns.name.from_text('HMAC-MD5.SIG-ALG.REG.INT.').to_digestable() | |
52 | default_algorithm = "HMAC-MD5.SIG-ALG.REG.INT" | |
53 | 53 | |
54 | 54 | BADSIG = 16 |
55 | 55 | BADKEY = 17 |
56 | 56 | BADTIME = 18 |
57 | 57 | BADTRUNC = 22 |
58 | 58 | |
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 | |
63 | 64 | TSIG signature algorithm, and the TSIG digest context. |
64 | 65 | @rtype: (string, string, hmac.HMAC object) |
65 | 66 | @raises ValueError: I{other_data} is too long |
67 | @raises NotImplementedError: I{algorithm} is not supported | |
66 | 68 | """ |
67 | 69 | |
70 | (algorithm_name, digestmod) = get_algorithm(algorithm) | |
68 | 71 | if first: |
69 | ctx = hmac.new(secret) | |
72 | ctx = hmac.new(secret, digestmod=digestmod) | |
70 | 73 | ml = len(request_mac) |
71 | 74 | if ml > 0: |
72 | 75 | ctx.update(struct.pack('!H', ml)) |
82 | 85 | upper_time = (long_time >> 32) & 0xffffL |
83 | 86 | lower_time = long_time & 0xffffffffL |
84 | 87 | time_mac = struct.pack('!HIH', upper_time, lower_time, fudge) |
85 | pre_mac = _alg_name + time_mac | |
88 | pre_mac = algorithm_name + time_mac | |
86 | 89 | ol = len(other_data) |
87 | 90 | if ol > 65535: |
88 | raise ValueError, 'TSIG Other Data is > 65535 bytes' | |
91 | raise ValueError('TSIG Other Data is > 65535 bytes') | |
89 | 92 | post_mac = struct.pack('!HH', error, ol) + other_data |
90 | 93 | if first: |
91 | 94 | ctx.update(pre_mac) |
103 | 106 | else: |
104 | 107 | ctx = None |
105 | 108 | 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) | |
106 | 115 | |
107 | 116 | def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata, |
108 | 117 | tsig_rdlen, ctx=None, multi=False, first=True): |
145 | 154 | elif error == BADTRUNC: |
146 | 155 | raise PeerBadTruncation |
147 | 156 | else: |
148 | raise PeerError, 'unknown TSIG error code %d' % error | |
157 | raise PeerError('unknown TSIG error code %d' % error) | |
149 | 158 | time_low = time - fudge |
150 | 159 | time_high = time + fudge |
151 | 160 | if now < time_low or now > time_high: |
152 | 161 | 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) | |
156 | 165 | if (our_mac != mac): |
157 | 166 | raise BadSignature |
158 | 167 | 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
18 | 18 | |
19 | 19 | class BadTTL(dns.exception.SyntaxError): |
20 | 20 | pass |
21 | ||
21 | ||
22 | 22 | def from_text(text): |
23 | 23 | """Convert the text form of a TTL to an integer. |
24 | 24 | |
29 | 29 | @raises dns.ttl.BadTTL: the TTL is not well-formed |
30 | 30 | @rtype: int |
31 | 31 | """ |
32 | ||
32 | ||
33 | 33 | if text.isdigit(): |
34 | 34 | total = long(text) |
35 | 35 | else: |
54 | 54 | elif c == 's': |
55 | 55 | total += current |
56 | 56 | else: |
57 | raise BadTTL, "unknown unit '%s'" % c | |
57 | raise BadTTL("unknown unit '%s'" % c) | |
58 | 58 | current = 0 |
59 | 59 | if not current == 0: |
60 | raise BadTTL, "trailing integer" | |
60 | raise BadTTL("trailing integer") | |
61 | 61 | 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)") | |
63 | 63 | return total |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
23 | 23 | |
24 | 24 | class Update(dns.message.Message): |
25 | 25 | def __init__(self, zone, rdclass=dns.rdataclass.IN, keyring=None, |
26 | keyname=None): | |
26 | keyname=None, keyalgorithm=dns.tsig.default_algorithm): | |
27 | 27 | """Initialize a new DNS Update object. |
28 | ||
28 | ||
29 | 29 | @param zone: The zone which is being updated. |
30 | 30 | @type zone: A dns.name.Name or string |
31 | 31 | @param rdclass: The class of the zone; defaults to dns.rdataclass.IN. |
40 | 40 | so applications should supply a keyname when a keyring is used, unless |
41 | 41 | they know the keyring contains only one key. |
42 | 42 | @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 | |
43 | 46 | """ |
44 | 47 | super(Update, self).__init__() |
45 | 48 | self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE) |
52 | 55 | self.find_rrset(self.question, self.origin, rdclass, dns.rdatatype.SOA, |
53 | 56 | create=True, force_unique=True) |
54 | 57 | if not keyring is None: |
55 | self.use_tsig(keyring, keyname) | |
58 | self.use_tsig(keyring, keyname, keyalgorithm) | |
56 | 59 | |
57 | 60 | def _add_rr(self, name, ttl, rd, deleting=None, section=None): |
58 | 61 | """Add a single RR to the update section.""" |
71 | 74 | argument is the section to add to. The third argument |
72 | 75 | is always a name. The other arguments can be: |
73 | 76 | |
74 | - rdataset... | |
77 | - rdataset... | |
75 | 78 | |
76 | 79 | - ttl, rdata... |
77 | 80 | |
108 | 111 | """Add records. The first argument is always a name. The other |
109 | 112 | arguments can be: |
110 | 113 | |
111 | - rdataset... | |
114 | - rdataset... | |
112 | 115 | |
113 | 116 | - ttl, rdata... |
114 | 117 | |
119 | 122 | """Delete records. The first argument is always a name. The other |
120 | 123 | arguments can be: |
121 | 124 | |
122 | - I{nothing} | |
123 | ||
124 | - rdataset... | |
125 | - I{nothing} | |
126 | ||
127 | - rdataset... | |
125 | 128 | |
126 | 129 | - rdata... |
127 | 130 | |
161 | 164 | def replace(self, name, *args): |
162 | 165 | """Replace records. The first argument is always a name. The other |
163 | 166 | arguments can be: |
164 | ||
165 | - rdataset... | |
167 | ||
168 | - rdataset... | |
166 | 169 | |
167 | 170 | - ttl, rdata... |
168 | 171 | |
170 | 173 | |
171 | 174 | Note that if you want to replace the entire node, you should do |
172 | 175 | a delete of the name followed by one or more calls to add.""" |
173 | ||
176 | ||
174 | 177 | self._add(True, self.authority, name, *args) |
175 | 178 | |
176 | 179 | def present(self, name, *args): |
178 | 181 | or specific rdataset) exists as a prerequisite to the |
179 | 182 | execution of the update. The first argument is always a name. |
180 | 183 | The other arguments can be: |
181 | ||
182 | - rdataset... | |
184 | ||
185 | - rdataset... | |
183 | 186 | |
184 | 187 | - rdata... |
185 | 188 | |
186 | 189 | - rdtype, string...""" |
187 | ||
190 | ||
188 | 191 | if isinstance(name, (str, unicode)): |
189 | 192 | name = dns.name.from_text(name, None) |
190 | 193 | if len(args) == 0: |
195 | 198 | elif isinstance(args[0], dns.rdataset.Rdataset) or \ |
196 | 199 | isinstance(args[0], dns.rdata.Rdata) or \ |
197 | 200 | len(args) > 1: |
198 | if len(args) > 1: | |
201 | if not isinstance(args[0], dns.rdataset.Rdataset): | |
199 | 202 | # Add a 0 TTL |
200 | 203 | args = list(args) |
201 | 204 | args.insert(0, 0) |
217 | 220 | name = dns.name.from_text(name, None) |
218 | 221 | if rdtype is None: |
219 | 222 | rrset = self.find_rrset(self.answer, name, |
220 | dns.rdataclass.NONE, dns.rdatatype.ANY, | |
223 | dns.rdataclass.NONE, dns.rdatatype.ANY, | |
221 | 224 | dns.rdatatype.NONE, None, |
222 | 225 | True, True) |
223 | 226 | else: |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
15 | 15 | """dnspython release version information.""" |
16 | 16 | |
17 | 17 | MAJOR = 1 |
18 | MINOR = 7 | |
19 | MICRO = 1 | |
18 | MINOR = 8 | |
19 | MICRO = 0 | |
20 | 20 | RELEASELEVEL = 0x0f |
21 | 21 | SERIAL = 0 |
22 | 22 |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
53 | 53 | dns.name.Name object, or it may be a string. In the either case, |
54 | 54 | if the name is relative it is treated as relative to the origin of |
55 | 55 | the zone. |
56 | ||
56 | ||
57 | 57 | @ivar rdclass: The zone's rdata class; the default is class IN. |
58 | 58 | @type rdclass: int |
59 | 59 | @ivar origin: The origin of the zone. |
70 | 70 | node_factory = dns.node.Node |
71 | 71 | |
72 | 72 | __slots__ = ['rdclass', 'origin', 'nodes', 'relativize'] |
73 | ||
73 | ||
74 | 74 | def __init__(self, origin, rdclass=dns.rdataclass.IN, relativize=True): |
75 | 75 | """Initialize a zone object. |
76 | 76 | |
89 | 89 | nodes. |
90 | 90 | @rtype: bool |
91 | 91 | """ |
92 | ||
92 | ||
93 | 93 | if not isinstance(other, Zone): |
94 | 94 | return False |
95 | 95 | if self.rdclass != other.rdclass or \ |
102 | 102 | """Are two zones not equal? |
103 | 103 | @rtype: bool |
104 | 104 | """ |
105 | ||
105 | ||
106 | 106 | return not self.__eq__(other) |
107 | 107 | |
108 | 108 | def _validate_name(self, name): |
109 | 109 | if isinstance(name, (str, unicode)): |
110 | 110 | name = dns.name.from_text(name, None) |
111 | 111 | 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") | |
114 | 113 | if name.is_absolute(): |
115 | 114 | 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") | |
118 | 116 | if self.relativize: |
119 | 117 | name = name.relativize(self.origin) |
120 | 118 | return name |
121 | ||
119 | ||
122 | 120 | def __getitem__(self, key): |
123 | 121 | key = self._validate_name(key) |
124 | 122 | return self.nodes[key] |
169 | 167 | @raises KeyError: the name is not known and create was not specified. |
170 | 168 | @rtype: dns.node.Node object |
171 | 169 | """ |
172 | ||
170 | ||
173 | 171 | name = self._validate_name(name) |
174 | 172 | node = self.nodes.get(name) |
175 | 173 | if node is None: |
185 | 183 | This method is like L{find_node}, except it returns None instead |
186 | 184 | of raising an exception if the node does not exist and creation |
187 | 185 | has not been requested. |
188 | ||
186 | ||
189 | 187 | @param name: the name of the node to find |
190 | 188 | @type name: dns.name.Name object or string |
191 | 189 | @param create: should the node be created if it doesn't exist? |
204 | 202 | |
205 | 203 | It is not an error if the node does not exist. |
206 | 204 | """ |
207 | ||
205 | ||
208 | 206 | name = self._validate_name(name) |
209 | 207 | if self.nodes.has_key(name): |
210 | 208 | del self.nodes[name] |
211 | ||
209 | ||
212 | 210 | def find_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE, |
213 | 211 | create=False): |
214 | 212 | """Look for rdata with the specified name and type in the zone, |
220 | 218 | |
221 | 219 | The rdataset returned is not a copy; changes to it will change |
222 | 220 | the zone. |
223 | ||
221 | ||
224 | 222 | KeyError is raised if the name or type are not found. |
225 | 223 | Use L{get_rdataset} if you want to have None returned instead. |
226 | 224 | |
256 | 254 | |
257 | 255 | The rdataset returned is not a copy; changes to it will change |
258 | 256 | the zone. |
259 | ||
257 | ||
260 | 258 | None is returned if the name or type are not found. |
261 | 259 | Use L{find_rdataset} if you want to have KeyError raised instead. |
262 | 260 | |
313 | 311 | |
314 | 312 | def replace_rdataset(self, name, replacement): |
315 | 313 | """Replace an rdataset at name. |
316 | ||
314 | ||
317 | 315 | It is not an error if there is no rdataset matching I{replacement}. |
318 | 316 | |
319 | 317 | Ownership of the I{replacement} object is transferred to the zone; |
329 | 327 | """ |
330 | 328 | |
331 | 329 | if replacement.rdclass != self.rdclass: |
332 | raise ValueError, 'replacement.rdclass != zone.rdclass' | |
330 | raise ValueError('replacement.rdclass != zone.rdclass') | |
333 | 331 | node = self.find_node(name, True) |
334 | 332 | node.replace_rdataset(replacement) |
335 | 333 | |
340 | 338 | The I{name}, I{rdtype}, and I{covers} parameters may be |
341 | 339 | strings, in which case they will be converted to their proper |
342 | 340 | type. |
343 | ||
341 | ||
344 | 342 | This method is less efficient than the similar |
345 | 343 | L{find_rdataset} because it creates an RRset instead of |
346 | 344 | returning the matching rdataset. It may be more convenient |
380 | 378 | The I{name}, I{rdtype}, and I{covers} parameters may be |
381 | 379 | strings, in which case they will be converted to their proper |
382 | 380 | type. |
383 | ||
381 | ||
384 | 382 | This method is less efficient than the similar L{get_rdataset} |
385 | 383 | because it creates an RRset instead of returning the matching |
386 | 384 | rdataset. It may be more convenient for some uses since it |
419 | 417 | @param covers: the covered type (defaults to None) |
420 | 418 | @type covers: int or string |
421 | 419 | """ |
422 | ||
420 | ||
423 | 421 | if isinstance(rdtype, str): |
424 | 422 | rdtype = dns.rdatatype.from_text(rdtype) |
425 | 423 | if isinstance(covers, str): |
442 | 440 | @param covers: the covered type (defaults to None) |
443 | 441 | @type covers: int or string |
444 | 442 | """ |
445 | ||
443 | ||
446 | 444 | if isinstance(rdtype, str): |
447 | 445 | rdtype = dns.rdatatype.from_text(rdtype) |
448 | 446 | if isinstance(covers, str): |
456 | 454 | |
457 | 455 | def to_file(self, f, sorted=True, relativize=True, nl=None): |
458 | 456 | """Write a zone to a file. |
459 | ||
457 | ||
460 | 458 | @param f: file or string. If I{f} is a string, it is treated |
461 | 459 | as the name of a file to open. |
462 | 460 | @param sorted: if True, the file will be written with the |
565 | 563 | |
566 | 564 | def _eat_line(self): |
567 | 565 | 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(): | |
570 | 568 | break |
571 | ||
569 | ||
572 | 570 | def _rr_line(self): |
573 | 571 | """Process one line from a DNS master file.""" |
574 | 572 | # Name |
575 | 573 | if self.current_origin is None: |
576 | 574 | raise UnknownOrigin |
577 | 575 | 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) | |
580 | 578 | else: |
581 | 579 | 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(): | |
584 | 581 | # treat leading WS followed by EOL/EOF as if they were EOL/EOF. |
585 | 582 | return |
586 | 583 | self.tok.unget(token) |
591 | 588 | if self.relativize: |
592 | 589 | name = name.relativize(self.zone.origin) |
593 | 590 | token = self.tok.get() |
594 | if token[0] != dns.tokenizer.IDENTIFIER: | |
591 | if not token.is_identifier(): | |
595 | 592 | raise dns.exception.SyntaxError |
596 | 593 | # TTL |
597 | 594 | try: |
598 | ttl = dns.ttl.from_text(token[1]) | |
595 | ttl = dns.ttl.from_text(token.value) | |
599 | 596 | token = self.tok.get() |
600 | if token[0] != dns.tokenizer.IDENTIFIER: | |
597 | if not token.is_identifier(): | |
601 | 598 | raise dns.exception.SyntaxError |
602 | 599 | except dns.ttl.BadTTL: |
603 | 600 | ttl = self.ttl |
604 | 601 | # Class |
605 | 602 | try: |
606 | rdclass = dns.rdataclass.from_text(token[1]) | |
603 | rdclass = dns.rdataclass.from_text(token.value) | |
607 | 604 | token = self.tok.get() |
608 | if token[0] != dns.tokenizer.IDENTIFIER: | |
605 | if not token.is_identifier(): | |
609 | 606 | raise dns.exception.SyntaxError |
610 | 607 | except dns.exception.SyntaxError: |
611 | 608 | raise dns.exception.SyntaxError |
612 | 609 | except: |
613 | 610 | rdclass = self.zone.rdclass |
614 | 611 | 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") | |
616 | 613 | # Type |
617 | 614 | try: |
618 | rdtype = dns.rdatatype.from_text(token[1]) | |
615 | rdtype = dns.rdatatype.from_text(token.value) | |
619 | 616 | except: |
620 | raise dns.exception.SyntaxError, \ | |
621 | "unknown rdatatype '%s'" % token[1] | |
617 | raise dns.exception.SyntaxError("unknown rdatatype '%s'" % token.value) | |
622 | 618 | n = self.zone.nodes.get(name) |
623 | 619 | if n is None: |
624 | 620 | n = self.zone.node_factory() |
629 | 625 | except dns.exception.SyntaxError: |
630 | 626 | # Catch and reraise. |
631 | 627 | (ty, va) = sys.exc_info()[:2] |
632 | raise ty, va | |
628 | raise va | |
633 | 629 | except: |
634 | 630 | # All exceptions that occur in the processing of rdata |
635 | 631 | # are treated as syntax errors. This is not strictly |
636 | 632 | # correct, but it is correct almost all of the time. |
637 | 633 | # We convert them to syntax errors so that we can emit |
638 | 634 | # helpful filename:line info. |
639 | ||
640 | 635 | (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))) | |
643 | 637 | |
644 | 638 | rd.choose_relativity(self.zone.origin, self.relativize) |
645 | 639 | covers = rd.covers() |
655 | 649 | |
656 | 650 | try: |
657 | 651 | 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(): | |
660 | 654 | if not self.current_file is None: |
661 | 655 | self.current_file.close() |
662 | 656 | if len(self.saved_state) > 0: |
667 | 661 | self.ttl) = self.saved_state.pop(-1) |
668 | 662 | continue |
669 | 663 | break |
670 | elif token[0] == dns.tokenizer.EOL: | |
664 | elif token.is_eol(): | |
671 | 665 | continue |
672 | elif token[0] == dns.tokenizer.COMMENT: | |
666 | elif token.is_comment(): | |
673 | 667 | self.tok.get_eol() |
674 | 668 | continue |
675 | elif token[1][0] == '$': | |
676 | u = token[1].upper() | |
669 | elif token.value[0] == '$': | |
670 | u = token.value.upper() | |
677 | 671 | if u == '$TTL': |
678 | 672 | 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) | |
682 | 676 | self.tok.get_eol() |
683 | 677 | elif u == '$ORIGIN': |
684 | 678 | self.current_origin = self.tok.get_name() |
687 | 681 | self.zone.origin = self.current_origin |
688 | 682 | elif u == '$INCLUDE' and self.allow_include: |
689 | 683 | 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 | |
694 | 687 | 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) | |
698 | 691 | 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") | |
703 | 694 | else: |
704 | 695 | new_origin = self.current_origin |
705 | 696 | self.saved_state.append((self.tok, |
712 | 703 | filename) |
713 | 704 | self.current_origin = new_origin |
714 | 705 | else: |
715 | raise dns.exception.SyntaxError, \ | |
716 | "Unknown master file directive '" + u + "'" | |
706 | raise dns.exception.SyntaxError("Unknown master file directive '" + u + "'") | |
717 | 707 | continue |
718 | 708 | self.tok.unget(token) |
719 | 709 | self._rr_line() |
721 | 711 | (filename, line_number) = self.tok.where() |
722 | 712 | if detail is None: |
723 | 713 | 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 | ||
727 | 716 | # Now that we're done reading, do some basic checking of the zone. |
728 | 717 | if self.check_origin: |
729 | 718 | self.zone.check_origin() |
818 | 807 | if filename is None: |
819 | 808 | filename = '<file>' |
820 | 809 | want_close = False |
821 | ||
810 | ||
822 | 811 | try: |
823 | 812 | z = from_text(f, origin, rdclass, relativize, zone_factory, |
824 | 813 | filename, allow_include, check_origin) |
829 | 818 | |
830 | 819 | def from_xfr(xfr, zone_factory=Zone, relativize=True): |
831 | 820 | """Convert the output of a zone transfer generator into a zone object. |
832 | ||
821 | ||
833 | 822 | @param xfr: The xfr generator |
834 | 823 | @type xfr: generator of dns.message.Message objects |
835 | 824 | @param relativize: should names be relativized? The default is True. |
840 | 829 | @raises dns.zone.NoNS: No NS RRset was found at the zone origin |
841 | 830 | @rtype: dns.zone.Zone object |
842 | 831 | """ |
843 | ||
832 | ||
844 | 833 | z = None |
845 | 834 | for r in xfr: |
846 | 835 | if z is None: |
0 | 0 | #!/usr/bin/env python |
1 | 1 | # |
2 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
2 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
3 | 3 | # |
4 | 4 | # Permission to use, copy, modify, and distribute this software and its |
5 | 5 | # documentation for any purpose with or without fee is hereby granted, |
17 | 17 | import sys |
18 | 18 | from distutils.core import setup |
19 | 19 | |
20 | version = '1.7.1' | |
20 | version = '1.8.0' | |
21 | 21 | |
22 | 22 | kwargs = { |
23 | 23 | 'name' : 'dnspython', |
0 | # Copyright (C) 2003-2007, 2009 Nominum, Inc. | |
0 | # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # 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. | |
1 | 1 | # |
2 | 2 | # Permission to use, copy, modify, and distribute this software and its |
3 | 3 | # documentation for any purpose with or without fee is hereby granted, |
17 | 17 | import dns.exception |
18 | 18 | import dns.tokenizer |
19 | 19 | |
20 | Token = dns.tokenizer.Token | |
21 | ||
20 | 22 | class TokenizerTestCase(unittest.TestCase): |
21 | ||
23 | ||
22 | 24 | def testQuotedString1(self): |
23 | 25 | 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')) | |
27 | 28 | |
28 | 29 | def testQuotedString2(self): |
29 | 30 | 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, '')) | |
33 | 33 | |
34 | 34 | def testQuotedString3(self): |
35 | 35 | 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"')) | |
39 | 38 | |
40 | 39 | def testQuotedString4(self): |
41 | 40 | 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')) | |
45 | 43 | |
46 | 44 | def testQuotedString5(self): |
47 | 45 | def bad(): |
48 | 46 | tok = dns.tokenizer.Tokenizer(r'"foo') |
49 | (ttype, value) = tok.get() | |
47 | token = tok.get() | |
50 | 48 | self.failUnlessRaises(dns.exception.UnexpectedEnd, bad) |
51 | 49 | |
52 | 50 | def testQuotedString6(self): |
53 | 51 | def bad(): |
54 | 52 | tok = dns.tokenizer.Tokenizer(r'"foo\01') |
55 | (ttype, value) = tok.get() | |
53 | token = tok.get() | |
56 | 54 | self.failUnlessRaises(dns.exception.SyntaxError, bad) |
57 | 55 | |
58 | 56 | def testQuotedString7(self): |
59 | 57 | def bad(): |
60 | 58 | tok = dns.tokenizer.Tokenizer('"foo\nbar"') |
61 | (ttype, value) = tok.get() | |
59 | token = tok.get() | |
62 | 60 | self.failUnlessRaises(dns.exception.SyntaxError, bad) |
63 | 61 | |
64 | 62 | def testEmpty1(self): |
65 | 63 | 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()) | |
68 | 66 | |
69 | 67 | def testEmpty2(self): |
70 | 68 | 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()) | |
75 | 72 | |
76 | 73 | def testEOL(self): |
77 | 74 | 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()) | |
82 | 78 | |
83 | 79 | def testWS1(self): |
84 | 80 | 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()) | |
87 | 83 | |
88 | 84 | def testWS2(self): |
89 | 85 | 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()) | |
92 | 88 | |
93 | 89 | def testComment1(self): |
94 | 90 | 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()) | |
97 | 93 | |
98 | 94 | def testComment2(self): |
99 | 95 | 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()) | |
105 | 100 | |
106 | 101 | def testComment3(self): |
107 | 102 | 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()) | |
113 | 107 | |
114 | 108 | def testMultiline1(self): |
115 | 109 | tok = dns.tokenizer.Tokenizer('( foo\n\n bar\n)') |
116 | 110 | 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')]) | |
119 | 113 | |
120 | 114 | def testMultiline2(self): |
121 | 115 | tok = dns.tokenizer.Tokenizer('( foo\n\n bar\n)\n') |
122 | 116 | 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')]) | |
126 | 120 | def testMultiline3(self): |
127 | 121 | def bad(): |
128 | 122 | tok = dns.tokenizer.Tokenizer('foo)') |
140 | 134 | t1 = tok.get() |
141 | 135 | tok.unget(t1) |
142 | 136 | 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') | |
144 | 139 | |
145 | 140 | def testUnget2(self): |
146 | 141 | def bad(): |
163 | 158 | def testEscapedDelimiter1(self): |
164 | 159 | tok = dns.tokenizer.Tokenizer(r'ch\ ld') |
165 | 160 | 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') | |
167 | 162 | |
168 | 163 | def testEscapedDelimiter2(self): |
169 | tok = dns.tokenizer.Tokenizer(r'ch\0ld') | |
164 | tok = dns.tokenizer.Tokenizer(r'ch\032ld') | |
170 | 165 | 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') | |
172 | 187 | |
173 | 188 | if __name__ == '__main__': |
174 | 189 | unittest.main() |