Codebase list dnspython / 0f5e57b
Imported Upstream version 1.5.0 SVN-Git Migration 8 years ago
37 changed file(s) with 1320 addition(s) and 203 deletion(s). Raw diff Collapse all Expand all
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
0146 2005-10-31 Bob Halley <halley@dnspython.org>
1147
2148 * (Version 1.3.5 released)
0 Metadata-Version: 1.0
0 Metadata-Version: 1.1
11 Name: dnspython
2 Version: 1.3.5
2 Version: 1.5.0
33 Summary: DNS toolkit
44 Home-page: http://www.dnspython.org
55 Author: Bob Halley
66 Author-email: halley@dnspython.org
77 License: BSD-like
8 Download-URL: http://www.dnspython.org/kits/dnspython-1.5.0.tar.gz
89 Description: dnspython is a DNS toolkit for Python. It supports almost all
910 record types. It can be used for queries, zone transfers, and dynamic
1011 updates. It supports TSIG authenticated messages and EDNS0.
1415 class, and return an answer set. The low level classes allow
1516 direct manipulation of DNS zones, messages, names, and records.
1617 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
2121
2222 ABOUT THIS RELEASE
2323
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].
25109
26110 New since 1.3.4:
27111
37121
38122 will output "dnspython.org." and
39123
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.')
41125
42126 will output ".".
43127
1616
1717 __all__ = [
1818 'dnssec',
19 'e164',
1920 'exception',
2021 'flags',
2122 'inet',
3435 'rdatatype',
3536 'renderer',
3637 'resolver',
38 'reversename',
3739 'rrset',
3840 'set',
3941 '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
7676 @param text: the textual address
7777 @type text: string
7878 @raises ValueError: the address family cannot be determined from the input.
79 @rtype int
79 @rtype: int
8080 """
8181 try:
8282 junk = dns.ipv4.inet_aton(text)
437437 if keyname is None:
438438 self.keyname = self.keyring.keys()[0]
439439 else:
440 if isinstance(keyname, str):
440 if isinstance(keyname, (str, unicode)):
441441 keyname = dns.name.from_text(keyname)
442442 self.keyname = keyname
443443 self.fudge = fudge
448448 self.tsig_error = tsig_error
449449 self.other_data = other_data
450450
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):
452452 """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
456458 @param ednsflags: EDNS flag values.
457459 @type ednsflags: int
458460 @param payload: The EDNS sender's payload field, which is the maximum
459461 size of UDP datagram the sender can handle.
460462 @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
461466 @see: RFC 2671
462467 """
463 if edns is None:
468 if edns is None or edns is False:
464469 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
465478 self.edns = edns
466479 self.ednsflags = ednsflags
467480 self.payload = payload
468481 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
469496
470497 def rcode(self):
471498 """Return the rcode.
936963 f.close()
937964 return m
938965
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):
940968 """Make a query message.
941969
942970 The query name, type, and class may all be specified either
951979 @type rdtype: int
952980 @param rdclass: The desired rdata class; the default is class IN.
953981 @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
954988 @rtype: dns.message.Message object"""
955989
956 if isinstance(qname, str):
990 if isinstance(qname, (str, unicode)):
957991 qname = dns.name.from_text(qname)
958992 if isinstance(rdtype, str):
959993 rdtype = dns.rdatatype.from_text(rdtype)
963997 m.flags |= dns.flags.RD
964998 m.find_rrset(m.question, qname, rdclass, rdtype, create=True,
965999 force_unique=True)
1000 m.use_edns(use_edns)
1001 m.want_dnssec(want_dnssec)
9661002 return m
9671003
9681004 def make_response(query, recursion_available=False, our_payload=8192):
2121 """
2222
2323 import cStringIO
24 import string
2524 import struct
2625 import sys
26
27 if sys.hexversion >= 0x02030000:
28 import encodings.idna
2729
2830 import dns.exception
2931
320322 l = self.labels[:-1]
321323 else:
322324 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])
324347 return s
325348
326349 def to_digestable(self, origin=None):
507530 root = Name([''])
508531 empty = Name([])
509532
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
510595 def from_text(text, origin = root):
511596 """Convert text into a Name object.
512597 @rtype: dns.name.Name object
513598 """
514599
515600 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"
517605 if not (origin is None or isinstance(origin, Name)):
518606 raise ValueError, "origin must be a Name or None"
519607 labels = []
6464 def _wait_for_writable(s, expiration):
6565 _wait_for([], [s], [s], expiration)
6666
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):
6869 """Return the response obtained after sending a query via UDP.
6970
7071 @param q: the query
8586 @type source: string
8687 @param source_port: The port from which to send the message.
8788 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"""
8993
9094 wire = q.to_wire()
9195 if af is None:
109113 s.bind(source)
110114 _wait_for_writable(s, expiration)
111115 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)
114125 finally:
115126 s.close()
116 if from_address != destination:
117 raise UnexpectedSource
118127 r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac)
119128 if not q.is_response(r):
120129 raise BadResponse
221230
222231 def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN,
223232 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):
225234 """Return a generator for the responses to a zone transfer.
226235
227236 @param where: where to send the message
260269 @type source: string
261270 @param source_port: The port from which to send the message.
262271 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)):
266278 zone = dns.name.from_text(zone)
267279 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)
268284 if not keyring is None:
269285 q.use_tsig(keyring, keyname)
270286 wire = q.to_wire()
290306 tcpmsg = struct.pack("!H", l) + wire
291307 _net_write(s, tcpmsg, expiration)
292308 done = False
293 seen_soa = False
309 soa_rrset = None
310 soa_count = 0
294311 if relativize:
295312 origin = zone
296313 oname = dns.name.empty
311328 multi=True, first=first)
312329 tsig_ctx = r.tsig_ctx
313330 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:
315335 if not r.answer or r.answer[0].name != oname:
316336 raise dns.exception.FormError
317337 rrset = r.answer[0]
318338 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 #
326347 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"
333377 yield r
334378 s.close()
168168 """
169169
170170 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())
171179
172180 def __repr__(self):
173181 covers = self.covers()
7070 APL = 42
7171 DS = 43
7272 SSHFP = 44
73 IPSECKEY = 45
7374 RRSIG = 46
7475 NSEC = 47
7576 DNSKEY = 48
77 DHCID = 49
78 SPF = 99
7679 UNSPEC = 103
7780 TKEY = 249
7881 TSIG = 250
124127 'APL' : APL,
125128 'DS' : DS,
126129 'SSHFP' : SSHFP,
130 'IPSECKEY' : IPSECKEY,
127131 'RRSIG' : RRSIG,
128132 'NSEC' : NSEC,
129133 'DNSKEY' : DNSKEY,
134 'DHCID' : DHCID,
135 'SPF' : SPF,
130136 'UNSPEC' : UNSPEC,
131137 'TKEY' : TKEY,
132138 'TSIG' : TSIG,
5757 value += float(what[1]) / 60.0
5858 value += float(what[2]) / 3600.0
5959 value += float(what[3]) / 3600000.0
60 return value
60 return sign * value
6161
6262 def _encode_size(what, desc):
6363 what = long(what);
3131 @type refresh: int
3232 @ivar retry: The zone's retry value (in seconds)
3333 @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
3636 @ivar minimum: The zone's negative caching time (in seconds, called
3737 "minimum" for historical reasons)
3838 @type minimum: int
6565 mname = mname.choose_relativity(origin, relativize)
6666 rname = rname.choose_relativity(origin, relativize)
6767 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()
7272 tok.get_eol()
7373 return cls(rdclass, rdtype, mname, rname, serial, refresh, retry,
7474 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.
11 #
22 # Permission to use, copy, modify, and distribute this software and its
33 # documentation for any purpose with or without fee is hereby granted,
1212 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
1313 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1414
15 import dns.exception
16 import dns.rdata
17 import dns.tokenizer
15 import dns.rdtypes.txtbase
1816
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
1919 'CERT',
2020 'CNAME',
2121 'DNAME',
22 'DNSKEY',
2223 'DS',
2324 'GPOS',
2425 'HINFO',
2728 'LOC',
2829 'MX',
2930 'NS',
31 'NSEC',
3032 'NXT',
3133 'PTR',
3234 'RP',
35 'RRSIG',
3336 'RT',
3437 'SIG',
3538 'SOA',
39 'SPF',
40 'SSHFP',
3641 'TXT',
3742 'X25',
38 'RRSIG',
39 'NSEC',
40 'DNSKEY',
41 'SSHFP',
4243 ]
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)
1818 'A',
1919 'AAAA',
2020 'APL',
21 'DHCID',
2122 'KX',
22 'PX',
2323 'NAPTR',
2424 'NSAP',
2525 'NSAP_PTR',
26 'PX',
2627 'SRV',
2728 'WKS',
2829 ]
100100 type_covered = dns.rdatatype.from_text(tok.get_string())
101101 algorithm = dns.dnssec.algorithm_from_text(tok.get_string())
102102 labels = tok.get_int()
103 original_ttl = tok.get_uint32()
103 original_ttl = tok.get_ttl()
104104 expiration = sigtime_to_posixtime(tok.get_string())
105105 inception = sigtime_to_posixtime(tok.get_string())
106106 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)
6767 Instances of this class bundle up the result of a successful DNS
6868 resolution.
6969
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]".
7275
7376 Note that CNAMEs or DNAMEs in the response may mean that answer
7477 node's name might not be the query name.
141144 def __iter__(self):
142145 return iter(self.rrset)
143146
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
144159 class Cache(object):
145160 """Simple DNS answer cache.
146161
223238 else:
224239 self.data = {}
225240 self.next_cleaning = time.time() + self.cleaning_interval
226
241
227242 class Resolver(object):
228243 """DNS stub resolver
229244
301316 a string, it is used as the name of the file to open; otherwise it
302317 is treated as the file itself."""
303318 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
305326 want_close = True
306327 else:
307328 want_close = False
325346 if len(self.nameservers) == 0:
326347 self.nameservers.append('127.0.0.1')
327348
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):
329365 """Configure a NameServer registry entry."""
330366 # 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)
332370 for ns in ns_list:
333371 if not ns in self.nameservers:
334372 self.nameservers.append(ns)
341379 def _config_win32_search(self, search):
342380 """Configure a Search registry entry."""
343381 # 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)
345385 for s in search_list:
346386 if not s in self.search:
347387 self.search.append(dns.name.from_text(s))
366406 except WindowsError:
367407 servers = None
368408 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)
372410 try:
373411 dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain')
374412 if dom:
380418 except WindowsError:
381419 search = None
382420 if search:
383 self._config_win32_search(servers)
421 self._config_win32_search(search)
384422
385423 def read_registry(self):
386424 """Extract resolver configuration from the Windows registry."""
473511 @raises NoNameservers: no non-broken nameservers are available to
474512 answer the question."""
475513
476 if isinstance(qname, str):
514 if isinstance(qname, (str, unicode)):
477515 qname = dns.name.from_text(qname, None)
478516 if isinstance(rdtype, str):
479517 rdtype = dns.rdatatype.from_text(rdtype)
510548 while response is None:
511549 if len(nameservers) == 0:
512550 raise NoNameservers
513 for nameserver in nameservers:
551 for nameserver in nameservers[:]:
514552 timeout = self._compute_timeout(start)
515553 try:
516554 if tcp:
553591 if rcode != dns.rcode.SERVFAIL:
554592 nameservers.remove(nameserver)
555593 response = None
594 if not response is None:
595 break
556596 #
557597 # All nameservers failed!
558598 #
641681 @type resolver: dns.resolver.Resolver object or None
642682 @rtype: dns.name.Name"""
643683
644 if isinstance(name, str):
684 if isinstance(name, (str, unicode)):
645685 name = dns.name.from_text(name, dns.name.root)
646686 if resolver is None:
647687 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'
106106 return super(RRset, self).to_wire(self.name, file, compress, origin,
107107 self.deleting, **kw)
108108
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
109116
110117 def from_text_list(name, ttl, rdclass, rdtype, text_rdatas):
111118 """Create an RRset with the specified name, TTL, class, and type, and with
114121 @rtype: dns.rrset.RRset object
115122 """
116123
117 if isinstance(name, str):
124 if isinstance(name, (str, unicode)):
118125 name = dns.name.from_text(name, None)
119126 if isinstance(rdclass, str):
120127 rdclass = dns.rdataclass.from_text(rdclass)
143150 @rtype: dns.rrset.RRset object
144151 """
145152
146 if isinstance(name, str):
153 if isinstance(name, (str, unicode)):
147154 name = dns.name.from_text(name, None)
148155
149156 if len(rdatas) == 0:
1919
2020 import dns.exception
2121 import dns.name
22 import dns.ttl
2223
2324 _DELIMITERS = {
2425 ' ' : True,
419420 raise dns.exception.SyntaxError, \
420421 'expected EOL or EOF, got %d "%s"' % (ttype, t)
421422 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)
3131 """
3232
3333 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)
5544 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)"
6063 return total
4343 """
4444 super(Update, self).__init__()
4545 self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE)
46 if isinstance(zone, str):
46 if isinstance(zone, (str, unicode)):
4747 zone = dns.name.from_text(zone)
48 else:
49 zone = zone.copy()
5048 self.origin = zone
5149 if isinstance(rdclass, str):
5250 rdclass = dns.rdataclass.from_text(rdclass)
7977
8078 - ttl, rdtype, string..."""
8179
82 if isinstance(name, str):
80 if isinstance(name, (str, unicode)):
8381 name = dns.name.from_text(name, None)
8482 if isinstance(args[0], dns.rdataset.Rdataset):
8583 for rds in args:
129127
130128 - rdtype, [string...]"""
131129
132 if isinstance(name, str):
130 if isinstance(name, (str, unicode)):
133131 name = dns.name.from_text(name, None)
134132 if len(args) == 0:
135133 rrset = self.find_rrset(self.authority, name, dns.rdataclass.ANY,
187185
188186 - rdtype, string..."""
189187
190 if isinstance(name, str):
188 if isinstance(name, (str, unicode)):
191189 name = dns.name.from_text(name, None)
192190 if len(args) == 0:
193191 rrset = self.find_rrset(self.answer, name,
215213 """Require that an owner name (and optionally an rdata type) does
216214 not exist as a prerequisite to the execution of the update."""
217215
218 if isinstance(name, str):
216 if isinstance(name, (str, unicode)):
219217 name = dns.name.from_text(name, None)
220218 if rdtype is None:
221219 rrset = self.find_rrset(self.answer, name,
1515 """dnspython release version information."""
1616
1717 MAJOR = 1
18 MINOR = 3
19 MICRO = 5
18 MINOR = 5
19 MICRO = 0
2020 RELEASELEVEL = 0x0f
2121 SERIAL = 0
2222
3737
3838 class NoNS(BadZone):
3939 """The zone has no NS RRset at its origin."""
40 pass
41
42 class UnknownOrigin(BadZone):
43 """The zone's origin is unknown."""
4044 pass
4145
4246 class Zone(object):
101105 return not self.__eq__(other)
102106
103107 def _validate_name(self, name):
104 if isinstance(name, str):
108 if isinstance(name, (str, unicode)):
105109 name = dns.name.from_text(name, None)
106110 elif not isinstance(name, dns.name.Name):
107111 raise KeyError, \
162166 @param create: should the node be created if it doesn't exist?
163167 @type create: bool
164168 @raises KeyError: the name is not known and create was not specified.
165 @rtype dns.node.Node object
169 @rtype: dns.node.Node object
166170 """
167171
168172 name = self._validate_name(name)
185189 @type name: dns.name.Name object or string
186190 @param create: should the node be created if it doesn't exist?
187191 @type create: bool
188 @rtype dns.node.Node object or None
192 @rtype: dns.node.Node object or None
189193 """
190194
191195 try:
538542 (None if no $INCLUDE is active).
539543 @ivar allow_include: is $INCLUDE allowed?
540544 @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
541548 """
542549
543550 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)):
546553 origin = dns.name.from_text(origin)
547554 self.tok = tok
548555 self.current_origin = origin
553560 self.saved_state = []
554561 self.current_file = None
555562 self.allow_include = allow_include
563 self.check_origin = check_origin
556564
557565 def _eat_line(self):
558566 while 1:
563571 def _rr_line(self):
564572 """Process one line from a DNS master file."""
565573 # Name
574 if self.current_origin is None:
575 raise UnknownOrigin
566576 token = self.tok.get(want_leading = True)
567577 if token[0] != dns.tokenizer.WHITESPACE:
568578 self.last_name = dns.name.from_text(token[1], self.current_origin)
672682 elif u == '$ORIGIN':
673683 self.current_origin = self.tok.get_name()
674684 self.tok.get_eol()
685 if self.zone.origin is None:
686 self.zone.origin = self.current_origin
675687 elif u == '$INCLUDE' and self.allow_include:
676688 token = self.tok.get()
677689 if token[0] != dns.tokenizer.QUOTED_STRING:
712724 "%s:%d: %s" % (filename, line_number, detail)
713725
714726 # 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):
719733 """Build a zone object from a master file format string.
720734
721735 @param text: the master file format input
722736 @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.
724740 @type origin: dns.name.Name object or string
725741 @param rdclass: The zone's rdata class; the default is class IN.
726742 @type rdclass: int
730746 @type zone_factory: function returning a Zone
731747 @param filename: The filename to emit when describing where an error
732748 occurred; the default is '<string>'.
749 @type filename: string
733750 @param allow_include: is $INCLUDE allowed?
734751 @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
736755 @raises dns.zone.NoSOA: No SOA RR was found at the zone origin
737756 @raises dns.zone.NoNS: No NS RRset was found at the zone origin
738757 @rtype: dns.zone.Zone object
746765 filename = '<string>'
747766 tok = dns.tokenizer.Tokenizer(text, filename)
748767 reader = _MasterReader(tok, origin, rdclass, relativize, zone_factory,
749 allow_include=allow_include)
768 allow_include=allow_include,
769 check_origin=check_origin)
750770 reader.read()
751771 return reader.zone
752772
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):
755776 """Read a master file and build a zone object.
756777
757778 @param f: file or string. If I{f} is a string, it is treated
758779 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.
760783 @type origin: dns.name.Name object or string
761784 @param rdclass: The zone's rdata class; the default is class IN.
762785 @type rdclass: int
770793 @type filename: string
771794 @param allow_include: is $INCLUDE allowed?
772795 @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
773799 @raises dns.zone.NoSOA: No SOA RR was found at the zone origin
774800 @raises dns.zone.NoNS: No NS RRset was found at the zone origin
775801 @rtype: dns.zone.Zone object
794820
795821 try:
796822 z = from_text(f, origin, rdclass, relativize, zone_factory,
797 filename, allow_include)
823 filename, allow_include, check_origin)
798824 finally:
799825 if want_close:
800826 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)
00 #!/usr/bin/env python
11 #
2 # Copyright (C) 2003-2005 Nominum, Inc.
2 # Copyright (C) 2003-2006 Nominum, Inc.
33 #
44 # Permission to use, copy, modify, and distribute this software and its
55 # documentation for any purpose with or without fee is hereby granted,
1414 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
1515 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1616
17 import sys
1718 from distutils.core import setup
1819
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' : \
2427 """dnspython is a DNS toolkit for Python. It supports almost all
2528 record types. It can be used for queries, zone transfers, and dynamic
2629 updates. It supports TSIG authenticated messages and EDNS0.
2932 level classes perform queries for data of a given name, type, and
3033 class, and return an answer set. The low level classes allow
3134 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 }
3241
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()
6868 $TTL 300 ; 5 minutes
6969 d A 73.80.65.49
7070 $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= )
7177 dname01 DNAME dname-target.
7278 dname02 DNAME dname-target
7379 dname03 DNAME .
204210 ;
205211 unknown3 A \# 4 7f000002
206212 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==
1818 cname02 3600 IN CNAME cname-target
1919 cname03 3600 IN CNAME .
2020 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=
2124 dname01 3600 IN DNAME dname-target.
2225 dname02 3600 IN DNAME dname-target
2326 dname03 3600 IN DNAME .
3639 gpos01 3600 IN GPOS -22.6882 116.8652 250.0
3740 hinfo01 3600 IN HINFO "Generic PC clone" "NetBSD-1.4"
3841 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==
3947 isdn01 3600 IN ISDN "isdn-address"
4048 isdn02 3600 IN ISDN "isdn-address" "subaddress"
4149 isdn03 3600 IN ISDN "isdn-address"
7785 s 300 IN NS ns.s
7886 ns.s 300 IN A 73.80.65.49
7987 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"
8089 srv01 3600 IN SRV 0 0 0 .
8190 srv02 3600 IN SRV 65535 65535 65535 old-slow-box.example.com.
8291 sshfp1 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab
1818 cname02.example. 3600 IN CNAME cname-target.example.
1919 cname03.example. 3600 IN CNAME .
2020 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=
2124 dname01.example. 3600 IN DNAME dname-target.
2225 dname02.example. 3600 IN DNAME dname-target.example.
2326 dname03.example. 3600 IN DNAME .
3639 gpos01.example. 3600 IN GPOS -22.6882 116.8652 250.0
3740 hinfo01.example. 3600 IN HINFO "Generic PC clone" "NetBSD-1.4"
3841 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==
3947 isdn01.example. 3600 IN ISDN "isdn-address"
4048 isdn02.example. 3600 IN ISDN "isdn-address" "subaddress"
4149 isdn03.example. 3600 IN ISDN "isdn-address"
7785 s.example. 300 IN NS ns.s.example.
7886 ns.s.example. 300 IN A 73.80.65.49
7987 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"
8089 srv01.example. 3600 IN SRV 0 0 0 .
8190 srv02.example. 3600 IN SRV 65535 65535 65535 old-slow-box.example.com.
8291 sshfp1.example. 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab
1515 import unittest
1616
1717 import cStringIO
18 import socket
1819
1920 import dns.name
21 import dns.reversename
22 import dns.e164
2023
2124 class NameTestCase(unittest.TestCase):
2225 def setUp(self):
610613 n.parent()
611614 self.failUnlessRaises(dns.name.NoParent, bad)
612615
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
613695 if __name__ == '__main__':
614696 unittest.main()