Imported Upstream version 1.5.0
SVN-Git Migration
8 years ago
0 | 2006-11-03 Bob Halley <halley@dnspython.org> | |
1 | ||
2 | * dns/rdtypes/IN/DHCID.py: Added support for the DHCID RR type. | |
3 | ||
4 | 2006-11-02 Bob Halley <halley@dnspython.org> | |
5 | ||
6 | * dns/query.py (udp): Messages from unexpected sources can now be | |
7 | ignored by setting ignore_unexpected to True. | |
8 | ||
9 | 2006-10-31 Bob Halley <halley@dnspython.org> | |
10 | ||
11 | * dns/query.py (udp): When raising UnexpectedSource, add more | |
12 | detail about what went wrong to the exception. | |
13 | ||
14 | 2006-09-22 Bob Halley <halley@dnspython.org> | |
15 | ||
16 | * dns/message.py (Message.use_edns): add reasonable defaults for | |
17 | the ednsflags, payload, and request_payload parameters. | |
18 | ||
19 | * dns/message.py (Message.want_dnssec): add a convenience method for | |
20 | enabling/disabling the "DNSSEC desired" flag in requests. | |
21 | ||
22 | * dns/message.py (make_query): add "use_edns" and "want_dnssec" | |
23 | parameters. | |
24 | ||
25 | 2006-08-17 Bob Halley <halley@dnspython.org> | |
26 | ||
27 | * dns/resolver.py (Resolver.read_resolv_conf): If /etc/resolv.conf | |
28 | doesn't exist, just use the default resolver configuration (i.e. | |
29 | the same thing we would have used if resolv.conf had existed and | |
30 | been empty). | |
31 | ||
32 | 2006-07-26 Bob Halley <halley@dnspython.org> | |
33 | ||
34 | * dns/resolver.py (Resolver._config_win32_fromkey): fix | |
35 | cut-and-paste error where we passed the wrong variable to | |
36 | self._config_win32_search(). Thanks to David Arnold for finding | |
37 | the bug and submitting a patch. | |
38 | ||
39 | 2006-07-20 Bob Halley <halley@dnspython.org> | |
40 | ||
41 | * dns/resolver.py (Answer): Add more support for the sequence | |
42 | protocol, forwarding requests to the answer object's rrset. | |
43 | E.g. "for a in answer" is equivalent to "for a in answer.rrset", | |
44 | "answer[i]" is equivalent to "answer.rrset[i]", and | |
45 | "answer[i:j]" is equivalent to "answer.rrset[i:j]". | |
46 | ||
47 | 2006-07-19 Bob Halley <halley@dnspython.org> | |
48 | ||
49 | * dns/query.py (xfr): Add IXFR support. | |
50 | ||
51 | 2006-06-22 Bob Halley <halley@dnspython.org> | |
52 | ||
53 | * dns/rdtypes/IN/IPSECKEY.py: Added support for the IPSECKEY RR type. | |
54 | ||
55 | 2006-06-21 Bob Halley <halley@dnspython.org> | |
56 | ||
57 | * dns/rdtypes/ANY/SPF.py: Added support for the SPF RR type. | |
58 | ||
59 | 2006-06-02 Bob Halley <halley@dnspython.org> | |
60 | ||
61 | * (Version 1.4.0 released) | |
62 | ||
63 | 2006-04-25 Bob Halley <halley@dnspython.org> | |
64 | ||
65 | * dns/rrset.py (RRset.to_rdataset): Added a convenience method | |
66 | to convert an rrset into an rdataset. | |
67 | ||
68 | 2006-03-27 Bob Halley <halley@dnspython.org> | |
69 | ||
70 | * Added dns.e164.query(). This function can be used to look for | |
71 | NAPTR RRs for a specified number in several domains, e.g.: | |
72 | ||
73 | dns.e164.query('16505551212', | |
74 | ['e164.dnspython.org.', 'e164.arpa.']) | |
75 | ||
76 | 2006-03-26 Bob Halley <halley@dnspython.org> | |
77 | ||
78 | * dns/resolver.py (Resolver.query): The resolver deleted from | |
79 | a list while iterating it, which makes the iterator unhappy. | |
80 | ||
81 | 2006-03-17 Bob Halley <halley@dnspython.org> | |
82 | ||
83 | * dns/resolver.py (Resolver.query): The resolver needlessly | |
84 | delayed responses for successful queries. | |
85 | ||
86 | 2006-01-18 Bob Halley <halley@dnspython.org> | |
87 | ||
88 | * dns/rdata.py: added a validate() method to the rdata class. If | |
89 | you change an rdata by assigning to its fields, it is a good | |
90 | idea to call validate() when you are done making changes. | |
91 | For example, if 'r' is an MX record and then you execute: | |
92 | ||
93 | r.preference = 100000 # invalid, because > 65535 | |
94 | r.validate() | |
95 | ||
96 | The validation will fail and an exception will be raised. | |
97 | ||
98 | 2006-01-11 Bob Halley <halley@dnspython.org> | |
99 | ||
100 | * dns/ttl.py: TTLs are now bounds checked to be within the closed | |
101 | interval [0, 2^31 - 1]. | |
102 | ||
103 | * The BIND 8 TTL syntax is now accepted in the SOA refresh, retry, | |
104 | expire, and minimum fields, and in the original_ttl field of | |
105 | SIG and RRSIG records. | |
106 | ||
107 | 2006-01-04 Bob Halley <halley@dnspython.org> | |
108 | ||
109 | * dns/resolver.py: The windows registry irritatingly changes the | |
110 | list element delimiter in between ' ' and ',' (and vice-versa) | |
111 | in various versions of windows. We now cope by always looking | |
112 | for either one (' ' first). | |
113 | ||
114 | 2005-12-27 Bob Halley <halley@dnspython.org> | |
115 | ||
116 | * dns/e164.py: Added routines to convert between E.164 numbers and | |
117 | their ENUM domain name equivalents. | |
118 | ||
119 | * dns/reversename.py: Added routines to convert between IPv4 and | |
120 | IPv6 addresses and their DNS reverse-map equivalents. | |
121 | ||
122 | 2005-12-18 Bob Halley <halley@dnspython.org> | |
123 | ||
124 | * dns/rdtypes/ANY/LOC.py (_tuple_to_float): The sign was lost when | |
125 | converting a tuple into a float, which broke conversions of | |
126 | south latitudes and west longitudes. | |
127 | ||
128 | 2005-11-17 Bob Halley <halley@dnspython.org> | |
129 | ||
130 | * dns/zone.py: The 'origin' parameter to from_text() and from_file() | |
131 | is now optional. If not specified, dnspython will use the | |
132 | first $ORIGIN in the text as the zone's origin. | |
133 | ||
134 | * dns/zone.py: Sanity checks of the zone's origin node can now | |
135 | be disabled. | |
136 | ||
137 | 2005-11-12 Bob Halley <halley@dnspython.org> | |
138 | ||
139 | * dns/name.py: Preliminary Unicode support has been added for | |
140 | domain names. Running dns.name.from_text() on a Unicode string | |
141 | will now encode each label using the IDN ACE encoding. The | |
142 | to_unicode() method may be used to convert a dns.name.Name with | |
143 | IDN ACE labels back into a Unicode string. This functionality | |
144 | requires Python 2.3 or greater. | |
145 | ||
0 | 146 | 2005-10-31 Bob Halley <halley@dnspython.org> |
1 | 147 | |
2 | 148 | * (Version 1.3.5 released) |
0 | Metadata-Version: 1.0 | |
0 | Metadata-Version: 1.1 | |
1 | 1 | Name: dnspython |
2 | Version: 1.3.5 | |
2 | Version: 1.5.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/dnspython-1.5.0.tar.gz | |
8 | 9 | Description: dnspython is a DNS toolkit for Python. It supports almost all |
9 | 10 | record types. It can be used for queries, zone transfers, and dynamic |
10 | 11 | updates. It supports TSIG authenticated messages and EDNS0. |
14 | 15 | class, and return an answer set. The low level classes allow |
15 | 16 | direct manipulation of DNS zones, messages, names, and records. |
16 | 17 | Platform: UNKNOWN |
18 | Classifier: Development Status :: 5 - Production/Stable | |
19 | Classifier: Intended Audience :: Developers | |
20 | Classifier: Intended Audience :: System Administrators | |
21 | Classifier: License :: Freeware | |
22 | Classifier: Operating System :: Microsoft :: Windows :: Windows 95/98/2000 | |
23 | Classifier: Operating System :: POSIX | |
24 | Classifier: Programming Language :: Python | |
25 | Classifier: Topic :: Internet :: Name Service (DNS) | |
26 | Classifier: Topic :: Software Development :: Libraries :: Python Modules | |
27 | Provides: dns |
21 | 21 | |
22 | 22 | ABOUT THIS RELEASE |
23 | 23 | |
24 | This is dnspython 1.3.5 | |
24 | This is dnspython 1.5.0. | |
25 | ||
26 | New since 1.4.0: | |
27 | ||
28 | Answer objects now support more of the python sequence | |
29 | protocol, forwarding the requests to the answer rrset. | |
30 | E.g. "for a in answer" is equivalent to "for a in | |
31 | answer.rrset", "answer[i]" is equivalent to "answer.rrset[i]", | |
32 | and "answer[i:j]" is equivalent to "answer.rrset[i:j]". | |
33 | ||
34 | Making requests using EDNS, including indicating DNSSEC awareness, | |
35 | is now easier. For example, you can now say: | |
36 | ||
37 | q = dns.message.make_query('www.dnspython.org', 'MX', | |
38 | want_dnssec=True) | |
39 | ||
40 | dns.query.xfr() can now be used for IXFR. | |
41 | ||
42 | Support has been added for the DHCID, IPSECKEY, and SPF RR types. | |
43 | ||
44 | UDP messages from unexpected sources can now be ignored by | |
45 | setting ignore_unexpected to True when calling dns.query.udp. | |
46 | ||
47 | Bugs fixed since 1.4.0: | |
48 | ||
49 | If /etc/resolv.conf didn't exist, we raised an exception | |
50 | instead of simply using the default resolver configuration. | |
51 | ||
52 | In dns.resolver.Resolver._config_win32_fromkey(), we were | |
53 | passing the wrong variable to self._config_win32_search(). | |
54 | ||
55 | New since 1.3.5: | |
56 | ||
57 | You can now convert E.164 numbers to/from their ENUM name | |
58 | forms: | |
59 | ||
60 | >>> import dns.e164 | |
61 | >>> n = dns.e164.from_e164("+1 555 1212") | |
62 | >>> n | |
63 | <DNS name 2.1.2.1.5.5.5.1.e164.arpa.> | |
64 | >>> dns.e164.to_e164(n) | |
65 | '+15551212' | |
66 | ||
67 | You can now convert IPv4 and IPv6 address to/from their | |
68 | corresponding DNS reverse map names: | |
69 | ||
70 | >>> import dns.reversename | |
71 | >>> n = dns.reversename.from_address("127.0.0.1") | |
72 | >>> n | |
73 | <DNS name 1.0.0.127.in-addr.arpa.> | |
74 | >>> dns.reversename.to_address(n) | |
75 | '127.0.0.1' | |
76 | ||
77 | You can now convert between Unicode strings and their IDN ACE | |
78 | form: | |
79 | ||
80 | >>> n = dns.name.from_text(u'les-\u00e9l\u00e8ves.example.') | |
81 | >>> n | |
82 | <DNS name xn--les-lves-50ai.example.> | |
83 | >>> n.to_unicode() | |
84 | u'les-\xe9l\xe8ves.example.' | |
85 | ||
86 | The origin parameter to dns.zone.from_text() and dns.zone.to_text() | |
87 | is now optional. If not specified, the origin will be taken from | |
88 | the first $ORIGIN statement in the master file. | |
89 | ||
90 | Sanity checking of a zone can be disabled; this is useful when | |
91 | working with files which are zone fragments. | |
92 | ||
93 | Bugs fixed since 1.3.5: | |
94 | ||
95 | The correct delimiter was not used when retrieving the | |
96 | list of nameservers from the registry in certain versions of | |
97 | windows. | |
98 | ||
99 | The floating-point version of latitude and longitude in LOC RRs | |
100 | (float_latitude and float_longitude) had incorrect signs for | |
101 | south latitudes and west longitudes. | |
102 | ||
103 | BIND 8 TTL syntax is now accepted in all TTL-like places (i.e. | |
104 | SOA fields refresh, retry, expire, and minimum; SIG/RRSIG | |
105 | field original_ttl). | |
106 | ||
107 | TTLs are now bounds checked when their text form is parsed, | |
108 | and their values must be in the closed interval [0, 2^31 - 1]. | |
25 | 109 | |
26 | 110 | New since 1.3.4: |
27 | 111 | |
37 | 121 | |
38 | 122 | will output "dnspython.org." and |
39 | 123 | |
40 | print dns.resolver.zone_for_name('a.b.c.d.e.f.example.') | |
124 | print dns.resolver.zone_for_name('a.b.c.d.e.f.example.') | |
41 | 125 | |
42 | 126 | will output ".". |
43 | 127 |
16 | 16 | |
17 | 17 | __all__ = [ |
18 | 18 | 'dnssec', |
19 | 'e164', | |
19 | 20 | 'exception', |
20 | 21 | 'flags', |
21 | 22 | 'inet', |
34 | 35 | 'rdatatype', |
35 | 36 | 'renderer', |
36 | 37 | 'resolver', |
38 | 'reversename', | |
37 | 39 | 'rrset', |
38 | 40 | 'set', |
39 | 41 | 'tokenizer', |
0 | # Copyright (C) 2005 Nominum, Inc. | |
1 | # | |
2 | # Permission to use, copy, modify, and distribute this software and its | |
3 | # documentation for any purpose with or without fee is hereby granted, | |
4 | # provided that the above copyright notice and this permission notice | |
5 | # appear in all copies. | |
6 | # | |
7 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
13 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | ||
15 | """DNS E.164 helpers | |
16 | ||
17 | @var public_enum_domain: The DNS public ENUM domain, e164.arpa. | |
18 | @type public_enum_domain: dns.name.Name object | |
19 | """ | |
20 | ||
21 | import dns.exception | |
22 | import dns.name | |
23 | import dns.resolver | |
24 | ||
25 | public_enum_domain = dns.name.from_text('e164.arpa.') | |
26 | ||
27 | def from_e164(text, origin=public_enum_domain): | |
28 | """Convert an E.164 number in textual form into a Name object whose | |
29 | value is the ENUM domain name for that number. | |
30 | @param text: an E.164 number in textual form. | |
31 | @type text: str | |
32 | @param origin: The domain in which the number should be constructed. | |
33 | The default is e164.arpa. | |
34 | @type: dns.name.Name object or None | |
35 | @rtype: dns.name.Name object | |
36 | """ | |
37 | parts = [d for d in text if d.isdigit()] | |
38 | parts.reverse() | |
39 | return dns.name.from_text('.'.join(parts), origin=origin) | |
40 | ||
41 | def to_e164(name, origin=public_enum_domain, want_plus_prefix=True): | |
42 | """Convert an ENUM domain name into an E.164 number. | |
43 | @param name: the ENUM domain name. | |
44 | @type name: dns.name.Name object. | |
45 | @param origin: A domain containing the ENUM domain name. The | |
46 | name is relativized to this domain before being converted to text. | |
47 | @type: dns.name.Name object or None | |
48 | @param want_plus_prefix: if True, add a '+' to the beginning of the | |
49 | returned number. | |
50 | @rtype: str | |
51 | """ | |
52 | if not origin is None: | |
53 | name = name.relativize(origin) | |
54 | dlabels = [d for d in name.labels if (d.isdigit() and len(d) == 1)] | |
55 | if len(dlabels) != len(name.labels): | |
56 | raise dns.exception.SyntaxError, 'non-digit labels in ENUM domain name' | |
57 | dlabels.reverse() | |
58 | text = ''.join(dlabels) | |
59 | if want_plus_prefix: | |
60 | text = '+' + text | |
61 | return text | |
62 | ||
63 | def query(number, domains, resolver=None): | |
64 | """Look for NAPTR RRs for the specified number in the specified domains. | |
65 | ||
66 | e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.']) | |
67 | """ | |
68 | if resolver is None: | |
69 | resolver = dns.resolver.get_default_resolver() | |
70 | for domain in domains: | |
71 | if isinstance(domain, (str, unicode)): | |
72 | domain = dns.name.from_text(domain) | |
73 | qname = dns.e164.from_e164(number, domain) | |
74 | try: | |
75 | return dns.resolver.query(qname, 'NAPTR') | |
76 | except dns.resolver.NXDOMAIN: | |
77 | pass | |
78 | raise dns.resolver.NXDOMAIN |
76 | 76 | @param text: the textual address |
77 | 77 | @type text: string |
78 | 78 | @raises ValueError: the address family cannot be determined from the input. |
79 | @rtype int | |
79 | @rtype: int | |
80 | 80 | """ |
81 | 81 | try: |
82 | 82 | junk = dns.ipv4.inet_aton(text) |
437 | 437 | if keyname is None: |
438 | 438 | self.keyname = self.keyring.keys()[0] |
439 | 439 | else: |
440 | if isinstance(keyname, str): | |
440 | if isinstance(keyname, (str, unicode)): | |
441 | 441 | keyname = dns.name.from_text(keyname) |
442 | 442 | self.keyname = keyname |
443 | 443 | self.fudge = fudge |
448 | 448 | self.tsig_error = tsig_error |
449 | 449 | self.other_data = other_data |
450 | 450 | |
451 | def use_edns(self, edns, ednsflags, payload, request_payload=0): | |
451 | def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None): | |
452 | 452 | """Configure EDNS behavior. |
453 | @param edns: The EDNS level to use. Specifying None or -1 means | |
454 | 'do not use EDNS'. | |
455 | @type edns: int or None | |
453 | @param edns: The EDNS level to use. Specifying None, False, or -1 | |
454 | means 'do not use EDNS', and in this case the other parameters are | |
455 | ignored. Specifying True is equivalent to specifying 0, i.e. 'use | |
456 | EDNS0'. | |
457 | @type edns: int or bool or None | |
456 | 458 | @param ednsflags: EDNS flag values. |
457 | 459 | @type ednsflags: int |
458 | 460 | @param payload: The EDNS sender's payload field, which is the maximum |
459 | 461 | size of UDP datagram the sender can handle. |
460 | 462 | @type payload: int |
463 | @param request_payload: The EDNS payload size to use when sending | |
464 | this message. If not specified, defaults to the value of payload. | |
465 | @type request_payload: int or None | |
461 | 466 | @see: RFC 2671 |
462 | 467 | """ |
463 | if edns is None: | |
468 | if edns is None or edns is False: | |
464 | 469 | edns = -1 |
470 | if edns is True: | |
471 | edns = 0 | |
472 | if request_payload is None: | |
473 | request_payload = payload | |
474 | if edns < 0: | |
475 | ednsflags = 0 | |
476 | payload = 0 | |
477 | request_payload = 0 | |
465 | 478 | self.edns = edns |
466 | 479 | self.ednsflags = ednsflags |
467 | 480 | self.payload = payload |
468 | 481 | self.request_payload = request_payload |
482 | ||
483 | def want_dnssec(self, wanted=True): | |
484 | """Enable or disable 'DNSSEC desired' flag in requests. | |
485 | @param wanted: Is DNSSEC desired? If True, EDNS is enabled if | |
486 | required, and then the DO bit is set. If False, the DO bit is | |
487 | cleared if EDNS is enabled. | |
488 | @type wanted: bool | |
489 | """ | |
490 | if wanted: | |
491 | if self.edns < 0: | |
492 | self.use_edns() | |
493 | self.ednsflags |= dns.flags.DO | |
494 | elif self.edns >= 0: | |
495 | self.ednsflags &= ~dns.flags.DO | |
469 | 496 | |
470 | 497 | def rcode(self): |
471 | 498 | """Return the rcode. |
936 | 963 | f.close() |
937 | 964 | return m |
938 | 965 | |
939 | def make_query(qname, rdtype, rdclass = dns.rdataclass.IN): | |
966 | def make_query(qname, rdtype, rdclass = dns.rdataclass.IN, use_edns=None, | |
967 | want_dnssec=False): | |
940 | 968 | """Make a query message. |
941 | 969 | |
942 | 970 | The query name, type, and class may all be specified either |
951 | 979 | @type rdtype: int |
952 | 980 | @param rdclass: The desired rdata class; the default is class IN. |
953 | 981 | @type rdclass: int |
982 | @param use_edns: The EDNS level to use; the default is None (no EDNS). | |
983 | See the description of dns.message.Message.use_edns() for the possible | |
984 | values for use_edns and their meanings. | |
985 | @type use_edns: int or bool or None | |
986 | @param want_dnssec: Should the query indicate that DNSSEC is desired? | |
987 | @type want_dnssec: bool | |
954 | 988 | @rtype: dns.message.Message object""" |
955 | 989 | |
956 | if isinstance(qname, str): | |
990 | if isinstance(qname, (str, unicode)): | |
957 | 991 | qname = dns.name.from_text(qname) |
958 | 992 | if isinstance(rdtype, str): |
959 | 993 | rdtype = dns.rdatatype.from_text(rdtype) |
963 | 997 | m.flags |= dns.flags.RD |
964 | 998 | m.find_rrset(m.question, qname, rdclass, rdtype, create=True, |
965 | 999 | force_unique=True) |
1000 | m.use_edns(use_edns) | |
1001 | m.want_dnssec(want_dnssec) | |
966 | 1002 | return m |
967 | 1003 | |
968 | 1004 | def make_response(query, recursion_available=False, our_payload=8192): |
21 | 21 | """ |
22 | 22 | |
23 | 23 | import cStringIO |
24 | import string | |
25 | 24 | import struct |
26 | 25 | import sys |
26 | ||
27 | if sys.hexversion >= 0x02030000: | |
28 | import encodings.idna | |
27 | 29 | |
28 | 30 | import dns.exception |
29 | 31 | |
320 | 322 | l = self.labels[:-1] |
321 | 323 | else: |
322 | 324 | l = self.labels |
323 | s = string.join(map(_escapify, l), '.') | |
325 | s = '.'.join(map(_escapify, l)) | |
326 | return s | |
327 | ||
328 | def to_unicode(self, omit_final_dot = False): | |
329 | """Convert name to Unicode text format. | |
330 | ||
331 | IDN ACE lables are converted to Unicode. | |
332 | ||
333 | @param omit_final_dot: If True, don't emit the final dot (denoting the | |
334 | root label) for absolute names. The default is False. | |
335 | @rtype: string | |
336 | """ | |
337 | ||
338 | if len(self.labels) == 0: | |
339 | return u'@' | |
340 | if len(self.labels) == 1 and self.labels[0] == '': | |
341 | return u'.' | |
342 | if omit_final_dot and self.is_absolute(): | |
343 | l = self.labels[:-1] | |
344 | else: | |
345 | l = self.labels | |
346 | s = u'.'.join([encodings.idna.ToUnicode(_escapify(x)) for x in l]) | |
324 | 347 | return s |
325 | 348 | |
326 | 349 | def to_digestable(self, origin=None): |
507 | 530 | root = Name(['']) |
508 | 531 | empty = Name([]) |
509 | 532 | |
533 | def from_unicode(text, origin = root): | |
534 | """Convert unicode text into a Name object. | |
535 | ||
536 | Lables are encoded in IDN ACE form. | |
537 | ||
538 | @rtype: dns.name.Name object | |
539 | """ | |
540 | ||
541 | if not isinstance(text, unicode): | |
542 | raise ValueError, "input to from_unicode() must be a unicode string" | |
543 | if not (origin is None or isinstance(origin, Name)): | |
544 | raise ValueError, "origin must be a Name or None" | |
545 | labels = [] | |
546 | label = u'' | |
547 | escaping = False | |
548 | edigits = 0 | |
549 | total = 0 | |
550 | if text == u'@': | |
551 | text = u'' | |
552 | if text: | |
553 | if text == u'.': | |
554 | return Name(['']) # no Unicode "u" on this constant! | |
555 | for c in text: | |
556 | if escaping: | |
557 | if edigits == 0: | |
558 | if c.isdigit(): | |
559 | total = int(c) | |
560 | edigits += 1 | |
561 | else: | |
562 | label += c | |
563 | escaping = False | |
564 | else: | |
565 | if not c.isdigit(): | |
566 | raise BadEscape | |
567 | total *= 10 | |
568 | total += int(c) | |
569 | edigits += 1 | |
570 | if edigits == 3: | |
571 | escaping = False | |
572 | label += chr(total) | |
573 | elif c == u'.' or c == u'\u3002' or \ | |
574 | c == u'\uff0e' or c == u'\uff61': | |
575 | if len(label) == 0: | |
576 | raise EmptyLabel | |
577 | labels.append(encodings.idna.ToASCII(label)) | |
578 | label = u'' | |
579 | elif c == u'\\': | |
580 | escaping = True | |
581 | edigits = 0 | |
582 | total = 0 | |
583 | else: | |
584 | label += c | |
585 | if escaping: | |
586 | raise BadEscape | |
587 | if len(label) > 0: | |
588 | labels.append(encodings.idna.ToASCII(label)) | |
589 | else: | |
590 | labels.append('') | |
591 | if (len(labels) == 0 or labels[-1] != '') and not origin is None: | |
592 | labels.extend(list(origin.labels)) | |
593 | return Name(labels) | |
594 | ||
510 | 595 | def from_text(text, origin = root): |
511 | 596 | """Convert text into a Name object. |
512 | 597 | @rtype: dns.name.Name object |
513 | 598 | """ |
514 | 599 | |
515 | 600 | if not isinstance(text, str): |
516 | raise ValueError, "input to from_text() must be a byte string" | |
601 | if isinstance(text, unicode) and sys.hexversion >= 0x02030000: | |
602 | return from_unicode(text, origin) | |
603 | else: | |
604 | raise ValueError, "input to from_text() must be a string" | |
517 | 605 | if not (origin is None or isinstance(origin, Name)): |
518 | 606 | raise ValueError, "origin must be a Name or None" |
519 | 607 | labels = [] |
64 | 64 | def _wait_for_writable(s, expiration): |
65 | 65 | _wait_for([], [s], [s], expiration) |
66 | 66 | |
67 | def udp(q, where, timeout=None, port=53, af=None, source=None, source_port=0): | |
67 | def udp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, | |
68 | ignore_unexpected=False): | |
68 | 69 | """Return the response obtained after sending a query via UDP. |
69 | 70 | |
70 | 71 | @param q: the query |
85 | 86 | @type source: string |
86 | 87 | @param source_port: The port from which to send the message. |
87 | 88 | The default is 0. |
88 | @type source_port: int""" | |
89 | @type source_port: int | |
90 | @param ignore_unexpected: If True, ignore responses from unexpected | |
91 | sources. The default is False. | |
92 | @type ignore_unexpected: bool""" | |
89 | 93 | |
90 | 94 | wire = q.to_wire() |
91 | 95 | if af is None: |
109 | 113 | s.bind(source) |
110 | 114 | _wait_for_writable(s, expiration) |
111 | 115 | s.sendto(wire, destination) |
112 | _wait_for_readable(s, expiration) | |
113 | (wire, from_address) = s.recvfrom(65535) | |
116 | while 1: | |
117 | _wait_for_readable(s, expiration) | |
118 | (wire, from_address) = s.recvfrom(65535) | |
119 | if from_address == destination: | |
120 | break | |
121 | if not ignore_unexpected: | |
122 | raise UnexpectedSource, \ | |
123 | 'got a response from %s instead of %s' % (from_address, | |
124 | destination) | |
114 | 125 | finally: |
115 | 126 | s.close() |
116 | if from_address != destination: | |
117 | raise UnexpectedSource | |
118 | 127 | r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac) |
119 | 128 | if not q.is_response(r): |
120 | 129 | raise BadResponse |
221 | 230 | |
222 | 231 | def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN, |
223 | 232 | timeout=None, port=53, keyring=None, keyname=None, relativize=True, |
224 | af=None, lifetime=None, source=None, source_port=0): | |
233 | af=None, lifetime=None, source=None, source_port=0, serial=0): | |
225 | 234 | """Return a generator for the responses to a zone transfer. |
226 | 235 | |
227 | 236 | @param where: where to send the message |
260 | 269 | @type source: string |
261 | 270 | @param source_port: The port from which to send the message. |
262 | 271 | The default is 0. |
263 | @type source_port: int""" | |
264 | ||
265 | if isinstance(zone, str): | |
272 | @type source_port: int | |
273 | @param serial: The SOA serial number to use as the base for an IXFR diff | |
274 | sequence (only meaningful if rdtype == dns.rdatatype.IXFR). | |
275 | @type serial: int""" | |
276 | ||
277 | if isinstance(zone, (str, unicode)): | |
266 | 278 | zone = dns.name.from_text(zone) |
267 | 279 | q = dns.message.make_query(zone, rdtype, rdclass) |
280 | if rdtype == dns.rdatatype.IXFR: | |
281 | rrset = dns.rrset.from_text(zone, 0, 'IN', 'SOA', | |
282 | '. . %u 0 0 0 0' % serial) | |
283 | q.authority.append(rrset) | |
268 | 284 | if not keyring is None: |
269 | 285 | q.use_tsig(keyring, keyname) |
270 | 286 | wire = q.to_wire() |
290 | 306 | tcpmsg = struct.pack("!H", l) + wire |
291 | 307 | _net_write(s, tcpmsg, expiration) |
292 | 308 | done = False |
293 | seen_soa = False | |
309 | soa_rrset = None | |
310 | soa_count = 0 | |
294 | 311 | if relativize: |
295 | 312 | origin = zone |
296 | 313 | oname = dns.name.empty |
311 | 328 | multi=True, first=first) |
312 | 329 | tsig_ctx = r.tsig_ctx |
313 | 330 | first = False |
314 | if not seen_soa: | |
331 | answer_index = 0 | |
332 | delete_mode = False | |
333 | expecting_SOA = False | |
334 | if soa_rrset is None: | |
315 | 335 | if not r.answer or r.answer[0].name != oname: |
316 | 336 | raise dns.exception.FormError |
317 | 337 | rrset = r.answer[0] |
318 | 338 | if rrset.rdtype != dns.rdatatype.SOA: |
319 | raise dns.exception.FormError | |
320 | seen_soa = True | |
321 | if len(r.answer) > 1 and r.answer[-1].name == oname: | |
322 | rrset = r.answer[-1] | |
323 | if rrset.rdtype == dns.rdatatype.SOA: | |
324 | if q.keyring and not r.had_tsig: | |
325 | raise dns.exception.FormError, "missing TSIG" | |
339 | raise dns.exception.FormError, "first RRset is not an SOA" | |
340 | answer_index = 1 | |
341 | soa_rrset = rrset.copy() | |
342 | if rdtype == dns.rdatatype.IXFR: | |
343 | if soa_rrset[0].serial == serial: | |
344 | # | |
345 | # We're already up-to-date. | |
346 | # | |
326 | 347 | done = True |
327 | elif r.answer and r.answer[-1].name == oname: | |
328 | rrset = r.answer[-1] | |
329 | if rrset.rdtype == dns.rdatatype.SOA: | |
330 | if q.keyring and not r.had_tsig: | |
331 | raise dns.exception.FormError, "missing TSIG" | |
332 | done = True | |
348 | else: | |
349 | expecting_SOA = True | |
350 | # | |
351 | # Process SOAs in the answer section (other than the initial | |
352 | # SOA in the first message). | |
353 | # | |
354 | for rrset in r.answer[answer_index:]: | |
355 | if done: | |
356 | raise dns.exception.FormError, "answers after final SOA" | |
357 | if rrset.rdtype == dns.rdatatype.SOA and rrset.name == oname: | |
358 | if expecting_SOA: | |
359 | if rrset[0].serial != serial: | |
360 | raise dns.exception.FormError, \ | |
361 | "IXFR base serial mismatch" | |
362 | expecting_SOA = False | |
363 | elif rdtype == dns.rdatatype.IXFR: | |
364 | delete_mode = not delete_mode | |
365 | if rrset == soa_rrset and not delete_mode: | |
366 | done = True | |
367 | elif expecting_SOA: | |
368 | # | |
369 | # We made an IXFR request and are expecting another | |
370 | # SOA RR, but saw something else, so this must be an | |
371 | # AXFR response. | |
372 | # | |
373 | rdtype = dns.rdatatype.AXFR | |
374 | expecting_SOA = False | |
375 | if done and q.keyring and not r.had_tsig: | |
376 | raise dns.exception.FormError, "missing TSIG" | |
333 | 377 | yield r |
334 | 378 | s.close() |
168 | 168 | """ |
169 | 169 | |
170 | 170 | raise NotImplementedError |
171 | ||
172 | def validate(self): | |
173 | """Check that the current contents of the rdata's fields are | |
174 | valid. If you change an rdata by assigning to its fields, | |
175 | it is a good idea to call validate() when you are done making | |
176 | changes. | |
177 | """ | |
178 | dns.rdata.from_text(self.rdclass, self.rdtype, self.to_text()) | |
171 | 179 | |
172 | 180 | def __repr__(self): |
173 | 181 | covers = self.covers() |
70 | 70 | APL = 42 |
71 | 71 | DS = 43 |
72 | 72 | SSHFP = 44 |
73 | IPSECKEY = 45 | |
73 | 74 | RRSIG = 46 |
74 | 75 | NSEC = 47 |
75 | 76 | DNSKEY = 48 |
77 | DHCID = 49 | |
78 | SPF = 99 | |
76 | 79 | UNSPEC = 103 |
77 | 80 | TKEY = 249 |
78 | 81 | TSIG = 250 |
124 | 127 | 'APL' : APL, |
125 | 128 | 'DS' : DS, |
126 | 129 | 'SSHFP' : SSHFP, |
130 | 'IPSECKEY' : IPSECKEY, | |
127 | 131 | 'RRSIG' : RRSIG, |
128 | 132 | 'NSEC' : NSEC, |
129 | 133 | 'DNSKEY' : DNSKEY, |
134 | 'DHCID' : DHCID, | |
135 | 'SPF' : SPF, | |
130 | 136 | 'UNSPEC' : UNSPEC, |
131 | 137 | 'TKEY' : TKEY, |
132 | 138 | 'TSIG' : TSIG, |
57 | 57 | value += float(what[1]) / 60.0 |
58 | 58 | value += float(what[2]) / 3600.0 |
59 | 59 | value += float(what[3]) / 3600000.0 |
60 | return value | |
60 | return sign * value | |
61 | 61 | |
62 | 62 | def _encode_size(what, desc): |
63 | 63 | what = long(what); |
31 | 31 | @type refresh: int |
32 | 32 | @ivar retry: The zone's retry value (in seconds) |
33 | 33 | @type retry: int |
34 | @ivar expiration: The zone's expiration value (in seconds) | |
35 | @type expiration: int | |
34 | @ivar expire: The zone's expiration value (in seconds) | |
35 | @type expire: int | |
36 | 36 | @ivar minimum: The zone's negative caching time (in seconds, called |
37 | 37 | "minimum" for historical reasons) |
38 | 38 | @type minimum: int |
65 | 65 | mname = mname.choose_relativity(origin, relativize) |
66 | 66 | rname = rname.choose_relativity(origin, relativize) |
67 | 67 | serial = tok.get_uint32() |
68 | refresh = tok.get_uint32() | |
69 | retry = tok.get_uint32() | |
70 | expire = tok.get_uint32() | |
71 | minimum = tok.get_uint32() | |
68 | refresh = tok.get_ttl() | |
69 | retry = tok.get_ttl() | |
70 | expire = tok.get_ttl() | |
71 | minimum = tok.get_ttl() | |
72 | 72 | tok.get_eol() |
73 | 73 | return cls(rdclass, rdtype, mname, rname, serial, refresh, retry, |
74 | 74 | expire, minimum ) |
0 | # Copyright (C) 2006 Nominum, Inc. | |
1 | # | |
2 | # Permission to use, copy, modify, and distribute this software and its | |
3 | # documentation for any purpose with or without fee is hereby granted, | |
4 | # provided that the above copyright notice and this permission notice | |
5 | # appear in all copies. | |
6 | # | |
7 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
13 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | ||
15 | import dns.rdtypes.txtbase | |
16 | ||
17 | class SPF(dns.rdtypes.txtbase.TXTBase): | |
18 | """SPF record | |
19 | ||
20 | @see: RFC 4408""" | |
21 | pass |
0 | # Copyright (C) 2003-2005 Nominum, Inc. | |
0 | # Copyright (C) 2003-2006 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, |
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 dns.exception | |
16 | import dns.rdata | |
17 | import dns.tokenizer | |
15 | import dns.rdtypes.txtbase | |
18 | 16 | |
19 | class TXT(dns.rdata.Rdata): | |
20 | """TXT record | |
21 | ||
22 | @ivar strings: the text strings | |
23 | @type strings: list of string | |
24 | @see: RFC 1035""" | |
25 | ||
26 | __slots__ = ['strings'] | |
27 | ||
28 | def __init__(self, rdclass, rdtype, strings): | |
29 | super(TXT, self).__init__(rdclass, rdtype) | |
30 | if isinstance(strings, str): | |
31 | strings = [ strings ] | |
32 | self.strings = strings[:] | |
33 | ||
34 | def to_text(self, origin=None, relativize=True, **kw): | |
35 | txt = '' | |
36 | prefix = '' | |
37 | for s in self.strings: | |
38 | txt += '%s"%s"' % (prefix, dns.rdata._escapify(s)) | |
39 | prefix = ' ' | |
40 | return txt | |
41 | ||
42 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): | |
43 | strings = [] | |
44 | while 1: | |
45 | (ttype, s) = tok.get() | |
46 | if ttype == dns.tokenizer.EOL or ttype == dns.tokenizer.EOF: | |
47 | break | |
48 | if ttype != dns.tokenizer.QUOTED_STRING and \ | |
49 | ttype != dns.tokenizer.IDENTIFIER: | |
50 | raise dns.exception.SyntaxError, "expected a string" | |
51 | if len(s) > 255: | |
52 | raise dns.exception.SyntaxError, "string too long" | |
53 | strings.append(s) | |
54 | if len(strings) == 0: | |
55 | raise dns.exception.UnexpectedEnd | |
56 | return cls(rdclass, rdtype, strings) | |
57 | ||
58 | from_text = classmethod(from_text) | |
59 | ||
60 | def to_wire(self, file, compress = None, origin = None): | |
61 | for s in self.strings: | |
62 | l = len(s) | |
63 | assert l < 256 | |
64 | byte = chr(l) | |
65 | file.write(byte) | |
66 | file.write(s) | |
67 | ||
68 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): | |
69 | strings = [] | |
70 | while rdlen > 0: | |
71 | l = ord(wire[current]) | |
72 | current += 1 | |
73 | rdlen -= 1 | |
74 | if l > rdlen: | |
75 | raise dns.exception.FormError | |
76 | s = wire[current : current + l] | |
77 | current += l | |
78 | rdlen -= l | |
79 | strings.append(s) | |
80 | return cls(rdclass, rdtype, strings) | |
81 | ||
82 | from_wire = classmethod(from_wire) | |
83 | ||
84 | def _cmp(self, other): | |
85 | return cmp(self.strings, other.strings) | |
17 | class TXT(dns.rdtypes.txtbase.TXTBase): | |
18 | """TXT record""" | |
19 | pass |
19 | 19 | 'CERT', |
20 | 20 | 'CNAME', |
21 | 21 | 'DNAME', |
22 | 'DNSKEY', | |
22 | 23 | 'DS', |
23 | 24 | 'GPOS', |
24 | 25 | 'HINFO', |
27 | 28 | 'LOC', |
28 | 29 | 'MX', |
29 | 30 | 'NS', |
31 | 'NSEC', | |
30 | 32 | 'NXT', |
31 | 33 | 'PTR', |
32 | 34 | 'RP', |
35 | 'RRSIG', | |
33 | 36 | 'RT', |
34 | 37 | 'SIG', |
35 | 38 | 'SOA', |
39 | 'SPF', | |
40 | 'SSHFP', | |
36 | 41 | 'TXT', |
37 | 42 | 'X25', |
38 | 'RRSIG', | |
39 | 'NSEC', | |
40 | 'DNSKEY', | |
41 | 'SSHFP', | |
42 | 43 | ] |
0 | # Copyright (C) 2006 Nominum, Inc. | |
1 | # | |
2 | # Permission to use, copy, modify, and distribute this software and its | |
3 | # documentation for any purpose with or without fee is hereby granted, | |
4 | # provided that the above copyright notice and this permission notice | |
5 | # appear in all copies. | |
6 | # | |
7 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
13 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | ||
15 | import dns.exception | |
16 | ||
17 | class DHCID(dns.rdata.Rdata): | |
18 | """DHCID record | |
19 | ||
20 | @ivar data: the data (the content of the RR is opaque as far as the | |
21 | DNS is concerned) | |
22 | @type data: string | |
23 | @see: RFC 4701""" | |
24 | ||
25 | __slots__ = ['data'] | |
26 | ||
27 | def __init__(self, rdclass, rdtype, data): | |
28 | super(DHCID, self).__init__(rdclass, rdtype) | |
29 | self.data = data | |
30 | ||
31 | def to_text(self, origin=None, relativize=True, **kw): | |
32 | return dns.rdata._base64ify(self.data) | |
33 | ||
34 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): | |
35 | chunks = [] | |
36 | while 1: | |
37 | t = tok.get() | |
38 | if t[0] == dns.tokenizer.EOL or t[0] == dns.tokenizer.EOF: | |
39 | break | |
40 | if t[0] != dns.tokenizer.IDENTIFIER: | |
41 | raise dns.exception.SyntaxError | |
42 | chunks.append(t[1]) | |
43 | b64 = ''.join(chunks) | |
44 | data = b64.decode('base64_codec') | |
45 | return cls(rdclass, rdtype, data) | |
46 | ||
47 | from_text = classmethod(from_text) | |
48 | ||
49 | def to_wire(self, file, compress = None, origin = None): | |
50 | file.write(self.data) | |
51 | ||
52 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): | |
53 | data = wire[current : current + rdlen] | |
54 | return cls(rdclass, rdtype, data) | |
55 | ||
56 | from_wire = classmethod(from_wire) | |
57 | ||
58 | def _cmp(self, other): | |
59 | return cmp(self.data, other.data) |
0 | # Copyright (C) 2006 Nominum, Inc. | |
1 | # | |
2 | # Permission to use, copy, modify, and distribute this software and its | |
3 | # documentation for any purpose with or without fee is hereby granted, | |
4 | # provided that the above copyright notice and this permission notice | |
5 | # appear in all copies. | |
6 | # | |
7 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
13 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | ||
15 | import cStringIO | |
16 | import struct | |
17 | ||
18 | import dns.exception | |
19 | import dns.inet | |
20 | import dns.name | |
21 | ||
22 | class IPSECKEY(dns.rdata.Rdata): | |
23 | """IPSECKEY record | |
24 | ||
25 | @ivar precedence: the precedence for this key data | |
26 | @type precedence: int | |
27 | @ivar gateway_type: the gateway type | |
28 | @type gateway_type: int | |
29 | @ivar algorithm: the algorithm to use | |
30 | @type algorithm: int | |
31 | @ivar gateway: the public key | |
32 | @type gateway: None, IPv4 address, IPV6 address, or domain name | |
33 | @ivar key: the public key | |
34 | @type key: string | |
35 | @see: RFC 4025""" | |
36 | ||
37 | __slots__ = ['precedence', 'gateway_type', 'algorithm', 'gateway', 'key'] | |
38 | ||
39 | def __init__(self, rdclass, rdtype, precedence, gateway_type, algorithm, | |
40 | gateway, key): | |
41 | super(IPSECKEY, self).__init__(rdclass, rdtype) | |
42 | if gateway_type == 0: | |
43 | if gateway != '.' and not gateway is None: | |
44 | raise SyntaxError, 'invalid gateway for gateway type 0' | |
45 | gateway = None | |
46 | elif gateway_type == 1: | |
47 | # check that it's OK | |
48 | junk = dns.inet.inet_pton(dns.inet.AF_INET, gateway) | |
49 | elif gateway_type == 2: | |
50 | # check that it's OK | |
51 | junk = dns.inet.inet_pton(dns.inet.AF_INET6, gateway) | |
52 | elif gateway_type == 3: | |
53 | pass | |
54 | else: | |
55 | raise SyntaxError, \ | |
56 | 'invalid IPSECKEY gateway type: %d' % gateway_type | |
57 | self.precedence = precedence | |
58 | self.gateway_type = gateway_type | |
59 | self.algorithm = algorithm | |
60 | self.gateway = gateway | |
61 | self.key = key | |
62 | ||
63 | def to_text(self, origin=None, relativize=True, **kw): | |
64 | if self.gateway_type == 0: | |
65 | gateway = '.' | |
66 | elif self.gateway_type == 1: | |
67 | gateway = self.gateway | |
68 | elif self.gateway_type == 2: | |
69 | gateway = self.gateway | |
70 | elif self.gateway_type == 3: | |
71 | gateway = str(self.gateway.choose_relativity(origin, relativize)) | |
72 | else: | |
73 | raise ValueError, 'invalid gateway type' | |
74 | return '%d %d %d %s %s' % (self.precedence, self.gateway_type, | |
75 | self.algorithm, gateway, | |
76 | dns.rdata._base64ify(self.key)) | |
77 | ||
78 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): | |
79 | precedence = tok.get_uint8() | |
80 | gateway_type = tok.get_uint8() | |
81 | algorithm = tok.get_uint8() | |
82 | if gateway_type == 3: | |
83 | gateway = tok.get_name().choose_relativity(origin, relativize) | |
84 | else: | |
85 | gateway = tok.get_string() | |
86 | chunks = [] | |
87 | while 1: | |
88 | t = tok.get() | |
89 | if t[0] == dns.tokenizer.EOL or t[0] == dns.tokenizer.EOF: | |
90 | break | |
91 | if t[0] != dns.tokenizer.IDENTIFIER: | |
92 | raise dns.exception.SyntaxError | |
93 | chunks.append(t[1]) | |
94 | b64 = ''.join(chunks) | |
95 | key = b64.decode('base64_codec') | |
96 | return cls(rdclass, rdtype, precedence, gateway_type, algorithm, | |
97 | gateway, key) | |
98 | ||
99 | from_text = classmethod(from_text) | |
100 | ||
101 | def to_wire(self, file, compress = None, origin = None): | |
102 | header = struct.pack("!BBB", self.precedence, self.gateway_type, | |
103 | self.algorithm) | |
104 | file.write(header) | |
105 | if self.gateway_type == 0: | |
106 | pass | |
107 | elif self.gateway_type == 1: | |
108 | file.write(dns.inet.inet_pton(dns.inet.AF_INET, self.gateway)) | |
109 | elif self.gateway_type == 2: | |
110 | file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.gateway)) | |
111 | elif self.gateway_type == 3: | |
112 | self.gateway.to_wire(file, None, origin) | |
113 | else: | |
114 | raise ValueError, 'invalid gateway type' | |
115 | file.write(self.key) | |
116 | ||
117 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): | |
118 | if rdlen < 3: | |
119 | raise dns.exception.FormError | |
120 | header = struct.unpack('!BBB', wire[current : current + 3]) | |
121 | gateway_type = header[1] | |
122 | current += 3 | |
123 | rdlen -= 3 | |
124 | if gateway_type == 0: | |
125 | gateway = None | |
126 | elif gateway_type == 1: | |
127 | gateway = dns.inet.inet_ntop(dns.inet.AF_INET, | |
128 | wire[current : current + 4]) | |
129 | current += 4 | |
130 | rdlen -= 4 | |
131 | elif gateway_type == 2: | |
132 | gateway = dns.inet.inet_ntop(dns.inet.AF_INET6, | |
133 | wire[current : current + 16]) | |
134 | current += 16 | |
135 | rdlen -= 16 | |
136 | elif gateway_type == 3: | |
137 | (gateway, cused) = dns.name.from_wire(wire[: current + rdlen], | |
138 | current) | |
139 | current += cused | |
140 | rdlen -= cused | |
141 | else: | |
142 | raise dns.exception.FormError, 'invalid IPSECKEY gateway type' | |
143 | key = wire[current : current + rdlen] | |
144 | return cls(rdclass, rdtype, header[0], gateway_type, header[2], | |
145 | gateway, key) | |
146 | ||
147 | from_wire = classmethod(from_wire) | |
148 | ||
149 | def _cmp(self, other): | |
150 | f = cStringIO.StringIO() | |
151 | self.to_wire(f) | |
152 | wire1 = f.getvalue() | |
153 | f.seek(0) | |
154 | f.truncate() | |
155 | other.to_wire(f) | |
156 | wire2 = f.getvalue() | |
157 | f.close() | |
158 | ||
159 | return cmp(wire1, wire2) |
18 | 18 | 'A', |
19 | 19 | 'AAAA', |
20 | 20 | 'APL', |
21 | 'DHCID', | |
21 | 22 | 'KX', |
22 | 'PX', | |
23 | 23 | 'NAPTR', |
24 | 24 | 'NSAP', |
25 | 25 | 'NSAP_PTR', |
26 | 'PX', | |
26 | 27 | 'SRV', |
27 | 28 | 'WKS', |
28 | 29 | ] |
100 | 100 | type_covered = dns.rdatatype.from_text(tok.get_string()) |
101 | 101 | algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) |
102 | 102 | labels = tok.get_int() |
103 | original_ttl = tok.get_uint32() | |
103 | original_ttl = tok.get_ttl() | |
104 | 104 | expiration = sigtime_to_posixtime(tok.get_string()) |
105 | 105 | inception = sigtime_to_posixtime(tok.get_string()) |
106 | 106 | key_tag = tok.get_int() |
0 | # Copyright (C) 2003-2005 Nominum, Inc. | |
1 | # | |
2 | # Permission to use, copy, modify, and distribute this software and its | |
3 | # documentation for any purpose with or without fee is hereby granted, | |
4 | # provided that the above copyright notice and this permission notice | |
5 | # appear in all copies. | |
6 | # | |
7 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
13 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | ||
15 | """TXT-like base class.""" | |
16 | ||
17 | import dns.exception | |
18 | import dns.rdata | |
19 | import dns.tokenizer | |
20 | ||
21 | class TXTBase(dns.rdata.Rdata): | |
22 | """Base class for rdata that is like a TXT record | |
23 | ||
24 | @ivar strings: the text strings | |
25 | @type strings: list of string | |
26 | @see: RFC 1035""" | |
27 | ||
28 | __slots__ = ['strings'] | |
29 | ||
30 | def __init__(self, rdclass, rdtype, strings): | |
31 | super(TXTBase, self).__init__(rdclass, rdtype) | |
32 | if isinstance(strings, str): | |
33 | strings = [ strings ] | |
34 | self.strings = strings[:] | |
35 | ||
36 | def to_text(self, origin=None, relativize=True, **kw): | |
37 | txt = '' | |
38 | prefix = '' | |
39 | for s in self.strings: | |
40 | txt += '%s"%s"' % (prefix, dns.rdata._escapify(s)) | |
41 | prefix = ' ' | |
42 | return txt | |
43 | ||
44 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): | |
45 | strings = [] | |
46 | while 1: | |
47 | (ttype, s) = tok.get() | |
48 | if ttype == dns.tokenizer.EOL or ttype == dns.tokenizer.EOF: | |
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) | |
56 | if len(strings) == 0: | |
57 | raise dns.exception.UnexpectedEnd | |
58 | return cls(rdclass, rdtype, strings) | |
59 | ||
60 | from_text = classmethod(from_text) | |
61 | ||
62 | def to_wire(self, file, compress = None, origin = None): | |
63 | for s in self.strings: | |
64 | l = len(s) | |
65 | assert l < 256 | |
66 | byte = chr(l) | |
67 | file.write(byte) | |
68 | file.write(s) | |
69 | ||
70 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): | |
71 | strings = [] | |
72 | while rdlen > 0: | |
73 | l = ord(wire[current]) | |
74 | current += 1 | |
75 | rdlen -= 1 | |
76 | if l > rdlen: | |
77 | raise dns.exception.FormError | |
78 | s = wire[current : current + l] | |
79 | current += l | |
80 | rdlen -= l | |
81 | strings.append(s) | |
82 | return cls(rdclass, rdtype, strings) | |
83 | ||
84 | from_wire = classmethod(from_wire) | |
85 | ||
86 | def _cmp(self, other): | |
87 | return cmp(self.strings, other.strings) |
67 | 67 | Instances of this class bundle up the result of a successful DNS |
68 | 68 | resolution. |
69 | 69 | |
70 | For convenience, the answer is iterable. "for a in answer" is | |
71 | equivalent to "for a in answer.rrset". | |
70 | For convenience, the answer object implements much of the sequence | |
71 | protocol, forwarding to its rrset. E.g. "for a in answer" is | |
72 | equivalent to "for a in answer.rrset", "answer[i]" is equivalent | |
73 | to "answer.rrset[i]", and "answer[i:j]" is equivalent to | |
74 | "answer.rrset[i:j]". | |
72 | 75 | |
73 | 76 | Note that CNAMEs or DNAMEs in the response may mean that answer |
74 | 77 | node's name might not be the query name. |
141 | 144 | def __iter__(self): |
142 | 145 | return iter(self.rrset) |
143 | 146 | |
147 | def __getitem__(self, i): | |
148 | return self.rrset[i] | |
149 | ||
150 | def __delitem__(self, i): | |
151 | del self.rrset[i] | |
152 | ||
153 | def __getslice__(self, i, j): | |
154 | return self.rrset[i:j] | |
155 | ||
156 | def __delslice__(self, i, j): | |
157 | del self.rrset[i:j] | |
158 | ||
144 | 159 | class Cache(object): |
145 | 160 | """Simple DNS answer cache. |
146 | 161 | |
223 | 238 | else: |
224 | 239 | self.data = {} |
225 | 240 | self.next_cleaning = time.time() + self.cleaning_interval |
226 | ||
241 | ||
227 | 242 | class Resolver(object): |
228 | 243 | """DNS stub resolver |
229 | 244 | |
301 | 316 | a string, it is used as the name of the file to open; otherwise it |
302 | 317 | is treated as the file itself.""" |
303 | 318 | if isinstance(f, str) or isinstance(f, unicode): |
304 | f = open(f, 'r') | |
319 | try: | |
320 | f = open(f, 'r') | |
321 | except IOError: | |
322 | # /etc/resolv.conf doesn't exist, can't be read, etc. | |
323 | # We'll just use the default resolver configuration. | |
324 | self.nameservers = ['127.0.0.1'] | |
325 | return | |
305 | 326 | want_close = True |
306 | 327 | else: |
307 | 328 | want_close = False |
325 | 346 | if len(self.nameservers) == 0: |
326 | 347 | self.nameservers.append('127.0.0.1') |
327 | 348 | |
328 | def _config_win32_nameservers(self, nameservers, split_char=','): | |
349 | def _determine_split_char(self, entry): | |
350 | # | |
351 | # The windows registry irritatingly changes the list element | |
352 | # delimiter in between ' ' and ',' (and vice-versa) in various | |
353 | # versions of windows. | |
354 | # | |
355 | if entry.find(' ') >= 0: | |
356 | split_char = ' ' | |
357 | elif entry.find(',') >= 0: | |
358 | split_char = ',' | |
359 | else: | |
360 | # probably a singleton; treat as a space-separated list. | |
361 | split_char = ' ' | |
362 | return split_char | |
363 | ||
364 | def _config_win32_nameservers(self, nameservers): | |
329 | 365 | """Configure a NameServer registry entry.""" |
330 | 366 | # we call str() on nameservers to convert it from unicode to ascii |
331 | ns_list = str(nameservers).split(split_char) | |
367 | nameservers = str(nameservers) | |
368 | split_char = self._determine_split_char(nameservers) | |
369 | ns_list = nameservers.split(split_char) | |
332 | 370 | for ns in ns_list: |
333 | 371 | if not ns in self.nameservers: |
334 | 372 | self.nameservers.append(ns) |
341 | 379 | def _config_win32_search(self, search): |
342 | 380 | """Configure a Search registry entry.""" |
343 | 381 | # we call str() on search to convert it from unicode to ascii |
344 | search_list = str(search).split(',') | |
382 | search = str(search) | |
383 | split_char = self._determine_split_char(search) | |
384 | search_list = search.split(split_char) | |
345 | 385 | for s in search_list: |
346 | 386 | if not s in self.search: |
347 | 387 | self.search.append(dns.name.from_text(s)) |
366 | 406 | except WindowsError: |
367 | 407 | servers = None |
368 | 408 | if servers: |
369 | # Annoyingly, the DhcpNameServer list is apparently space | |
370 | # separated instead of comma separated like NameServer. | |
371 | self._config_win32_nameservers(servers, ' ') | |
409 | self._config_win32_nameservers(servers) | |
372 | 410 | try: |
373 | 411 | dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain') |
374 | 412 | if dom: |
380 | 418 | except WindowsError: |
381 | 419 | search = None |
382 | 420 | if search: |
383 | self._config_win32_search(servers) | |
421 | self._config_win32_search(search) | |
384 | 422 | |
385 | 423 | def read_registry(self): |
386 | 424 | """Extract resolver configuration from the Windows registry.""" |
473 | 511 | @raises NoNameservers: no non-broken nameservers are available to |
474 | 512 | answer the question.""" |
475 | 513 | |
476 | if isinstance(qname, str): | |
514 | if isinstance(qname, (str, unicode)): | |
477 | 515 | qname = dns.name.from_text(qname, None) |
478 | 516 | if isinstance(rdtype, str): |
479 | 517 | rdtype = dns.rdatatype.from_text(rdtype) |
510 | 548 | while response is None: |
511 | 549 | if len(nameservers) == 0: |
512 | 550 | raise NoNameservers |
513 | for nameserver in nameservers: | |
551 | for nameserver in nameservers[:]: | |
514 | 552 | timeout = self._compute_timeout(start) |
515 | 553 | try: |
516 | 554 | if tcp: |
553 | 591 | if rcode != dns.rcode.SERVFAIL: |
554 | 592 | nameservers.remove(nameserver) |
555 | 593 | response = None |
594 | if not response is None: | |
595 | break | |
556 | 596 | # |
557 | 597 | # All nameservers failed! |
558 | 598 | # |
641 | 681 | @type resolver: dns.resolver.Resolver object or None |
642 | 682 | @rtype: dns.name.Name""" |
643 | 683 | |
644 | if isinstance(name, str): | |
684 | if isinstance(name, (str, unicode)): | |
645 | 685 | name = dns.name.from_text(name, dns.name.root) |
646 | 686 | if resolver is None: |
647 | 687 | resolver = get_default_resolver() |
0 | # Copyright (C) 2005 Nominum, Inc. | |
1 | # | |
2 | # Permission to use, copy, modify, and distribute this software and its | |
3 | # documentation for any purpose with or without fee is hereby granted, | |
4 | # provided that the above copyright notice and this permission notice | |
5 | # appear in all copies. | |
6 | # | |
7 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
13 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | ||
15 | """DNS Reverse Map Names. | |
16 | ||
17 | @var ipv4_reverse_domain: The DNS IPv4 reverse-map domain, in-addr.arpa. | |
18 | @type ipv4_reverse_domain: dns.name.Name object | |
19 | @var ipv6_reverse_domain: The DNS IPv6 reverse-map domain, ip6.arpa. | |
20 | @type ipv6_reverse_domain: dns.name.Name object | |
21 | """ | |
22 | ||
23 | import dns.name | |
24 | import dns.ipv6 | |
25 | import dns.ipv4 | |
26 | ||
27 | ipv4_reverse_domain = dns.name.from_text('in-addr.arpa.') | |
28 | ipv6_reverse_domain = dns.name.from_text('ip6.arpa.') | |
29 | ||
30 | def from_address(text): | |
31 | """Convert an IPv4 or IPv6 address in textual form into a Name object whose | |
32 | value is the reverse-map domain name of the address. | |
33 | @param text: an IPv4 or IPv6 address in textual form (e.g. '127.0.0.1', | |
34 | '::1') | |
35 | @type text: str | |
36 | @rtype: dns.name.Name object | |
37 | """ | |
38 | try: | |
39 | parts = list(dns.ipv6.inet_aton(text).encode('hex_codec')) | |
40 | origin = ipv6_reverse_domain | |
41 | except: | |
42 | parts = ['%d' % ord(byte) for byte in dns.ipv4.inet_aton(text)] | |
43 | origin = ipv4_reverse_domain | |
44 | parts.reverse() | |
45 | return dns.name.from_text('.'.join(parts), origin=origin) | |
46 | ||
47 | def to_address(name): | |
48 | """Convert a reverse map domain name into textual address form. | |
49 | @param name: an IPv4 or IPv6 address in reverse-map form. | |
50 | @type name: dns.name.Name object | |
51 | @rtype: str | |
52 | """ | |
53 | if name.is_subdomain(ipv4_reverse_domain): | |
54 | name = name.relativize(ipv4_reverse_domain) | |
55 | labels = list(name.labels) | |
56 | labels.reverse() | |
57 | text = '.'.join(labels) | |
58 | # run through inet_aton() to check syntax and make pretty. | |
59 | return dns.ipv4.inet_ntoa(dns.ipv4.inet_aton(text)) | |
60 | elif name.is_subdomain(ipv6_reverse_domain): | |
61 | name = name.relativize(ipv6_reverse_domain) | |
62 | labels = list(name.labels) | |
63 | labels.reverse() | |
64 | parts = [] | |
65 | i = 0 | |
66 | l = len(labels) | |
67 | while i < l: | |
68 | parts.append(''.join(labels[i:i+4])) | |
69 | i += 4 | |
70 | text = ':'.join(parts) | |
71 | # run through inet_aton() to check syntax and make pretty. | |
72 | return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text)) | |
73 | else: | |
74 | raise dns.exception.SyntaxError, 'unknown reverse-map address family' |
106 | 106 | return super(RRset, self).to_wire(self.name, file, compress, origin, |
107 | 107 | self.deleting, **kw) |
108 | 108 | |
109 | def to_rdataset(self): | |
110 | """Convert an RRset into an Rdataset. | |
111 | ||
112 | @rtype: dns.rdataset.Rdataset object | |
113 | """ | |
114 | return dns.rdataset.from_rdata_list(self.ttl, list(self)) | |
115 | ||
109 | 116 | |
110 | 117 | def from_text_list(name, ttl, rdclass, rdtype, text_rdatas): |
111 | 118 | """Create an RRset with the specified name, TTL, class, and type, and with |
114 | 121 | @rtype: dns.rrset.RRset object |
115 | 122 | """ |
116 | 123 | |
117 | if isinstance(name, str): | |
124 | if isinstance(name, (str, unicode)): | |
118 | 125 | name = dns.name.from_text(name, None) |
119 | 126 | if isinstance(rdclass, str): |
120 | 127 | rdclass = dns.rdataclass.from_text(rdclass) |
143 | 150 | @rtype: dns.rrset.RRset object |
144 | 151 | """ |
145 | 152 | |
146 | if isinstance(name, str): | |
153 | if isinstance(name, (str, unicode)): | |
147 | 154 | name = dns.name.from_text(name, None) |
148 | 155 | |
149 | 156 | if len(rdatas) == 0: |
19 | 19 | |
20 | 20 | import dns.exception |
21 | 21 | import dns.name |
22 | import dns.ttl | |
22 | 23 | |
23 | 24 | _DELIMITERS = { |
24 | 25 | ' ' : True, |
419 | 420 | raise dns.exception.SyntaxError, \ |
420 | 421 | 'expected EOL or EOF, got %d "%s"' % (ttype, t) |
421 | 422 | return t |
423 | ||
424 | 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) |
31 | 31 | """ |
32 | 32 | |
33 | 33 | if text.isdigit(): |
34 | return int(text) | |
35 | if not text[0].isdigit(): | |
36 | raise BadTTL | |
37 | total = 0 | |
38 | current = 0 | |
39 | for c in text: | |
40 | if c.isdigit(): | |
41 | current *= 10 | |
42 | current += int(c) | |
43 | else: | |
44 | c = c.lower() | |
45 | if c == 'w': | |
46 | total += current * 604800 | |
47 | elif c == 'd': | |
48 | total += current * 86400 | |
49 | elif c == 'h': | |
50 | total += current * 3600 | |
51 | elif c == 'm': | |
52 | total += current * 60 | |
53 | elif c == 's': | |
54 | total += current | |
34 | total = long(text) | |
35 | else: | |
36 | if not text[0].isdigit(): | |
37 | raise BadTTL | |
38 | total = 0L | |
39 | current = 0L | |
40 | for c in text: | |
41 | if c.isdigit(): | |
42 | current *= 10 | |
43 | current += long(c) | |
55 | 44 | else: |
56 | raise BadTTL, "unknown unit '%s'" % c | |
57 | current = 0 | |
58 | if not current == 0: | |
59 | raise BadTTL, "trailing integer" | |
45 | c = c.lower() | |
46 | if c == 'w': | |
47 | total += current * 604800L | |
48 | elif c == 'd': | |
49 | total += current * 86400L | |
50 | elif c == 'h': | |
51 | total += current * 3600L | |
52 | elif c == 'm': | |
53 | total += current * 60L | |
54 | elif c == 's': | |
55 | total += current | |
56 | else: | |
57 | raise BadTTL, "unknown unit '%s'" % c | |
58 | current = 0 | |
59 | if not current == 0: | |
60 | raise BadTTL, "trailing integer" | |
61 | if total < 0L or total > 2147483647L: | |
62 | raise BadTTL, "TTL should be between 0 and 2^31 - 1 (inclusive)" | |
60 | 63 | return total |
43 | 43 | """ |
44 | 44 | super(Update, self).__init__() |
45 | 45 | self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE) |
46 | if isinstance(zone, str): | |
46 | if isinstance(zone, (str, unicode)): | |
47 | 47 | zone = dns.name.from_text(zone) |
48 | else: | |
49 | zone = zone.copy() | |
50 | 48 | self.origin = zone |
51 | 49 | if isinstance(rdclass, str): |
52 | 50 | rdclass = dns.rdataclass.from_text(rdclass) |
79 | 77 | |
80 | 78 | - ttl, rdtype, string...""" |
81 | 79 | |
82 | if isinstance(name, str): | |
80 | if isinstance(name, (str, unicode)): | |
83 | 81 | name = dns.name.from_text(name, None) |
84 | 82 | if isinstance(args[0], dns.rdataset.Rdataset): |
85 | 83 | for rds in args: |
129 | 127 | |
130 | 128 | - rdtype, [string...]""" |
131 | 129 | |
132 | if isinstance(name, str): | |
130 | if isinstance(name, (str, unicode)): | |
133 | 131 | name = dns.name.from_text(name, None) |
134 | 132 | if len(args) == 0: |
135 | 133 | rrset = self.find_rrset(self.authority, name, dns.rdataclass.ANY, |
187 | 185 | |
188 | 186 | - rdtype, string...""" |
189 | 187 | |
190 | if isinstance(name, str): | |
188 | if isinstance(name, (str, unicode)): | |
191 | 189 | name = dns.name.from_text(name, None) |
192 | 190 | if len(args) == 0: |
193 | 191 | rrset = self.find_rrset(self.answer, name, |
215 | 213 | """Require that an owner name (and optionally an rdata type) does |
216 | 214 | not exist as a prerequisite to the execution of the update.""" |
217 | 215 | |
218 | if isinstance(name, str): | |
216 | if isinstance(name, (str, unicode)): | |
219 | 217 | name = dns.name.from_text(name, None) |
220 | 218 | if rdtype is None: |
221 | 219 | rrset = self.find_rrset(self.answer, name, |
15 | 15 | """dnspython release version information.""" |
16 | 16 | |
17 | 17 | MAJOR = 1 |
18 | MINOR = 3 | |
19 | MICRO = 5 | |
18 | MINOR = 5 | |
19 | MICRO = 0 | |
20 | 20 | RELEASELEVEL = 0x0f |
21 | 21 | SERIAL = 0 |
22 | 22 |
37 | 37 | |
38 | 38 | class NoNS(BadZone): |
39 | 39 | """The zone has no NS RRset at its origin.""" |
40 | pass | |
41 | ||
42 | class UnknownOrigin(BadZone): | |
43 | """The zone's origin is unknown.""" | |
40 | 44 | pass |
41 | 45 | |
42 | 46 | class Zone(object): |
101 | 105 | return not self.__eq__(other) |
102 | 106 | |
103 | 107 | def _validate_name(self, name): |
104 | if isinstance(name, str): | |
108 | if isinstance(name, (str, unicode)): | |
105 | 109 | name = dns.name.from_text(name, None) |
106 | 110 | elif not isinstance(name, dns.name.Name): |
107 | 111 | raise KeyError, \ |
162 | 166 | @param create: should the node be created if it doesn't exist? |
163 | 167 | @type create: bool |
164 | 168 | @raises KeyError: the name is not known and create was not specified. |
165 | @rtype dns.node.Node object | |
169 | @rtype: dns.node.Node object | |
166 | 170 | """ |
167 | 171 | |
168 | 172 | name = self._validate_name(name) |
185 | 189 | @type name: dns.name.Name object or string |
186 | 190 | @param create: should the node be created if it doesn't exist? |
187 | 191 | @type create: bool |
188 | @rtype dns.node.Node object or None | |
192 | @rtype: dns.node.Node object or None | |
189 | 193 | """ |
190 | 194 | |
191 | 195 | try: |
538 | 542 | (None if no $INCLUDE is active). |
539 | 543 | @ivar allow_include: is $INCLUDE allowed? |
540 | 544 | @type allow_include: bool |
545 | @ivar check_origin: should sanity checks of the origin node be done? | |
546 | The default is True. | |
547 | @type check_origin: bool | |
541 | 548 | """ |
542 | 549 | |
543 | 550 | def __init__(self, tok, origin, rdclass, relativize, zone_factory=Zone, |
544 | allow_include=False): | |
545 | if isinstance(origin, str): | |
551 | allow_include=False, check_origin=True): | |
552 | if isinstance(origin, (str, unicode)): | |
546 | 553 | origin = dns.name.from_text(origin) |
547 | 554 | self.tok = tok |
548 | 555 | self.current_origin = origin |
553 | 560 | self.saved_state = [] |
554 | 561 | self.current_file = None |
555 | 562 | self.allow_include = allow_include |
563 | self.check_origin = check_origin | |
556 | 564 | |
557 | 565 | def _eat_line(self): |
558 | 566 | while 1: |
563 | 571 | def _rr_line(self): |
564 | 572 | """Process one line from a DNS master file.""" |
565 | 573 | # Name |
574 | if self.current_origin is None: | |
575 | raise UnknownOrigin | |
566 | 576 | token = self.tok.get(want_leading = True) |
567 | 577 | if token[0] != dns.tokenizer.WHITESPACE: |
568 | 578 | self.last_name = dns.name.from_text(token[1], self.current_origin) |
672 | 682 | elif u == '$ORIGIN': |
673 | 683 | self.current_origin = self.tok.get_name() |
674 | 684 | self.tok.get_eol() |
685 | if self.zone.origin is None: | |
686 | self.zone.origin = self.current_origin | |
675 | 687 | elif u == '$INCLUDE' and self.allow_include: |
676 | 688 | token = self.tok.get() |
677 | 689 | if token[0] != dns.tokenizer.QUOTED_STRING: |
712 | 724 | "%s:%d: %s" % (filename, line_number, detail) |
713 | 725 | |
714 | 726 | # Now that we're done reading, do some basic checking of the zone. |
715 | self.zone.check_origin() | |
716 | ||
717 | def from_text(text, origin, rdclass = dns.rdataclass.IN, relativize = True, | |
718 | zone_factory=Zone, filename=None, allow_include=False): | |
727 | if self.check_origin: | |
728 | self.zone.check_origin() | |
729 | ||
730 | def from_text(text, origin = None, rdclass = dns.rdataclass.IN, | |
731 | relativize = True, zone_factory=Zone, filename=None, | |
732 | allow_include=False, check_origin=True): | |
719 | 733 | """Build a zone object from a master file format string. |
720 | 734 | |
721 | 735 | @param text: the master file format input |
722 | 736 | @type text: string. |
723 | @param origin: The origin of the zone. | |
737 | @param origin: The origin of the zone; if not specified, the first | |
738 | $ORIGIN statement in the master file will determine the origin of the | |
739 | zone. | |
724 | 740 | @type origin: dns.name.Name object or string |
725 | 741 | @param rdclass: The zone's rdata class; the default is class IN. |
726 | 742 | @type rdclass: int |
730 | 746 | @type zone_factory: function returning a Zone |
731 | 747 | @param filename: The filename to emit when describing where an error |
732 | 748 | occurred; the default is '<string>'. |
749 | @type filename: string | |
733 | 750 | @param allow_include: is $INCLUDE allowed? |
734 | 751 | @type allow_include: bool |
735 | @type filename: string | |
752 | @param check_origin: should sanity checks of the origin node be done? | |
753 | The default is True. | |
754 | @type check_origin: bool | |
736 | 755 | @raises dns.zone.NoSOA: No SOA RR was found at the zone origin |
737 | 756 | @raises dns.zone.NoNS: No NS RRset was found at the zone origin |
738 | 757 | @rtype: dns.zone.Zone object |
746 | 765 | filename = '<string>' |
747 | 766 | tok = dns.tokenizer.Tokenizer(text, filename) |
748 | 767 | reader = _MasterReader(tok, origin, rdclass, relativize, zone_factory, |
749 | allow_include=allow_include) | |
768 | allow_include=allow_include, | |
769 | check_origin=check_origin) | |
750 | 770 | reader.read() |
751 | 771 | return reader.zone |
752 | 772 | |
753 | def from_file(f, origin, rdclass = dns.rdataclass.IN, relativize = True, | |
754 | zone_factory=Zone, filename=None, allow_include=True): | |
773 | def from_file(f, origin = None, rdclass = dns.rdataclass.IN, | |
774 | relativize = True, zone_factory=Zone, filename=None, | |
775 | allow_include=True, check_origin=True): | |
755 | 776 | """Read a master file and build a zone object. |
756 | 777 | |
757 | 778 | @param f: file or string. If I{f} is a string, it is treated |
758 | 779 | as the name of a file to open. |
759 | @param origin: The origin of the zone. | |
780 | @param origin: The origin of the zone; if not specified, the first | |
781 | $ORIGIN statement in the master file will determine the origin of the | |
782 | zone. | |
760 | 783 | @type origin: dns.name.Name object or string |
761 | 784 | @param rdclass: The zone's rdata class; the default is class IN. |
762 | 785 | @type rdclass: int |
770 | 793 | @type filename: string |
771 | 794 | @param allow_include: is $INCLUDE allowed? |
772 | 795 | @type allow_include: bool |
796 | @param check_origin: should sanity checks of the origin node be done? | |
797 | The default is True. | |
798 | @type check_origin: bool | |
773 | 799 | @raises dns.zone.NoSOA: No SOA RR was found at the zone origin |
774 | 800 | @raises dns.zone.NoNS: No NS RRset was found at the zone origin |
775 | 801 | @rtype: dns.zone.Zone object |
794 | 820 | |
795 | 821 | try: |
796 | 822 | z = from_text(f, origin, rdclass, relativize, zone_factory, |
797 | filename, allow_include) | |
823 | filename, allow_include, check_origin) | |
798 | 824 | finally: |
799 | 825 | if want_close: |
800 | 826 | f.close() |
0 | #!/usr/bin/env python | |
1 | ||
2 | import dns.e164 | |
3 | n = dns.e164.from_e164("+1 555 1212") | |
4 | print n | |
5 | print dns.e164.to_e164(n) |
0 | #!/usr/bin/env python | |
1 | ||
2 | import dns.reversename | |
3 | n = dns.reversename.from_address("127.0.0.1") | |
4 | print n | |
5 | print dns.reversename.to_address(n) |
0 | 0 | #!/usr/bin/env python |
1 | 1 | # |
2 | # Copyright (C) 2003-2005 Nominum, Inc. | |
2 | # Copyright (C) 2003-2006 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, |
14 | 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
15 | 15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | 16 | |
17 | import sys | |
17 | 18 | from distutils.core import setup |
18 | 19 | |
19 | setup( | |
20 | name = "dnspython", | |
21 | version = "1.3.5", | |
22 | description = "DNS toolkit", | |
23 | long_description = \ | |
20 | version = '1.5.0' | |
21 | ||
22 | kwargs = { | |
23 | 'name' : 'dnspython', | |
24 | 'version' : version, | |
25 | 'description' : 'DNS toolkit', | |
26 | 'long_description' : \ | |
24 | 27 | """dnspython is a DNS toolkit for Python. It supports almost all |
25 | 28 | record types. It can be used for queries, zone transfers, and dynamic |
26 | 29 | updates. It supports TSIG authenticated messages and EDNS0. |
29 | 32 | level classes perform queries for data of a given name, type, and |
30 | 33 | class, and return an answer set. The low level classes allow |
31 | 34 | direct manipulation of DNS zones, messages, names, and records.""", |
35 | 'author' : 'Bob Halley', | |
36 | 'author_email' : 'halley@dnspython.org', | |
37 | 'license' : 'BSD-like', | |
38 | 'url' : 'http://www.dnspython.org', | |
39 | 'packages' : ['dns', 'dns.rdtypes', 'dns.rdtypes.IN', 'dns.rdtypes.ANY'], | |
40 | } | |
32 | 41 | |
33 | author = "Bob Halley", | |
34 | author_email = "halley@dnspython.org", | |
35 | license = "BSD-like", | |
36 | url = "http://www.dnspython.org", | |
37 | packages = ['dns', 'dns.rdtypes', 'dns.rdtypes.IN', 'dns.rdtypes.ANY'] | |
38 | ) | |
42 | if sys.hexversion >= 0x02020300: | |
43 | kwargs['download_url'] = \ | |
44 | 'http://www.dnspython.org/kits/dnspython-%s.tar.gz' % version | |
45 | kwargs['classifiers'] = [ | |
46 | "Development Status :: 5 - Production/Stable", | |
47 | "Intended Audience :: Developers", | |
48 | "Intended Audience :: System Administrators", | |
49 | "License :: Freeware", | |
50 | "Operating System :: Microsoft :: Windows :: Windows 95/98/2000", | |
51 | "Operating System :: POSIX", | |
52 | "Programming Language :: Python", | |
53 | "Topic :: Internet :: Name Service (DNS)", | |
54 | "Topic :: Software Development :: Libraries :: Python Modules", | |
55 | ] | |
56 | ||
57 | if sys.hexversion >= 0x02050000: | |
58 | kwargs['requires'] = [] | |
59 | kwargs['provides'] = ['dns'] | |
60 | ||
61 | setup(**kwargs) |
0 | # Copyright (C) 2005 Nominum, Inc. | |
1 | # | |
2 | # Permission to use, copy, modify, and distribute this software and its | |
3 | # documentation for any purpose with or without fee is hereby granted, | |
4 | # provided that the above copyright notice and this permission notice | |
5 | # appear in all copies. | |
6 | # | |
7 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES | |
8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR | |
10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
13 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | ||
15 | import unittest | |
16 | ||
17 | import dns.rdata | |
18 | import dns.rdataclass | |
19 | import dns.rdatatype | |
20 | import dns.ttl | |
21 | ||
22 | class BugsTestCase(unittest.TestCase): | |
23 | ||
24 | def test_float_LOC(self): | |
25 | rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.LOC, | |
26 | "30 30 0.000 N 100 30 0.000 W 10.00m 20m 2000m 20m") | |
27 | self.failUnless(rdata.float_latitude == 30.5) | |
28 | self.failUnless(rdata.float_longitude == -100.5) | |
29 | ||
30 | def test_SOA_BIND8_TTL(self): | |
31 | rdata1 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, | |
32 | "a b 100 1s 1m 1h 1d") | |
33 | rdata2 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, | |
34 | "a b 100 1 60 3600 86400") | |
35 | self.failUnless(rdata1 == rdata2) | |
36 | ||
37 | def test_TTL_bounds_check(self): | |
38 | def bad(): | |
39 | ttl = dns.ttl.from_text("2147483648") | |
40 | self.failUnlessRaises(dns.ttl.BadTTL, bad) | |
41 | ||
42 | if __name__ == '__main__': | |
43 | unittest.main() |
68 | 68 | $TTL 300 ; 5 minutes |
69 | 69 | d A 73.80.65.49 |
70 | 70 | $TTL 3600 ; 1 hour |
71 | dhcid01 DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69l | |
72 | OjxfNuVAA2kjEA= ) | |
73 | dhcid02 DHCID ( AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQdW | |
74 | L3b/NaiUDlW2No= ) | |
75 | dhcid03 DHCID ( AAABxLmlskllE0MVjd57zHcWmEH3pCQ6V | |
76 | ytcKD//7es/deY= ) | |
71 | 77 | dname01 DNAME dname-target. |
72 | 78 | dname02 DNAME dname-target |
73 | 79 | dname03 DNAME . |
204 | 210 | ; |
205 | 211 | unknown3 A \# 4 7f000002 |
206 | 212 | sshfp1 SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab |
213 | spf SPF "v=spf1 mx -all" | |
214 | ipseckey01 IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== | |
215 | ipseckey02 IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== | |
216 | ipseckey03 IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== | |
217 | ipseckey04 IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== | |
218 | ipseckey05 IPSECKEY 10 3 2 mygateway2 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== |
18 | 18 | cname02 3600 IN CNAME cname-target |
19 | 19 | cname03 3600 IN CNAME . |
20 | 20 | d 300 IN A 73.80.65.49 |
21 | dhcid01 3600 IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69 lOjxfNuVAA2kjEA= | |
22 | dhcid02 3600 IN DHCID AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQd WL3b/NaiUDlW2No= | |
23 | dhcid03 3600 IN DHCID AAABxLmlskllE0MVjd57zHcWmEH3pCQ6 VytcKD//7es/deY= | |
21 | 24 | dname01 3600 IN DNAME dname-target. |
22 | 25 | dname02 3600 IN DNAME dname-target |
23 | 26 | dname03 3600 IN DNAME . |
36 | 39 | gpos01 3600 IN GPOS -22.6882 116.8652 250.0 |
37 | 40 | hinfo01 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" |
38 | 41 | hinfo02 3600 IN HINFO "PC" "NetBSD" |
42 | ipseckey01 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== | |
43 | ipseckey02 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== | |
44 | ipseckey03 3600 IN IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== | |
45 | ipseckey04 3600 IN IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== | |
46 | ipseckey05 3600 IN IPSECKEY 10 3 2 mygateway2 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== | |
39 | 47 | isdn01 3600 IN ISDN "isdn-address" |
40 | 48 | isdn02 3600 IN ISDN "isdn-address" "subaddress" |
41 | 49 | isdn03 3600 IN ISDN "isdn-address" |
77 | 85 | s 300 IN NS ns.s |
78 | 86 | ns.s 300 IN A 73.80.65.49 |
79 | 87 | sig01 3600 IN SIG NXT 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= |
88 | spf 3600 IN SPF "v=spf1 mx -all" | |
80 | 89 | srv01 3600 IN SRV 0 0 0 . |
81 | 90 | srv02 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. |
82 | 91 | sshfp1 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab |
18 | 18 | cname02.example. 3600 IN CNAME cname-target.example. |
19 | 19 | cname03.example. 3600 IN CNAME . |
20 | 20 | d.example. 300 IN A 73.80.65.49 |
21 | dhcid01.example. 3600 IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69 lOjxfNuVAA2kjEA= | |
22 | dhcid02.example. 3600 IN DHCID AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQd WL3b/NaiUDlW2No= | |
23 | dhcid03.example. 3600 IN DHCID AAABxLmlskllE0MVjd57zHcWmEH3pCQ6 VytcKD//7es/deY= | |
21 | 24 | dname01.example. 3600 IN DNAME dname-target. |
22 | 25 | dname02.example. 3600 IN DNAME dname-target.example. |
23 | 26 | dname03.example. 3600 IN DNAME . |
36 | 39 | gpos01.example. 3600 IN GPOS -22.6882 116.8652 250.0 |
37 | 40 | hinfo01.example. 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" |
38 | 41 | hinfo02.example. 3600 IN HINFO "PC" "NetBSD" |
42 | ipseckey01.example. 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== | |
43 | ipseckey02.example. 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== | |
44 | ipseckey03.example. 3600 IN IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== | |
45 | ipseckey04.example. 3600 IN IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== | |
46 | ipseckey05.example. 3600 IN IPSECKEY 10 3 2 mygateway2.example. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== | |
39 | 47 | isdn01.example. 3600 IN ISDN "isdn-address" |
40 | 48 | isdn02.example. 3600 IN ISDN "isdn-address" "subaddress" |
41 | 49 | isdn03.example. 3600 IN ISDN "isdn-address" |
77 | 85 | s.example. 300 IN NS ns.s.example. |
78 | 86 | ns.s.example. 300 IN A 73.80.65.49 |
79 | 87 | sig01.example. 3600 IN SIG NXT 1 3 3600 20200101000000 20030101000000 2143 foo.example. MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= |
88 | spf.example. 3600 IN SPF "v=spf1 mx -all" | |
80 | 89 | srv01.example. 3600 IN SRV 0 0 0 . |
81 | 90 | srv02.example. 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. |
82 | 91 | sshfp1.example. 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab |
15 | 15 | import unittest |
16 | 16 | |
17 | 17 | import cStringIO |
18 | import socket | |
18 | 19 | |
19 | 20 | import dns.name |
21 | import dns.reversename | |
22 | import dns.e164 | |
20 | 23 | |
21 | 24 | class NameTestCase(unittest.TestCase): |
22 | 25 | def setUp(self): |
610 | 613 | n.parent() |
611 | 614 | self.failUnlessRaises(dns.name.NoParent, bad) |
612 | 615 | |
616 | def testFromUnicode1(self): | |
617 | n = dns.name.from_text(u'foo.bar') | |
618 | self.failUnless(n.labels == ('foo', 'bar', '')) | |
619 | ||
620 | def testFromUnicode2(self): | |
621 | n = dns.name.from_text(u'foo\u1234bar.bar') | |
622 | self.failUnless(n.labels == ('xn--foobar-r5z', 'bar', '')) | |
623 | ||
624 | def testFromUnicodeAlternateDot1(self): | |
625 | n = dns.name.from_text(u'foo\u3002bar') | |
626 | self.failUnless(n.labels == ('foo', 'bar', '')) | |
627 | ||
628 | def testFromUnicodeAlternateDot2(self): | |
629 | n = dns.name.from_text(u'foo\uff0ebar') | |
630 | self.failUnless(n.labels == ('foo', 'bar', '')) | |
631 | ||
632 | def testFromUnicodeAlternateDot3(self): | |
633 | n = dns.name.from_text(u'foo\uff61bar') | |
634 | self.failUnless(n.labels == ('foo', 'bar', '')) | |
635 | ||
636 | def testToUnicode1(self): | |
637 | n = dns.name.from_text(u'foo.bar') | |
638 | s = n.to_unicode() | |
639 | self.failUnless(s == u'foo.bar.') | |
640 | ||
641 | def testToUnicode2(self): | |
642 | n = dns.name.from_text(u'foo\u1234bar.bar') | |
643 | s = n.to_unicode() | |
644 | self.failUnless(s == u'foo\u1234bar.bar.') | |
645 | ||
646 | def testToUnicode3(self): | |
647 | n = dns.name.from_text('foo.bar') | |
648 | s = n.to_unicode() | |
649 | self.failUnless(s == u'foo.bar.') | |
650 | ||
651 | def testReverseIPv4(self): | |
652 | e = dns.name.from_text('1.0.0.127.in-addr.arpa.') | |
653 | n = dns.reversename.from_address('127.0.0.1') | |
654 | self.failUnless(e == n) | |
655 | ||
656 | def testReverseIPv6(self): | |
657 | e = dns.name.from_text('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.') | |
658 | n = dns.reversename.from_address('::1') | |
659 | self.failUnless(e == n) | |
660 | ||
661 | def testBadReverseIPv4(self): | |
662 | def bad(): | |
663 | n = dns.reversename.from_address('127.0.foo.1') | |
664 | self.failUnlessRaises(socket.error, bad) | |
665 | ||
666 | def testBadReverseIPv6(self): | |
667 | def bad(): | |
668 | n = dns.reversename.from_address('::1::1') | |
669 | self.failUnlessRaises(socket.error, bad) | |
670 | ||
671 | def testForwardIPv4(self): | |
672 | n = dns.name.from_text('1.0.0.127.in-addr.arpa.') | |
673 | e = '127.0.0.1' | |
674 | text = dns.reversename.to_address(n) | |
675 | self.failUnless(text == e) | |
676 | ||
677 | def testForwardIPv6(self): | |
678 | n = dns.name.from_text('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.') | |
679 | e = '::1' | |
680 | text = dns.reversename.to_address(n) | |
681 | self.failUnless(text == e) | |
682 | ||
683 | def testE164ToEnum(self): | |
684 | text = '+1 650 555 1212' | |
685 | e = dns.name.from_text('2.1.2.1.5.5.5.0.5.6.1.e164.arpa.') | |
686 | n = dns.e164.from_e164(text) | |
687 | self.failUnless(n == e) | |
688 | ||
689 | def testEnumToE164(self): | |
690 | n = dns.name.from_text('2.1.2.1.5.5.5.0.5.6.1.e164.arpa.') | |
691 | e = '+16505551212' | |
692 | text = dns.e164.to_e164(n) | |
693 | self.failUnless(text == e) | |
694 | ||
613 | 695 | if __name__ == '__main__': |
614 | 696 | unittest.main() |