Imported Upstream version 1.11.0
SVN-Git Migration
8 years ago
0 | 2013-07-01 Bob Halley <halley@dnspython.org> | |
1 | ||
2 | * (Version 1.11.0 released) | |
3 | ||
4 | 2013-04-28 Bob Halley <halley@dnspython.org> | |
5 | ||
6 | * dns/name.py (Name.to_wire): Do not add items with offsets >= 2^14 | |
7 | to the compression table. Thanks to Casey Deccio for discovering | |
8 | this bug. | |
9 | ||
10 | 2013-04-26 Bob Halley <halley@dnspython.org> | |
11 | ||
12 | * dns/ipv6.py (inet_ntoa): We now comply with RFC 5952 section | |
13 | 5.2.2, by *not* using the :: syntax to shorten just one 16-bit | |
14 | field. Thanks to David Waitzman for reporting the bug and | |
15 | suggesting the fix. | |
16 | ||
17 | 2013-03-31 Bob Halley <halley@dnspython.org> | |
18 | ||
19 | * lock caches in case they are shared | |
20 | ||
21 | * raise YXDOMAIN if we see one | |
22 | ||
23 | * do not print empty rdatasets | |
24 | ||
25 | * Add contributed $GENERATE support (thanks uberj) | |
26 | ||
27 | * Remove DNSKEY keytag uniqueness assumption (RFC 4034, section 8) | |
28 | (thanks James Dempsey) | |
29 | ||
30 | 2012-09-25 Sean Leach | |
31 | ||
32 | * added set_flags() method to dns.resolver.Resolver | |
33 | ||
34 | 2012-09-25 Pieter Lexis | |
35 | ||
36 | * added support for TLSA RR | |
37 | ||
38 | 2012-08-28 Bob Halley <halley@dnspython.org> | |
39 | ||
40 | * dns/rdtypes/ANY/NSEC3.py (NSEC3.from_text): The NSEC3 from_text() | |
41 | method could erroneously emit empty bitmap windows (i.e. windows | |
42 | with a count of 0 bytes); such bitmaps are illegal. | |
43 | ||
0 | 44 | 2012-04-08 Bob Halley <halley@dnspython.org> |
1 | 45 | |
2 | 46 | * (Version 1.10.0 released) |
0 | 0 | Metadata-Version: 1.1 |
1 | 1 | Name: dnspython |
2 | Version: 1.10.0 | |
2 | Version: 1.11.0 | |
3 | 3 | Summary: DNS toolkit |
4 | 4 | Home-page: http://www.dnspython.org |
5 | 5 | Author: Bob Halley |
6 | 6 | Author-email: halley@dnspython.org |
7 | 7 | License: BSD-like |
8 | Download-URL: http://www.dnspython.org/kits/1.10.0/dnspython-1.10.0.tar.gz | |
8 | Download-URL: http://www.dnspython.org/kits/1.11.0/dnspython-1.11.0.tar.gz | |
9 | 9 | Description: dnspython is a DNS toolkit for Python. It supports almost all |
10 | 10 | record types. It can be used for queries, zone transfers, and dynamic |
11 | 11 | updates. It supports TSIG authenticated messages and EDNS0. |
21 | 21 | |
22 | 22 | ABOUT THIS RELEASE |
23 | 23 | |
24 | This is dnspython 1.10.0 | |
24 | This is dnspython 1.11.0 | |
25 | ||
26 | New since 1.10.0: | |
27 | ||
28 | $GENERATE support | |
29 | ||
30 | TLSA RR support | |
31 | ||
32 | Added set_flags() method to dns.resolver.Resolver | |
33 | ||
34 | Bugs fixed since 1.10.0: | |
35 | ||
36 | Names with offsets >= 2^14 are no longer added to the compression | |
37 | table. | |
38 | ||
39 | The "::" syntax is not used to shorten a single 16-bit section of | |
40 | the text form an IPv6 address. | |
41 | ||
42 | Caches are now locked. | |
43 | ||
44 | YXDOMAIN is raised if seen by the resolver. | |
45 | ||
46 | Empty rdatasets are not printed. | |
47 | ||
48 | DNSKEY key tags are no longer assumed to be unique. | |
25 | 49 | |
26 | 50 | New since 1.9.4: |
27 | 51 |
125 | 125 | return dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.DS, dsrdata, 0, |
126 | 126 | len(dsrdata)) |
127 | 127 | |
128 | def _find_key(keys, rrsig): | |
128 | def _find_candidate_keys(keys, rrsig): | |
129 | candidate_keys=[] | |
129 | 130 | value = keys.get(rrsig.signer) |
130 | 131 | if value is None: |
131 | 132 | return None |
140 | 141 | for rdata in rdataset: |
141 | 142 | if rdata.algorithm == rrsig.algorithm and \ |
142 | 143 | key_id(rdata) == rrsig.key_tag: |
143 | return rdata | |
144 | return None | |
144 | candidate_keys.append(rdata) | |
145 | return candidate_keys | |
145 | 146 | |
146 | 147 | def _is_rsa(algorithm): |
147 | 148 | return algorithm in (RSAMD5, RSASHA1, |
216 | 217 | if isinstance(origin, (str, unicode)): |
217 | 218 | origin = dns.name.from_text(origin, dns.name.root) |
218 | 219 | |
219 | key = _find_key(keys, rrsig) | |
220 | if not key: | |
221 | raise ValidationFailure, 'unknown key' | |
222 | ||
223 | # For convenience, allow the rrset to be specified as a (name, rdataset) | |
224 | # tuple as well as a proper rrset | |
225 | if isinstance(rrset, tuple): | |
226 | rrname = rrset[0] | |
227 | rdataset = rrset[1] | |
228 | else: | |
229 | rrname = rrset.name | |
230 | rdataset = rrset | |
231 | ||
232 | if now is None: | |
233 | now = time.time() | |
234 | if rrsig.expiration < now: | |
235 | raise ValidationFailure, 'expired' | |
236 | if rrsig.inception > now: | |
237 | raise ValidationFailure, 'not yet valid' | |
238 | ||
239 | hash = _make_hash(rrsig.algorithm) | |
240 | ||
241 | if _is_rsa(rrsig.algorithm): | |
242 | keyptr = key.key | |
243 | (bytes,) = struct.unpack('!B', keyptr[0:1]) | |
244 | keyptr = keyptr[1:] | |
245 | if bytes == 0: | |
246 | (bytes,) = struct.unpack('!H', keyptr[0:2]) | |
247 | keyptr = keyptr[2:] | |
248 | rsa_e = keyptr[0:bytes] | |
249 | rsa_n = keyptr[bytes:] | |
250 | keylen = len(rsa_n) * 8 | |
251 | pubkey = Crypto.PublicKey.RSA.construct( | |
252 | (Crypto.Util.number.bytes_to_long(rsa_n), | |
253 | Crypto.Util.number.bytes_to_long(rsa_e))) | |
254 | sig = (Crypto.Util.number.bytes_to_long(rrsig.signature),) | |
255 | elif _is_dsa(rrsig.algorithm): | |
256 | keyptr = key.key | |
257 | (t,) = struct.unpack('!B', keyptr[0:1]) | |
258 | keyptr = keyptr[1:] | |
259 | octets = 64 + t * 8 | |
260 | dsa_q = keyptr[0:20] | |
261 | keyptr = keyptr[20:] | |
262 | dsa_p = keyptr[0:octets] | |
263 | keyptr = keyptr[octets:] | |
264 | dsa_g = keyptr[0:octets] | |
265 | keyptr = keyptr[octets:] | |
266 | dsa_y = keyptr[0:octets] | |
267 | pubkey = Crypto.PublicKey.DSA.construct( | |
268 | (Crypto.Util.number.bytes_to_long(dsa_y), | |
269 | Crypto.Util.number.bytes_to_long(dsa_g), | |
270 | Crypto.Util.number.bytes_to_long(dsa_p), | |
271 | Crypto.Util.number.bytes_to_long(dsa_q))) | |
272 | (dsa_r, dsa_s) = struct.unpack('!20s20s', rrsig.signature[1:]) | |
273 | sig = (Crypto.Util.number.bytes_to_long(dsa_r), | |
274 | Crypto.Util.number.bytes_to_long(dsa_s)) | |
275 | else: | |
276 | raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm | |
277 | ||
278 | hash.update(_to_rdata(rrsig, origin)[:18]) | |
279 | hash.update(rrsig.signer.to_digestable(origin)) | |
280 | ||
281 | if rrsig.labels < len(rrname) - 1: | |
282 | suffix = rrname.split(rrsig.labels + 1)[1] | |
283 | rrname = dns.name.from_text('*', suffix) | |
284 | rrnamebuf = rrname.to_digestable(origin) | |
285 | rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass, | |
286 | rrsig.original_ttl) | |
287 | rrlist = sorted(rdataset); | |
288 | for rr in rrlist: | |
289 | hash.update(rrnamebuf) | |
290 | hash.update(rrfixed) | |
291 | rrdata = rr.to_digestable(origin) | |
292 | rrlen = struct.pack('!H', len(rrdata)) | |
293 | hash.update(rrlen) | |
294 | hash.update(rrdata) | |
295 | ||
296 | digest = hash.digest() | |
297 | ||
298 | if _is_rsa(rrsig.algorithm): | |
299 | # PKCS1 algorithm identifier goop | |
300 | digest = _make_algorithm_id(rrsig.algorithm) + digest | |
301 | padlen = keylen // 8 - len(digest) - 3 | |
302 | digest = chr(0) + chr(1) + chr(0xFF) * padlen + chr(0) + digest | |
303 | elif _is_dsa(rrsig.algorithm): | |
304 | pass | |
305 | else: | |
306 | # Raise here for code clarity; this won't actually ever happen | |
307 | # since if the algorithm is really unknown we'd already have | |
308 | # raised an exception above | |
309 | raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm | |
310 | ||
311 | if not pubkey.verify(digest, sig): | |
312 | raise ValidationFailure, 'verify failure' | |
220 | for candidate_key in _find_candidate_keys(keys, rrsig): | |
221 | if not candidate_key: | |
222 | raise ValidationFailure, 'unknown key' | |
223 | ||
224 | # For convenience, allow the rrset to be specified as a (name, rdataset) | |
225 | # tuple as well as a proper rrset | |
226 | if isinstance(rrset, tuple): | |
227 | rrname = rrset[0] | |
228 | rdataset = rrset[1] | |
229 | else: | |
230 | rrname = rrset.name | |
231 | rdataset = rrset | |
232 | ||
233 | if now is None: | |
234 | now = time.time() | |
235 | if rrsig.expiration < now: | |
236 | raise ValidationFailure, 'expired' | |
237 | if rrsig.inception > now: | |
238 | raise ValidationFailure, 'not yet valid' | |
239 | ||
240 | hash = _make_hash(rrsig.algorithm) | |
241 | ||
242 | if _is_rsa(rrsig.algorithm): | |
243 | keyptr = candidate_key.key | |
244 | (bytes,) = struct.unpack('!B', keyptr[0:1]) | |
245 | keyptr = keyptr[1:] | |
246 | if bytes == 0: | |
247 | (bytes,) = struct.unpack('!H', keyptr[0:2]) | |
248 | keyptr = keyptr[2:] | |
249 | rsa_e = keyptr[0:bytes] | |
250 | rsa_n = keyptr[bytes:] | |
251 | keylen = len(rsa_n) * 8 | |
252 | pubkey = Crypto.PublicKey.RSA.construct( | |
253 | (Crypto.Util.number.bytes_to_long(rsa_n), | |
254 | Crypto.Util.number.bytes_to_long(rsa_e))) | |
255 | sig = (Crypto.Util.number.bytes_to_long(rrsig.signature),) | |
256 | elif _is_dsa(rrsig.algorithm): | |
257 | keyptr = candidate_key.key | |
258 | (t,) = struct.unpack('!B', keyptr[0:1]) | |
259 | keyptr = keyptr[1:] | |
260 | octets = 64 + t * 8 | |
261 | dsa_q = keyptr[0:20] | |
262 | keyptr = keyptr[20:] | |
263 | dsa_p = keyptr[0:octets] | |
264 | keyptr = keyptr[octets:] | |
265 | dsa_g = keyptr[0:octets] | |
266 | keyptr = keyptr[octets:] | |
267 | dsa_y = keyptr[0:octets] | |
268 | pubkey = Crypto.PublicKey.DSA.construct( | |
269 | (Crypto.Util.number.bytes_to_long(dsa_y), | |
270 | Crypto.Util.number.bytes_to_long(dsa_g), | |
271 | Crypto.Util.number.bytes_to_long(dsa_p), | |
272 | Crypto.Util.number.bytes_to_long(dsa_q))) | |
273 | (dsa_r, dsa_s) = struct.unpack('!20s20s', rrsig.signature[1:]) | |
274 | sig = (Crypto.Util.number.bytes_to_long(dsa_r), | |
275 | Crypto.Util.number.bytes_to_long(dsa_s)) | |
276 | else: | |
277 | raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm | |
278 | ||
279 | hash.update(_to_rdata(rrsig, origin)[:18]) | |
280 | hash.update(rrsig.signer.to_digestable(origin)) | |
281 | ||
282 | if rrsig.labels < len(rrname) - 1: | |
283 | suffix = rrname.split(rrsig.labels + 1)[1] | |
284 | rrname = dns.name.from_text('*', suffix) | |
285 | rrnamebuf = rrname.to_digestable(origin) | |
286 | rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass, | |
287 | rrsig.original_ttl) | |
288 | rrlist = sorted(rdataset); | |
289 | for rr in rrlist: | |
290 | hash.update(rrnamebuf) | |
291 | hash.update(rrfixed) | |
292 | rrdata = rr.to_digestable(origin) | |
293 | rrlen = struct.pack('!H', len(rrdata)) | |
294 | hash.update(rrlen) | |
295 | hash.update(rrdata) | |
296 | ||
297 | digest = hash.digest() | |
298 | ||
299 | if _is_rsa(rrsig.algorithm): | |
300 | # PKCS1 algorithm identifier goop | |
301 | digest = _make_algorithm_id(rrsig.algorithm) + digest | |
302 | padlen = keylen // 8 - len(digest) - 3 | |
303 | digest = chr(0) + chr(1) + chr(0xFF) * padlen + chr(0) + digest | |
304 | elif _is_dsa(rrsig.algorithm): | |
305 | pass | |
306 | else: | |
307 | # Raise here for code clarity; this won't actually ever happen | |
308 | # since if the algorithm is really unknown we'd already have | |
309 | # raised an exception above | |
310 | raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm | |
311 | ||
312 | if pubkey.verify(digest, sig): | |
313 | return | |
314 | raise ValidationFailure, 'verify failure' | |
313 | 315 | |
314 | 316 | def _validate(rrset, rrsigset, keys, origin=None, now=None): |
315 | 317 | """Validate an RRset |
43 | 43 | @type current: int |
44 | 44 | @param olen: The length of the wire-format option data |
45 | 45 | @type olen: int |
46 | @rtype: dns.ends.Option instance""" | |
46 | @rtype: dns.edns.Option instance""" | |
47 | 47 | raise NotImplementedError |
48 | 48 | |
49 | 49 | from_wire = classmethod(from_wire) |
50 | 50 | |
51 | 51 | def _cmp(self, other): |
52 | """Compare an ENDS option with another option of the same type. | |
52 | """Compare an EDNS option with another option of the same type. | |
53 | 53 | Return < 0 if self < other, 0 if self == other, and > 0 if self > other. |
54 | 54 | """ |
55 | 55 | raise NotImplementedError |
135 | 135 | @type current: int |
136 | 136 | @param olen: The length of the wire-format option data |
137 | 137 | @type olen: int |
138 | @rtype: dns.ends.Option instance""" | |
138 | @rtype: dns.edns.Option instance""" | |
139 | 139 | |
140 | 140 | cls = get_option_class(otype) |
141 | 141 | return cls.from_wire(otype, wire, current, olen) |
0 | # Copyright (C) 2003-2007, 2009-2011 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 GENERATE range conversion.""" | |
16 | ||
17 | import dns | |
18 | ||
19 | def from_text(text): | |
20 | """Convert the text form of a range in a GENERATE statement to an | |
21 | integer. | |
22 | ||
23 | @param text: the textual range | |
24 | @type text: string | |
25 | @return: The start, stop and step values. | |
26 | @rtype: tuple | |
27 | """ | |
28 | # TODO, figure out the bounds on start, stop and step. | |
29 | ||
30 | import pdb | |
31 | step = 1 | |
32 | cur = '' | |
33 | state = 0 | |
34 | # state 0 1 2 3 4 | |
35 | # x - y / z | |
36 | for c in text: | |
37 | if c == '-' and state == 0: | |
38 | start = int(cur) | |
39 | cur = '' | |
40 | state = 2 | |
41 | elif c == '/': | |
42 | stop = int(cur) | |
43 | cur = '' | |
44 | state = 4 | |
45 | elif c.isdigit(): | |
46 | cur += c | |
47 | else: | |
48 | raise dns.exception.SyntaxError("Could not parse %s" % (c)) | |
49 | ||
50 | if state in (1, 3): | |
51 | raise dns.exception.SyntaxError | |
52 | ||
53 | if state == 2: | |
54 | stop = int(cur) | |
55 | ||
56 | if state == 4: | |
57 | step = int(cur) | |
58 | ||
59 | assert step >= 1 | |
60 | assert start >= 0 | |
61 | assert start <= stop | |
62 | # TODO, can start == stop? | |
63 | ||
64 | return (start, stop, step) |
71 | 71 | if current_len > best_len: |
72 | 72 | best_start = start |
73 | 73 | best_len = current_len |
74 | if best_len > 0: | |
74 | if best_len > 1: | |
75 | 75 | if best_start == 0 and \ |
76 | 76 | (best_len == 6 or |
77 | 77 | best_len == 5 and chunks[5] == 'ffff'): |
418 | 418 | else: |
419 | 419 | if not compress is None and len(n) > 1: |
420 | 420 | pos = file.tell() |
421 | if pos < 0xc000: | |
421 | if pos <= 0x3fff: | |
422 | 422 | compress[n] = pos |
423 | 423 | l = len(label) |
424 | 424 | file.write(chr(l)) |
48 | 48 | |
49 | 49 | s = StringIO.StringIO() |
50 | 50 | for rds in self.rdatasets: |
51 | print >> s, rds.to_text(name, **kw) | |
51 | if len(rds) > 0: | |
52 | print >> s, rds.to_text(name, **kw) | |
52 | 53 | return s.getvalue()[:-1] |
53 | 54 | |
54 | 55 | def __repr__(self): |
338 | 338 | dns.rdatatype.AXFR. |
339 | 339 | @type rdtype: int or string |
340 | 340 | @param rdclass: The class of the zone transfer. The default is |
341 | dns.rdatatype.IN. | |
341 | dns.rdataclass.IN. | |
342 | 342 | @type rdclass: int or string |
343 | 343 | @param timeout: The number of seconds to wait for each response message. |
344 | 344 | If None, the default, wait forever. |
77 | 77 | DHCID = 49 |
78 | 78 | NSEC3 = 50 |
79 | 79 | NSEC3PARAM = 51 |
80 | TLSA = 52 | |
80 | 81 | HIP = 55 |
81 | 82 | SPF = 99 |
82 | 83 | UNSPEC = 103 |
139 | 140 | 'DHCID' : DHCID, |
140 | 141 | 'NSEC3' : NSEC3, |
141 | 142 | 'NSEC3PARAM' : NSEC3PARAM, |
143 | 'TLSA' : TLSA, | |
142 | 144 | 'HIP' : HIP, |
143 | 145 | 'SPF' : SPF, |
144 | 146 | 'UNSPEC' : UNSPEC, |
113 | 113 | prior_rdtype = nrdtype |
114 | 114 | new_window = nrdtype // 256 |
115 | 115 | if new_window != window: |
116 | windows.append((window, ''.join(bitmap[0:octets]))) | |
116 | if octets != 0: | |
117 | windows.append((window, ''.join(bitmap[0:octets]))) | |
117 | 118 | bitmap = ['\0'] * 32 |
118 | 119 | window = new_window |
119 | 120 | offset = nrdtype % 256 |
121 | 122 | bit = offset % 8 |
122 | 123 | octets = byte + 1 |
123 | 124 | bitmap[byte] = chr(ord(bitmap[byte]) | (0x80 >> bit)) |
124 | windows.append((window, ''.join(bitmap[0:octets]))) | |
125 | if octets != 0: | |
126 | windows.append((window, ''.join(bitmap[0:octets]))) | |
125 | 127 | return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, windows) |
126 | 128 | |
127 | 129 | from_text = classmethod(from_text) |
0 | # Copyright (C) 2005-2007, 2009-2011 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 struct | |
16 | ||
17 | import dns.rdata | |
18 | import dns.rdatatype | |
19 | ||
20 | class TLSA(dns.rdata.Rdata): | |
21 | """TLSA record | |
22 | ||
23 | @ivar usage: The certificate usage | |
24 | @type usage: int | |
25 | @ivar selector: The selector field | |
26 | @type selector: int | |
27 | @ivar mtype: The 'matching type' field | |
28 | @type mtype: int | |
29 | @ivar cert: The 'Certificate Association Data' field | |
30 | @type cert: string | |
31 | @see: RFC 6698""" | |
32 | ||
33 | __slots__ = ['usage', 'selector', 'mtype', 'cert'] | |
34 | ||
35 | def __init__(self, rdclass, rdtype, usage, selector, | |
36 | mtype, cert): | |
37 | super(TLSA, self).__init__(rdclass, rdtype) | |
38 | self.usage = usage | |
39 | self.selector = selector | |
40 | self.mtype = mtype | |
41 | self.cert = cert | |
42 | ||
43 | def to_text(self, origin=None, relativize=True, **kw): | |
44 | return '%d %d %d %s' % (self.usage, | |
45 | self.selector, | |
46 | self.mtype, | |
47 | dns.rdata._hexify(self.cert, | |
48 | chunksize=128)) | |
49 | ||
50 | def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): | |
51 | usage = tok.get_uint8() | |
52 | selector = tok.get_uint8() | |
53 | mtype = tok.get_uint8() | |
54 | cert_chunks = [] | |
55 | while 1: | |
56 | t = tok.get().unescape() | |
57 | if t.is_eol_or_eof(): | |
58 | break | |
59 | if not t.is_identifier(): | |
60 | raise dns.exception.SyntaxError | |
61 | cert_chunks.append(t.value) | |
62 | cert = ''.join(cert_chunks) | |
63 | cert = cert.decode('hex_codec') | |
64 | return cls(rdclass, rdtype, usage, selector, mtype, cert) | |
65 | ||
66 | from_text = classmethod(from_text) | |
67 | ||
68 | def to_wire(self, file, compress = None, origin = None): | |
69 | header = struct.pack("!BBB", self.usage, self.selector, self.mtype) | |
70 | file.write(header) | |
71 | file.write(self.cert) | |
72 | ||
73 | def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): | |
74 | header = struct.unpack("!BBB", wire[current : current + 3]) | |
75 | current += 3 | |
76 | rdlen -= 3 | |
77 | cert = wire[current : current + rdlen].unwrap() | |
78 | return cls(rdclass, rdtype, header[0], header[1], header[2], cert) | |
79 | ||
80 | from_wire = classmethod(from_wire) | |
81 | ||
82 | def _cmp(self, other): | |
83 | hs = struct.pack("!BBB", self.usage, self.selector, self.mtype) | |
84 | ho = struct.pack("!BBB", other.usage, other.selector, other.mtype) | |
85 | v = cmp(hs, ho) | |
86 | if v == 0: | |
87 | v = cmp(self.cert, other.cert) | |
88 | return v |
304 | 304 | def write_header(self): |
305 | 305 | """Write the DNS message header. |
306 | 306 | |
307 | Writing the DNS message header is done asfter all sections | |
307 | Writing the DNS message header is done after all sections | |
308 | 308 | have been rendered, but before the optional TSIG signature |
309 | 309 | is added. |
310 | 310 | """ |
21 | 21 | import sys |
22 | 22 | import time |
23 | 23 | |
24 | try: | |
25 | import threading as _threading | |
26 | except ImportError: | |
27 | import dummy_threading as _threading | |
28 | ||
24 | 29 | import dns.exception |
25 | 30 | import dns.flags |
26 | 31 | import dns.ipv4 |
38 | 43 | |
39 | 44 | class NXDOMAIN(dns.exception.DNSException): |
40 | 45 | """The query name does not exist.""" |
46 | pass | |
47 | ||
48 | class YXDOMAIN(dns.exception.DNSException): | |
49 | """The query name is too long after DNAME substitution.""" | |
41 | 50 | pass |
42 | 51 | |
43 | 52 | # The definition of the Timeout exception has moved from here to the |
211 | 220 | self.data = {} |
212 | 221 | self.cleaning_interval = cleaning_interval |
213 | 222 | self.next_cleaning = time.time() + self.cleaning_interval |
214 | ||
215 | def maybe_clean(self): | |
223 | self.lock = _threading.Lock() | |
224 | ||
225 | def _maybe_clean(self): | |
216 | 226 | """Clean the cache if it's time to do so.""" |
217 | 227 | |
218 | 228 | now = time.time() |
235 | 245 | @rtype: dns.resolver.Answer object or None |
236 | 246 | """ |
237 | 247 | |
238 | self.maybe_clean() | |
239 | v = self.data.get(key) | |
240 | if v is None or v.expiration <= time.time(): | |
241 | return None | |
242 | return v | |
248 | try: | |
249 | self.lock.acquire() | |
250 | self._maybe_clean() | |
251 | v = self.data.get(key) | |
252 | if v is None or v.expiration <= time.time(): | |
253 | return None | |
254 | return v | |
255 | finally: | |
256 | self.lock.release() | |
243 | 257 | |
244 | 258 | def put(self, key, value): |
245 | 259 | """Associate key and value in the cache. |
250 | 264 | @type value: dns.resolver.Answer object |
251 | 265 | """ |
252 | 266 | |
253 | self.maybe_clean() | |
254 | self.data[key] = value | |
267 | try: | |
268 | self.lock.acquire() | |
269 | self._maybe_clean() | |
270 | self.data[key] = value | |
271 | finally: | |
272 | self.lock.release() | |
255 | 273 | |
256 | 274 | def flush(self, key=None): |
257 | 275 | """Flush the cache. |
263 | 281 | @type key: (dns.name.Name, int, int) tuple or None |
264 | 282 | """ |
265 | 283 | |
266 | if not key is None: | |
267 | if self.data.has_key(key): | |
268 | del self.data[key] | |
269 | else: | |
270 | self.data = {} | |
271 | self.next_cleaning = time.time() + self.cleaning_interval | |
284 | try: | |
285 | self.lock.acquire() | |
286 | if not key is None: | |
287 | if self.data.has_key(key): | |
288 | del self.data[key] | |
289 | else: | |
290 | self.data = {} | |
291 | self.next_cleaning = time.time() + self.cleaning_interval | |
292 | finally: | |
293 | self.lock.release() | |
272 | 294 | |
273 | 295 | class LRUCacheNode(object): |
274 | 296 | """LRUCache node. |
321 | 343 | self.data = {} |
322 | 344 | self.set_max_size(max_size) |
323 | 345 | self.sentinel = LRUCacheNode(None, None) |
346 | self.lock = _threading.Lock() | |
324 | 347 | |
325 | 348 | def set_max_size(self, max_size): |
326 | 349 | if max_size < 1: |
335 | 358 | query name, rdtype, and rdclass. |
336 | 359 | @rtype: dns.resolver.Answer object or None |
337 | 360 | """ |
338 | node = self.data.get(key) | |
339 | if node is None: | |
340 | return None | |
341 | # Unlink because we're either going to move the node to the front | |
342 | # of the LRU list or we're going to free it. | |
343 | node.unlink() | |
344 | if node.value.expiration <= time.time(): | |
345 | del self.data[node.key] | |
346 | return None | |
347 | node.link_after(self.sentinel) | |
348 | return node.value | |
361 | try: | |
362 | self.lock.acquire() | |
363 | node = self.data.get(key) | |
364 | if node is None: | |
365 | return None | |
366 | # Unlink because we're either going to move the node to the front | |
367 | # of the LRU list or we're going to free it. | |
368 | node.unlink() | |
369 | if node.value.expiration <= time.time(): | |
370 | del self.data[node.key] | |
371 | return None | |
372 | node.link_after(self.sentinel) | |
373 | return node.value | |
374 | finally: | |
375 | self.lock.release() | |
349 | 376 | |
350 | 377 | def put(self, key, value): |
351 | 378 | """Associate key and value in the cache. |
355 | 382 | @param value: The answer being cached |
356 | 383 | @type value: dns.resolver.Answer object |
357 | 384 | """ |
358 | node = self.data.get(key) | |
359 | if not node is None: | |
360 | node.unlink() | |
361 | del self.data[node.key] | |
362 | while len(self.data) >= self.max_size: | |
363 | node = self.sentinel.prev | |
364 | node.unlink() | |
365 | del self.data[node.key] | |
366 | node = LRUCacheNode(key, value) | |
367 | node.link_after(self.sentinel) | |
368 | self.data[key] = node | |
369 | ||
370 | def flush(self, key=None): | |
371 | """Flush the cache. | |
372 | ||
373 | If I{key} is specified, only that item is flushed. Otherwise | |
374 | the entire cache is flushed. | |
375 | ||
376 | @param key: the key to flush | |
377 | @type key: (dns.name.Name, int, int) tuple or None | |
378 | """ | |
379 | if not key is None: | |
385 | try: | |
386 | self.lock.acquire() | |
380 | 387 | node = self.data.get(key) |
381 | 388 | if not node is None: |
382 | 389 | node.unlink() |
383 | 390 | del self.data[node.key] |
384 | else: | |
385 | node = self.sentinel.next | |
386 | while node != self.sentinel: | |
387 | next = node.next | |
388 | node.prev = None | |
389 | node.next = None | |
390 | node = next | |
391 | self.data = {} | |
391 | while len(self.data) >= self.max_size: | |
392 | node = self.sentinel.prev | |
393 | node.unlink() | |
394 | del self.data[node.key] | |
395 | node = LRUCacheNode(key, value) | |
396 | node.link_after(self.sentinel) | |
397 | self.data[key] = node | |
398 | finally: | |
399 | self.lock.release() | |
400 | ||
401 | def flush(self, key=None): | |
402 | """Flush the cache. | |
403 | ||
404 | If I{key} is specified, only that item is flushed. Otherwise | |
405 | the entire cache is flushed. | |
406 | ||
407 | @param key: the key to flush | |
408 | @type key: (dns.name.Name, int, int) tuple or None | |
409 | """ | |
410 | try: | |
411 | self.lock.acquire() | |
412 | if not key is None: | |
413 | node = self.data.get(key) | |
414 | if not node is None: | |
415 | node.unlink() | |
416 | del self.data[node.key] | |
417 | else: | |
418 | node = self.sentinel.next | |
419 | while node != self.sentinel: | |
420 | next = node.next | |
421 | node.prev = None | |
422 | node.next = None | |
423 | node = next | |
424 | self.data = {} | |
425 | finally: | |
426 | self.lock.release() | |
392 | 427 | |
393 | 428 | class Resolver(object): |
394 | 429 | """DNS stub resolver |
424 | 459 | @type ednsflags: int |
425 | 460 | @ivar payload: The EDNS payload size. The default is 0. |
426 | 461 | @type payload: int |
462 | @ivar flags: The message flags to use. The default is None (i.e. not overwritten) | |
463 | @type flags: int | |
427 | 464 | @ivar cache: The cache to use. The default is None. |
428 | 465 | @type cache: dns.resolver.Cache object |
466 | @ivar retry_servfail: should we retry a nameserver if it says SERVFAIL? | |
467 | The default is 'false'. | |
468 | @type retry_servfail: bool | |
429 | 469 | """ |
430 | 470 | def __init__(self, filename='/etc/resolv.conf', configure=True): |
431 | 471 | """Initialize a resolver instance. |
465 | 505 | self.ednsflags = 0 |
466 | 506 | self.payload = 0 |
467 | 507 | self.cache = None |
508 | self.flags = None | |
509 | self.retry_servfail = False | |
468 | 510 | |
469 | 511 | def read_resolv_conf(self, f): |
470 | 512 | """Process f as a file in the /etc/resolv.conf format. If f is |
719 | 761 | @rtype: dns.resolver.Answer instance |
720 | 762 | @raises Timeout: no answers could be found in the specified lifetime |
721 | 763 | @raises NXDOMAIN: the query name does not exist |
764 | @raises YXDOMAIN: the query name is too long after DNAME substitution | |
722 | 765 | @raises NoAnswer: the response did not contain an answer and |
723 | 766 | raise_on_no_answer is True. |
724 | 767 | @raises NoNameservers: no non-broken nameservers are available to |
760 | 803 | request.use_tsig(self.keyring, self.keyname, |
761 | 804 | algorithm=self.keyalgorithm) |
762 | 805 | request.use_edns(self.edns, self.ednsflags, self.payload) |
806 | if self.flags is not None: | |
807 | request.flags = self.flags | |
763 | 808 | response = None |
764 | 809 | # |
765 | 810 | # make a copy of the servers list so we can alter it later. |
822 | 867 | response = None |
823 | 868 | continue |
824 | 869 | rcode = response.rcode() |
870 | if rcode == dns.rcode.YXDOMAIN: | |
871 | raise YXDOMAIN | |
825 | 872 | if rcode == dns.rcode.NOERROR or \ |
826 | 873 | rcode == dns.rcode.NXDOMAIN: |
827 | 874 | break |
830 | 877 | # rcode in it. Remove the server from the mix if |
831 | 878 | # the rcode isn't SERVFAIL. |
832 | 879 | # |
833 | if rcode != dns.rcode.SERVFAIL: | |
880 | if rcode != dns.rcode.SERVFAIL or not retry_servfail: | |
834 | 881 | nameservers.remove(nameserver) |
835 | 882 | response = None |
836 | 883 | if not response is None: |
896 | 943 | self.edns = edns |
897 | 944 | self.ednsflags = ednsflags |
898 | 945 | self.payload = payload |
946 | ||
947 | def set_flags(self, flags): | |
948 | """Overrides the default flags with your own | |
949 | ||
950 | @param flags: The flags to overwrite the default with | |
951 | @type flags: int""" | |
952 | self.flags = flags | |
899 | 953 | |
900 | 954 | default_resolver = None |
901 | 955 |
15 | 15 | """dnspython release version information.""" |
16 | 16 | |
17 | 17 | MAJOR = 1 |
18 | MINOR = 10 | |
18 | MINOR = 11 | |
19 | 19 | MICRO = 0 |
20 | 20 | RELEASELEVEL = 0x0f |
21 | 21 | SERIAL = 0 |
17 | 17 | from __future__ import generators |
18 | 18 | |
19 | 19 | import sys |
20 | import re | |
20 | 21 | |
21 | 22 | import dns.exception |
22 | 23 | import dns.name |
27 | 28 | import dns.rrset |
28 | 29 | import dns.tokenizer |
29 | 30 | import dns.ttl |
31 | import dns.grange | |
32 | ||
30 | 33 | |
31 | 34 | class BadZone(dns.exception.DNSException): |
32 | 35 | """The zone is malformed.""" |
639 | 642 | covers = rd.covers() |
640 | 643 | rds = n.find_rdataset(rdclass, rdtype, covers, True) |
641 | 644 | rds.add(rd, ttl) |
645 | ||
646 | def _parse_modify(self, side): | |
647 | # Here we catch everything in '{' '}' in a group so we can replace it | |
648 | # with ''. | |
649 | is_generate1 = re.compile("^.*\$({(\+|-?)(\d+),(\d+),(.)}).*$") | |
650 | is_generate2 = re.compile("^.*\$({(\+|-?)(\d+)}).*$") | |
651 | is_generate3 = re.compile("^.*\$({(\+|-?)(\d+),(\d+)}).*$") | |
652 | # Sometimes there are modifiers in the hostname. These come after | |
653 | # the dollar sign. They are in the form: ${offset[,width[,base]]}. | |
654 | # Make names | |
655 | g1 = is_generate1.match(side) | |
656 | if g1: | |
657 | mod, sign, offset, width, base = g1.groups() | |
658 | if sign == '': | |
659 | sign = '+' | |
660 | g2 = is_generate2.match(side) | |
661 | if g2: | |
662 | mod, sign, offset = g2.groups() | |
663 | if sign == '': | |
664 | sign = '+' | |
665 | width = 0 | |
666 | base = 'd' | |
667 | g3 = is_generate3.match(side) | |
668 | if g3: | |
669 | mod, sign, offset, width = g1.groups() | |
670 | if sign == '': | |
671 | sign = '+' | |
672 | width = g1.groups()[2] | |
673 | base = 'd' | |
674 | ||
675 | if not (g1 or g2 or g3): | |
676 | mod = '' | |
677 | sign = '+' | |
678 | offset = 0 | |
679 | width = 0 | |
680 | base = 'd' | |
681 | ||
682 | if base != 'd': | |
683 | raise NotImplemented | |
684 | ||
685 | return mod, sign, offset, width, base | |
686 | ||
687 | def _generate_line(self): | |
688 | # range lhs [ttl] [class] type rhs [ comment ] | |
689 | """Process one line containing the GENERATE statement from a DNS | |
690 | master file.""" | |
691 | if self.current_origin is None: | |
692 | raise UnknownOrigin | |
693 | ||
694 | token = self.tok.get() | |
695 | # Range (required) | |
696 | try: | |
697 | start, stop, step = dns.grange.from_text(token.value) | |
698 | token = self.tok.get() | |
699 | if not token.is_identifier(): | |
700 | raise dns.exception.SyntaxError | |
701 | except: | |
702 | raise dns.exception.SyntaxError | |
703 | ||
704 | # lhs (required) | |
705 | try: | |
706 | lhs = token.value | |
707 | token = self.tok.get() | |
708 | if not token.is_identifier(): | |
709 | raise dns.exception.SyntaxError | |
710 | except: | |
711 | raise dns.exception.SyntaxError | |
712 | ||
713 | # TTL | |
714 | try: | |
715 | ttl = dns.ttl.from_text(token.value) | |
716 | token = self.tok.get() | |
717 | if not token.is_identifier(): | |
718 | raise dns.exception.SyntaxError | |
719 | except dns.ttl.BadTTL: | |
720 | ttl = self.ttl | |
721 | # Class | |
722 | try: | |
723 | rdclass = dns.rdataclass.from_text(token.value) | |
724 | token = self.tok.get() | |
725 | if not token.is_identifier(): | |
726 | raise dns.exception.SyntaxError | |
727 | except dns.exception.SyntaxError: | |
728 | raise dns.exception.SyntaxError | |
729 | except: | |
730 | rdclass = self.zone.rdclass | |
731 | if rdclass != self.zone.rdclass: | |
732 | raise dns.exception.SyntaxError("RR class is not zone's class") | |
733 | # Type | |
734 | try: | |
735 | rdtype = dns.rdatatype.from_text(token.value) | |
736 | token = self.tok.get() | |
737 | if not token.is_identifier(): | |
738 | raise dns.exception.SyntaxError | |
739 | except: | |
740 | raise dns.exception.SyntaxError("unknown rdatatype '%s'" % | |
741 | token.value) | |
742 | ||
743 | # lhs (required) | |
744 | try: | |
745 | rhs = token.value | |
746 | except: | |
747 | raise dns.exception.SyntaxError | |
748 | ||
749 | ||
750 | lmod, lsign, loffset, lwidth, lbase = self._parse_modify(lhs) | |
751 | rmod, rsign, roffset, rwidth, rbase = self._parse_modify(rhs) | |
752 | for i in range(start, stop + 1, step): | |
753 | # +1 because bind is inclusive and python is exclusive | |
754 | ||
755 | if lsign == '+': | |
756 | lindex = i + int(loffset) | |
757 | elif lsign == '-': | |
758 | lindex = i - int(loffset) | |
759 | ||
760 | if rsign == '-': | |
761 | rindex = i - int(roffset) | |
762 | elif rsign == '+': | |
763 | rindex = i + int(roffset) | |
764 | ||
765 | lzfindex = str(lindex).zfill(int(lwidth)) | |
766 | rzfindex = str(rindex).zfill(int(rwidth)) | |
767 | ||
768 | ||
769 | name = lhs.replace('$%s' % (lmod), lzfindex) | |
770 | rdata = rhs.replace('$%s' % (rmod), rzfindex) | |
771 | ||
772 | self.last_name = dns.name.from_text(name, self.current_origin) | |
773 | name = self.last_name | |
774 | if not name.is_subdomain(self.zone.origin): | |
775 | self._eat_line() | |
776 | return | |
777 | if self.relativize: | |
778 | name = name.relativize(self.zone.origin) | |
779 | ||
780 | n = self.zone.nodes.get(name) | |
781 | if n is None: | |
782 | n = self.zone.node_factory() | |
783 | self.zone.nodes[name] = n | |
784 | try: | |
785 | rd = dns.rdata.from_text(rdclass, rdtype, rdata, | |
786 | self.current_origin, False) | |
787 | except dns.exception.SyntaxError: | |
788 | # Catch and reraise. | |
789 | (ty, va) = sys.exc_info()[:2] | |
790 | raise va | |
791 | except: | |
792 | # All exceptions that occur in the processing of rdata | |
793 | # are treated as syntax errors. This is not strictly | |
794 | # correct, but it is correct almost all of the time. | |
795 | # We convert them to syntax errors so that we can emit | |
796 | # helpful filename:line info. | |
797 | (ty, va) = sys.exc_info()[:2] | |
798 | raise dns.exception.SyntaxError("caught exception %s: %s" % | |
799 | (str(ty), str(va))) | |
800 | ||
801 | rd.choose_relativity(self.zone.origin, self.relativize) | |
802 | covers = rd.covers() | |
803 | rds = n.find_rdataset(rdclass, rdtype, covers, True) | |
804 | rds.add(rd, ttl) | |
642 | 805 | |
643 | 806 | def read(self): |
644 | 807 | """Read a DNS master file and build a zone object. |
681 | 844 | self.zone.origin = self.current_origin |
682 | 845 | elif u == '$INCLUDE' and self.allow_include: |
683 | 846 | token = self.tok.get() |
684 | if not token.is_quoted_string(): | |
685 | raise dns.exception.SyntaxError("bad filename in $INCLUDE") | |
686 | 847 | filename = token.value |
687 | 848 | token = self.tok.get() |
688 | 849 | if token.is_identifier(): |
702 | 863 | self.tok = dns.tokenizer.Tokenizer(self.current_file, |
703 | 864 | filename) |
704 | 865 | self.current_origin = new_origin |
866 | elif u == '$GENERATE': | |
867 | self._generate_line() | |
705 | 868 | else: |
706 | 869 | raise dns.exception.SyntaxError("Unknown master file directive '" + u + "'") |
707 | 870 | continue |
17 | 17 | import sys |
18 | 18 | from distutils.core import setup |
19 | 19 | |
20 | version = '1.10.0' | |
20 | version = '1.11.0' | |
21 | 21 | |
22 | 22 | kwargs = { |
23 | 23 | 'name' : 'dnspython', |
39 | 39 | ttl = dns.ttl.from_text("2147483648") |
40 | 40 | self.failUnlessRaises(dns.ttl.BadTTL, bad) |
41 | 41 | |
42 | def test_empty_NSEC3_window(self): | |
43 | rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NSEC3, | |
44 | "1 0 100 ABCD SCBCQHKU35969L2A68P3AD59LHF30715") | |
45 | self.failUnless(rdata.windows == []) | |
46 | ||
42 | 47 | if __name__ == '__main__': |
43 | 48 | unittest.main() |
26 | 26 | abs_keys = { abs_dnspython_org : |
27 | 27 | dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'DNSKEY', |
28 | 28 | '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=', |
29 | '256 3 5 AwEAAdSSghOGjU33IQZgwZM2Hh771VGXX05olJK49FxpSyuEAjDBXY58 LGU9R2Zgeecnk/b9EAhFu/vCV9oECtiTCvwuVAkt9YEweqYDluQInmgP NGMJCKdSLlnX93DkjDw8rMYv5dqXCuSGPlKChfTJOLQxIAxGloS7lL+c 0CTZydAF') | |
30 | } | |
31 | ||
32 | abs_keys_duplicate_keytag = { abs_dnspython_org : | |
33 | dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'DNSKEY', | |
34 | '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=', | |
35 | '256 3 5 AwEAAdSSg++++THIS/IS/NOT/THE/CORRECT/KEY++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AaOSydAF', | |
29 | 36 | '256 3 5 AwEAAdSSghOGjU33IQZgwZM2Hh771VGXX05olJK49FxpSyuEAjDBXY58 LGU9R2Zgeecnk/b9EAhFu/vCV9oECtiTCvwuVAkt9YEweqYDluQInmgP NGMJCKdSLlnX93DkjDw8rMYv5dqXCuSGPlKChfTJOLQxIAxGloS7lL+c 0CTZydAF') |
30 | 37 | } |
31 | 38 | |
94 | 101 | def testAbsoluteRSAGood(self): |
95 | 102 | dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys, None, when) |
96 | 103 | |
104 | def testDuplicateKeytag(self): | |
105 | dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys_duplicate_keytag, None, when) | |
106 | ||
97 | 107 | def testAbsoluteRSABad(self): |
98 | 108 | def bad(): |
99 | 109 | dns.dnssec.validate(abs_other_soa, abs_soa_rrsig, abs_keys, None, |
164 | 164 | $TTL 301 ; 5 minutes 1 second |
165 | 165 | t A 73.80.65.49 |
166 | 166 | $TTL 3600 ; 1 hour |
167 | tlsa1 TLSA 3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065 | |
168 | tlsa2 TLSA 1 0 1 efddf0d915c7bdc5782c0881e1b2a95ad099fbdd06d7b1f77982d9364338d955 | |
169 | tlsa3 TLSA 1 0 2 81ee7f6c0ecc6b09b7785a9418f54432de630dd54dc6ee9e3c49de547708d236d4c413c3e97e44f969e635958aa410495844127c04883503e5b024cf7a8f6a94 | |
167 | 170 | txt01 TXT "foo" |
168 | 171 | txt02 TXT "foo" "bar" |
169 | 172 | txt03 TXT "foo" |
89 | 89 | srv02 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. |
90 | 90 | sshfp1 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab |
91 | 91 | t 301 IN A 73.80.65.49 |
92 | tlsa1 3600 IN TLSA 3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065 | |
93 | tlsa2 3600 IN TLSA 1 0 1 efddf0d915c7bdc5782c0881e1b2a95ad099fbdd06d7b1f77982d9364338d955 | |
94 | tlsa3 3600 IN TLSA 1 0 2 81ee7f6c0ecc6b09b7785a9418f54432de630dd54dc6ee9e3c49de547708d236d4c413c3e97e44f969e635958aa410495844127c04883503e5b024cf7a8f6a94 | |
92 | 95 | txt01 3600 IN TXT "foo" |
93 | 96 | txt02 3600 IN TXT "foo" "bar" |
94 | 97 | txt03 3600 IN TXT "foo" |
89 | 89 | srv02.example. 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. |
90 | 90 | sshfp1.example. 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab |
91 | 91 | t.example. 301 IN A 73.80.65.49 |
92 | tlsa1.example. 3600 IN TLSA 3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065 | |
93 | tlsa2.example. 3600 IN TLSA 1 0 1 efddf0d915c7bdc5782c0881e1b2a95ad099fbdd06d7b1f77982d9364338d955 | |
94 | tlsa3.example. 3600 IN TLSA 1 0 2 81ee7f6c0ecc6b09b7785a9418f54432de630dd54dc6ee9e3c49de547708d236d4c413c3e97e44f969e635958aa410495844127c04883503e5b024cf7a8f6a94 | |
92 | 95 | txt01.example. 3600 IN TXT "foo" |
93 | 96 | txt02.example. 3600 IN TXT "foo" "bar" |
94 | 97 | txt03.example. 3600 IN TXT "foo" |
0 | # Copyright (C) 2003-2007, 2009-2011 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 sys | |
16 | sys.path.insert(0, '../') # Force the local project to be *the* dns | |
17 | ||
18 | import cStringIO | |
19 | import filecmp | |
20 | import os | |
21 | import unittest | |
22 | ||
23 | import dns.exception | |
24 | import dns.rdata | |
25 | import dns.rdataclass | |
26 | import dns.rdatatype | |
27 | import dns.rrset | |
28 | import dns.zone | |
29 | ||
30 | import pprint | |
31 | ||
32 | pp = pprint.PrettyPrinter(indent=2) | |
33 | ||
34 | import pdb | |
35 | example_text = """$TTL 1h | |
36 | $ORIGIN 0.0.192.IN-ADDR.ARPA. | |
37 | $GENERATE 1-2 0 CNAME SERVER$.EXAMPLE. | |
38 | """ | |
39 | ||
40 | example_text1 = """$TTL 1h | |
41 | $ORIGIN 0.0.192.IN-ADDR.ARPA. | |
42 | $GENERATE 1-10 fooo$ CNAME $.0 | |
43 | """ | |
44 | ||
45 | example_text2 = """$TTL 1h | |
46 | @ 3600 IN SOA foo bar 1 2 3 4 5 | |
47 | @ 3600 IN NS ns1 | |
48 | @ 3600 IN NS ns2 | |
49 | bar.foo 300 IN MX 0 blaz.foo | |
50 | ns1 3600 IN A 10.0.0.1 | |
51 | ns2 3600 IN A 10.0.0.2 | |
52 | $GENERATE 3-5 foo$ A 10.0.0.$ | |
53 | """ | |
54 | ||
55 | example_text3 = """$TTL 1h | |
56 | @ 3600 IN SOA foo bar 1 2 3 4 5 | |
57 | @ 3600 IN NS ns1 | |
58 | @ 3600 IN NS ns2 | |
59 | bar.foo 300 IN MX 0 blaz.foo | |
60 | ns1 3600 IN A 10.0.0.1 | |
61 | ns2 3600 IN A 10.0.0.2 | |
62 | $GENERATE 4-8/2 foo$ A 10.0.0.$ | |
63 | """ | |
64 | ||
65 | example_text4 = """$TTL 1h | |
66 | @ 3600 IN SOA foo bar 1 2 3 4 5 | |
67 | @ 3600 IN NS ns1 | |
68 | @ 3600 IN NS ns2 | |
69 | bar.foo 300 IN MX 0 blaz.foo | |
70 | ns1 3600 IN A 10.0.0.1 | |
71 | ns2 3600 IN A 10.0.0.2 | |
72 | $GENERATE 11-13 wp-db${-10,2,d}.services.mozilla.com 0 CNAME SERVER.FOOBAR. | |
73 | """ | |
74 | ||
75 | example_text5 = """$TTL 1h | |
76 | @ 3600 IN SOA foo bar 1 2 3 4 5 | |
77 | @ 3600 IN NS ns1 | |
78 | @ 3600 IN NS ns2 | |
79 | bar.foo 300 IN MX 0 blaz.foo | |
80 | ns1 3600 IN A 10.0.0.1 | |
81 | ns2 3600 IN A 10.0.0.2 | |
82 | $GENERATE 11-13 wp-db${10,2,d}.services.mozilla.com 0 CNAME SERVER.FOOBAR. | |
83 | """ | |
84 | ||
85 | example_text6 = """$TTL 1h | |
86 | @ 3600 IN SOA foo bar 1 2 3 4 5 | |
87 | @ 3600 IN NS ns1 | |
88 | @ 3600 IN NS ns2 | |
89 | bar.foo 300 IN MX 0 blaz.foo | |
90 | ns1 3600 IN A 10.0.0.1 | |
91 | ns2 3600 IN A 10.0.0.2 | |
92 | $GENERATE 11-13 wp-db${+10,2,d}.services.mozilla.com 0 CNAME SERVER.FOOBAR. | |
93 | """ | |
94 | ||
95 | example_text7 = """$TTL 1h | |
96 | @ 3600 IN SOA foo bar 1 2 3 4 5 | |
97 | @ 3600 IN NS ns1 | |
98 | @ 3600 IN NS ns2 | |
99 | bar.foo 300 IN MX 0 blaz.foo | |
100 | ns1 3600 IN A 10.0.0.1 | |
101 | ns2 3600 IN A 10.0.0.2 | |
102 | $GENERATE 11-13 sync${-10}.db IN A 10.10.16.0 | |
103 | """ | |
104 | ||
105 | example_text8 = """$TTL 1h | |
106 | @ 3600 IN SOA foo bar 1 2 3 4 5 | |
107 | @ 3600 IN NS ns1 | |
108 | @ 3600 IN NS ns2 | |
109 | bar.foo 300 IN MX 0 blaz.foo | |
110 | ns1 3600 IN A 10.0.0.1 | |
111 | ns2 3600 IN A 10.0.0.2 | |
112 | $GENERATE 11-12 wp-db${-10,2,d} IN A 10.10.16.0 | |
113 | """ | |
114 | ||
115 | example_text9 = """$TTL 1h | |
116 | @ 3600 IN SOA foo bar 1 2 3 4 5 | |
117 | @ 3600 IN NS ns1 | |
118 | @ 3600 IN NS ns2 | |
119 | bar.foo 300 IN MX 0 blaz.foo | |
120 | ns1 3600 IN A 10.0.0.1 | |
121 | ns2 3600 IN A 10.0.0.2 | |
122 | $GENERATE 11-12 wp-db${-10,2,d} IN A 10.10.16.0 | |
123 | $GENERATE 11-13 sync${-10}.db IN A 10.10.16.0 | |
124 | """ | |
125 | example_text10 = """$TTL 1h | |
126 | @ 3600 IN SOA foo bar 1 2 3 4 5 | |
127 | @ 3600 IN NS ns1 | |
128 | @ 3600 IN NS ns2 | |
129 | bar.foo 300 IN MX 0 blaz.foo | |
130 | ns1 3600 IN A 10.0.0.1 | |
131 | ns2 3600 IN A 10.0.0.2 | |
132 | $GENERATE 27-28 $.2 PTR zlb${-26}.oob | |
133 | """ | |
134 | ||
135 | ||
136 | class GenerateTestCase(unittest.TestCase): | |
137 | ||
138 | def testFromText(self): | |
139 | def bad(): | |
140 | z = dns.zone.from_text(example_text, 'example.', relativize=True) | |
141 | self.failUnlessRaises(dns.zone.NoSOA, bad) | |
142 | ||
143 | def testFromText1(self): | |
144 | def bad(): | |
145 | z = dns.zone.from_text(example_text1, 'example.', relativize=True) | |
146 | self.failUnlessRaises(dns.zone.NoSOA, bad) | |
147 | ||
148 | def testIterateAllRdatas2(self): | |
149 | z = dns.zone.from_text(example_text2, 'example.', relativize=True) | |
150 | l = list(z.iterate_rdatas()) | |
151 | l.sort() | |
152 | exl = [(dns.name.from_text('@', None), | |
153 | 3600, | |
154 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
155 | 'ns1')), | |
156 | (dns.name.from_text('@', None), | |
157 | 3600, | |
158 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
159 | 'ns2')), | |
160 | (dns.name.from_text('@', None), | |
161 | 3600, | |
162 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, | |
163 | 'foo bar 1 2 3 4 5')), | |
164 | (dns.name.from_text('bar.foo', None), | |
165 | 300, | |
166 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, | |
167 | '0 blaz.foo')), | |
168 | (dns.name.from_text('ns1', None), | |
169 | 3600, | |
170 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
171 | '10.0.0.1')), | |
172 | (dns.name.from_text('ns2', None), | |
173 | 3600, | |
174 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
175 | '10.0.0.2')), | |
176 | (dns.name.from_text('foo3', None), | |
177 | 3600, | |
178 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
179 | '10.0.0.3')), | |
180 | (dns.name.from_text('foo4', None), | |
181 | 3600, | |
182 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
183 | '10.0.0.4')), | |
184 | (dns.name.from_text('foo5', None), | |
185 | 3600, | |
186 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
187 | '10.0.0.5'))] | |
188 | ||
189 | exl.sort() | |
190 | self.failUnless(l == exl) | |
191 | ||
192 | def testIterateAllRdatas3(self): | |
193 | z = dns.zone.from_text(example_text3, 'example.', relativize=True) | |
194 | l = list(z.iterate_rdatas()) | |
195 | l.sort() | |
196 | exl = [(dns.name.from_text('@', None), | |
197 | 3600, | |
198 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
199 | 'ns1')), | |
200 | (dns.name.from_text('@', None), | |
201 | 3600, | |
202 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
203 | 'ns2')), | |
204 | (dns.name.from_text('@', None), | |
205 | 3600, | |
206 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, | |
207 | 'foo bar 1 2 3 4 5')), | |
208 | (dns.name.from_text('bar.foo', None), | |
209 | 300, | |
210 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, | |
211 | '0 blaz.foo')), | |
212 | (dns.name.from_text('ns1', None), | |
213 | 3600, | |
214 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
215 | '10.0.0.1')), | |
216 | (dns.name.from_text('ns2', None), | |
217 | 3600, | |
218 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
219 | '10.0.0.2')), | |
220 | (dns.name.from_text('foo4', None), 3600, | |
221 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
222 | '10.0.0.4')), | |
223 | (dns.name.from_text('foo6', None), 3600, | |
224 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
225 | '10.0.0.6')), | |
226 | (dns.name.from_text('foo8', None), 3600, | |
227 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
228 | '10.0.0.8'))] | |
229 | exl.sort() | |
230 | self.failUnless(l == exl) | |
231 | def testGenerate1(self): | |
232 | z = dns.zone.from_text(example_text4, 'example.', relativize=True) | |
233 | l = list(z.iterate_rdatas()) | |
234 | l.sort() | |
235 | exl = [(dns.name.from_text('@', None), | |
236 | 3600L, | |
237 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
238 | 'ns1')), | |
239 | (dns.name.from_text('@', None), | |
240 | 3600L, | |
241 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
242 | 'ns2')), | |
243 | (dns.name.from_text('@', None), | |
244 | 3600L, | |
245 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, | |
246 | 'foo bar 1 2 3 4 5')), | |
247 | (dns.name.from_text('bar.foo', None), | |
248 | 300L, | |
249 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, | |
250 | '0 blaz.foo')), | |
251 | (dns.name.from_text('ns1', None), | |
252 | 3600L, | |
253 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
254 | '10.0.0.1')), | |
255 | (dns.name.from_text('ns2', None), | |
256 | 3600L, | |
257 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
258 | '10.0.0.2')), | |
259 | ||
260 | (dns.name.from_text('wp-db01.services.mozilla.com', None), | |
261 | 0L, | |
262 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, | |
263 | 'SERVER.FOOBAR.')), | |
264 | ||
265 | (dns.name.from_text('wp-db02.services.mozilla.com', None), | |
266 | 0L, | |
267 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, | |
268 | 'SERVER.FOOBAR.')), | |
269 | ||
270 | (dns.name.from_text('wp-db03.services.mozilla.com', None), | |
271 | 0L, | |
272 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, | |
273 | 'SERVER.FOOBAR.'))] | |
274 | exl.sort() | |
275 | self.failUnless(l == exl) | |
276 | ||
277 | def testGenerate2(self): | |
278 | z = dns.zone.from_text(example_text5, 'example.', relativize=True) | |
279 | l = list(z.iterate_rdatas()) | |
280 | l.sort() | |
281 | exl = [(dns.name.from_text('@', None), | |
282 | 3600L, | |
283 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
284 | 'ns1')), | |
285 | (dns.name.from_text('@', None), | |
286 | 3600L, | |
287 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
288 | 'ns2')), | |
289 | (dns.name.from_text('@', None), | |
290 | 3600L, | |
291 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, | |
292 | 'foo bar 1 2 3 4 5')), | |
293 | (dns.name.from_text('bar.foo', None), | |
294 | 300L, | |
295 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, | |
296 | '0 blaz.foo')), | |
297 | (dns.name.from_text('ns1', None), | |
298 | 3600L, | |
299 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
300 | '10.0.0.1')), | |
301 | (dns.name.from_text('ns2', None), | |
302 | 3600L, | |
303 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
304 | '10.0.0.2')), | |
305 | ||
306 | (dns.name.from_text('wp-db21.services.mozilla.com', None), 0L, | |
307 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, | |
308 | 'SERVER.FOOBAR.')), | |
309 | ||
310 | (dns.name.from_text('wp-db22.services.mozilla.com', None), 0L, | |
311 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, | |
312 | 'SERVER.FOOBAR.')), | |
313 | ||
314 | (dns.name.from_text('wp-db23.services.mozilla.com', None), 0L, | |
315 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, | |
316 | 'SERVER.FOOBAR.'))] | |
317 | exl.sort() | |
318 | self.failUnless(l == exl) | |
319 | ||
320 | def testGenerate3(self): | |
321 | z = dns.zone.from_text(example_text6, 'example.', relativize=True) | |
322 | l = list(z.iterate_rdatas()) | |
323 | l.sort() | |
324 | ||
325 | exl = [(dns.name.from_text('@', None), | |
326 | 3600L, | |
327 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
328 | 'ns1')), | |
329 | (dns.name.from_text('@', None), | |
330 | 3600L, | |
331 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
332 | 'ns2')), | |
333 | (dns.name.from_text('@', None), | |
334 | 3600L, | |
335 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, | |
336 | 'foo bar 1 2 3 4 5')), | |
337 | (dns.name.from_text('bar.foo', None), | |
338 | 300L, | |
339 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, | |
340 | '0 blaz.foo')), | |
341 | (dns.name.from_text('ns1', None), | |
342 | 3600L, | |
343 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
344 | '10.0.0.1')), | |
345 | (dns.name.from_text('ns2', None), | |
346 | 3600L, | |
347 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
348 | '10.0.0.2')), | |
349 | (dns.name.from_text('wp-db21.services.mozilla.com', None), 0L, | |
350 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, | |
351 | 'SERVER.FOOBAR.')), | |
352 | ||
353 | (dns.name.from_text('wp-db22.services.mozilla.com', None), 0L, | |
354 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, | |
355 | 'SERVER.FOOBAR.')), | |
356 | ||
357 | (dns.name.from_text('wp-db23.services.mozilla.com', None), 0L, | |
358 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, | |
359 | 'SERVER.FOOBAR.'))] | |
360 | exl.sort() | |
361 | self.failUnless(l == exl) | |
362 | ||
363 | def testGenerate4(self): | |
364 | z = dns.zone.from_text(example_text7, 'example.', relativize=True) | |
365 | l = list(z.iterate_rdatas()) | |
366 | l.sort() | |
367 | exl = [(dns.name.from_text('@', None), | |
368 | 3600L, | |
369 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
370 | 'ns1')), | |
371 | (dns.name.from_text('@', None), | |
372 | 3600L, | |
373 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
374 | 'ns2')), | |
375 | (dns.name.from_text('@', None), | |
376 | 3600L, | |
377 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, | |
378 | 'foo bar 1 2 3 4 5')), | |
379 | (dns.name.from_text('bar.foo', None), | |
380 | 300L, | |
381 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, | |
382 | '0 blaz.foo')), | |
383 | (dns.name.from_text('ns1', None), | |
384 | 3600L, | |
385 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
386 | '10.0.0.1')), | |
387 | (dns.name.from_text('ns2', None), | |
388 | 3600L, | |
389 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
390 | '10.0.0.2')), | |
391 | ||
392 | (dns.name.from_text('sync1.db', None), 3600L, | |
393 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
394 | '10.10.16.0')), | |
395 | ||
396 | (dns.name.from_text('sync2.db', None), 3600L, | |
397 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
398 | '10.10.16.0')), | |
399 | ||
400 | (dns.name.from_text('sync3.db', None), 3600L, | |
401 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
402 | '10.10.16.0'))] | |
403 | exl.sort() | |
404 | self.failUnless(l == exl) | |
405 | ||
406 | def testGenerate6(self): | |
407 | z = dns.zone.from_text(example_text9, 'example.', relativize=True) | |
408 | l = list(z.iterate_rdatas()) | |
409 | l.sort() | |
410 | exl = [(dns.name.from_text('@', None), | |
411 | 3600L, | |
412 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
413 | 'ns1')), | |
414 | (dns.name.from_text('@', None), | |
415 | 3600L, | |
416 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
417 | 'ns2')), | |
418 | (dns.name.from_text('@', None), | |
419 | 3600L, | |
420 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, | |
421 | 'foo bar 1 2 3 4 5')), | |
422 | (dns.name.from_text('bar.foo', None), | |
423 | 300L, | |
424 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, | |
425 | '0 blaz.foo')), | |
426 | (dns.name.from_text('ns1', None), | |
427 | 3600L, | |
428 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
429 | '10.0.0.1')), | |
430 | (dns.name.from_text('ns2', None), | |
431 | 3600L, | |
432 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
433 | '10.0.0.2')), | |
434 | ||
435 | (dns.name.from_text('wp-db01', None), 3600L, | |
436 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
437 | '10.10.16.0')), | |
438 | (dns.name.from_text('wp-db02', None), 3600L, | |
439 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
440 | '10.10.16.0')), | |
441 | ||
442 | (dns.name.from_text('sync1.db', None), 3600L, | |
443 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
444 | '10.10.16.0')), | |
445 | ||
446 | (dns.name.from_text('sync2.db', None), 3600L, | |
447 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
448 | '10.10.16.0')), | |
449 | ||
450 | (dns.name.from_text('sync3.db', None), 3600L, | |
451 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
452 | '10.10.16.0'))] | |
453 | exl.sort() | |
454 | self.failUnless(l == exl) | |
455 | ||
456 | def testGenerate7(self): | |
457 | z = dns.zone.from_text(example_text10, 'example.', relativize=True) | |
458 | l = list(z.iterate_rdatas()) | |
459 | l.sort() | |
460 | exl = [(dns.name.from_text('@', None), | |
461 | 3600L, | |
462 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
463 | 'ns1')), | |
464 | (dns.name.from_text('@', None), | |
465 | 3600L, | |
466 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, | |
467 | 'ns2')), | |
468 | (dns.name.from_text('@', None), | |
469 | 3600L, | |
470 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, | |
471 | 'foo bar 1 2 3 4 5')), | |
472 | (dns.name.from_text('bar.foo', None), | |
473 | 300L, | |
474 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, | |
475 | '0 blaz.foo')), | |
476 | (dns.name.from_text('ns1', None), | |
477 | 3600L, | |
478 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
479 | '10.0.0.1')), | |
480 | (dns.name.from_text('ns2', None), | |
481 | 3600L, | |
482 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, | |
483 | '10.0.0.2')), | |
484 | ||
485 | (dns.name.from_text('27.2', None), 3600L, | |
486 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.PTR, | |
487 | 'zlb1.oob')), | |
488 | ||
489 | (dns.name.from_text('28.2', None), 3600L, | |
490 | dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.PTR, | |
491 | 'zlb2.oob'))] | |
492 | ||
493 | exl.sort() | |
494 | self.failUnless(l == exl) | |
495 | ||
496 | ||
497 | if __name__ == '__main__': | |
498 | unittest.main() |
0 | # Copyright (C) 2003-2007, 2009-2011 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 sys | |
16 | sys.path.insert(0, '../') | |
17 | ||
18 | import cStringIO | |
19 | import filecmp | |
20 | import os | |
21 | import unittest | |
22 | ||
23 | import dns | |
24 | import dns.exception | |
25 | import dns.grange | |
26 | ||
27 | import pdb | |
28 | ||
29 | ||
30 | ||
31 | class GRangeTestCase(unittest.TestCase): | |
32 | ||
33 | def testFromText1(self): | |
34 | start, stop, step = dns.grange.from_text('1-1') | |
35 | self.assertEqual(start, 1) | |
36 | self.assertEqual(stop, 1) | |
37 | self.assertEqual(step, 1) | |
38 | ||
39 | def testFromText2(self): | |
40 | start, stop, step = dns.grange.from_text('1-4') | |
41 | self.assertEqual(start, 1) | |
42 | self.assertEqual(stop, 4) | |
43 | self.assertEqual(step, 1) | |
44 | ||
45 | def testFromText3(self): | |
46 | start, stop, step = dns.grange.from_text('4-255') | |
47 | self.assertEqual(start, 4) | |
48 | self.assertEqual(stop, 255) | |
49 | self.assertEqual(step, 1) | |
50 | ||
51 | def testFromText4(self): | |
52 | start, stop, step = dns.grange.from_text('1-1/1') | |
53 | self.assertEqual(start, 1) | |
54 | self.assertEqual(stop, 1) | |
55 | self.assertEqual(step, 1) | |
56 | ||
57 | def testFromText5(self): | |
58 | start, stop, step = dns.grange.from_text('1-4/2') | |
59 | self.assertEqual(start, 1) | |
60 | self.assertEqual(stop, 4) | |
61 | self.assertEqual(step, 2) | |
62 | ||
63 | def testFromText6(self): | |
64 | start, stop, step = dns.grange.from_text('4-255/77') | |
65 | self.assertEqual(start, 4) | |
66 | self.assertEqual(stop, 255) | |
67 | self.assertEqual(step, 77) | |
68 | ||
69 | def testFailFromText1(self): | |
70 | def bad(): | |
71 | start = 2 | |
72 | stop = 1 | |
73 | step = 1 | |
74 | dns.grange.from_text('{0}-{1}/{2}'.format(start, stop, step)) | |
75 | self.assertRaises(AssertionError, bad) | |
76 | ||
77 | def testFailFromText2(self): | |
78 | def bad(): | |
79 | start = '-1' | |
80 | stop = 3 | |
81 | step = 1 | |
82 | dns.grange.from_text('{0}-{1}/{2}'.format(start, stop, step)) | |
83 | self.assertRaises(dns.exception.SyntaxError, bad) | |
84 | ||
85 | def testFailFromText2(self): | |
86 | def bad(): | |
87 | start = 1 | |
88 | stop = 4 | |
89 | step = '-2' | |
90 | dns.grange.from_text('{0}-{1}/{2}'.format(start, stop, step)) | |
91 | self.assertRaises(dns.exception.SyntaxError, bad) | |
92 | ||
93 | if __name__ == '__main__': | |
94 | unittest.main() |
192 | 192 | for addr in addrs: |
193 | 193 | self.failUnlessRaises(dns.exception.SyntaxError, make_bad(addr)) |
194 | 194 | |
195 | def test_rfc5952_section_4_2_2(self): | |
196 | addr = '2001:db8:0:1:1:1:1:1' | |
197 | b1 = aton6(addr) | |
198 | t1 = ntoa6(b1) | |
199 | self.failUnless(t1 == addr) | |
200 | ||
195 | 201 | if __name__ == '__main__': |
196 | 202 | unittest.main() |