Codebase list dnspython / 62190c2
Imported Upstream version 1.11.0 SVN-Git Migration 8 years ago
27 changed file(s) with 1242 addition(s) and 171 deletion(s). Raw diff Collapse all Expand all
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
044 2012-04-08 Bob Halley <halley@dnspython.org>
145
246 * (Version 1.10.0 released)
00 Metadata-Version: 1.1
11 Name: dnspython
2 Version: 1.10.0
2 Version: 1.11.0
33 Summary: DNS toolkit
44 Home-page: http://www.dnspython.org
55 Author: Bob Halley
66 Author-email: halley@dnspython.org
77 License: BSD-like
8 Download-URL: http://www.dnspython.org/kits/1.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
99 Description: dnspython is a DNS toolkit for Python. It supports almost all
1010 record types. It can be used for queries, zone transfers, and dynamic
1111 updates. It supports TSIG authenticated messages and EDNS0.
2121
2222 ABOUT THIS RELEASE
2323
24 This is dnspython 1.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.
2549
2650 New since 1.9.4:
2751
125125 return dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.DS, dsrdata, 0,
126126 len(dsrdata))
127127
128 def _find_key(keys, rrsig):
128 def _find_candidate_keys(keys, rrsig):
129 candidate_keys=[]
129130 value = keys.get(rrsig.signer)
130131 if value is None:
131132 return None
140141 for rdata in rdataset:
141142 if rdata.algorithm == rrsig.algorithm and \
142143 key_id(rdata) == rrsig.key_tag:
143 return rdata
144 return None
144 candidate_keys.append(rdata)
145 return candidate_keys
145146
146147 def _is_rsa(algorithm):
147148 return algorithm in (RSAMD5, RSASHA1,
216217 if isinstance(origin, (str, unicode)):
217218 origin = dns.name.from_text(origin, dns.name.root)
218219
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'
313315
314316 def _validate(rrset, rrsigset, keys, origin=None, now=None):
315317 """Validate an RRset
4343 @type current: int
4444 @param olen: The length of the wire-format option data
4545 @type olen: int
46 @rtype: dns.ends.Option instance"""
46 @rtype: dns.edns.Option instance"""
4747 raise NotImplementedError
4848
4949 from_wire = classmethod(from_wire)
5050
5151 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.
5353 Return < 0 if self < other, 0 if self == other, and > 0 if self > other.
5454 """
5555 raise NotImplementedError
135135 @type current: int
136136 @param olen: The length of the wire-format option data
137137 @type olen: int
138 @rtype: dns.ends.Option instance"""
138 @rtype: dns.edns.Option instance"""
139139
140140 cls = get_option_class(otype)
141141 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)
7171 if current_len > best_len:
7272 best_start = start
7373 best_len = current_len
74 if best_len > 0:
74 if best_len > 1:
7575 if best_start == 0 and \
7676 (best_len == 6 or
7777 best_len == 5 and chunks[5] == 'ffff'):
418418 else:
419419 if not compress is None and len(n) > 1:
420420 pos = file.tell()
421 if pos < 0xc000:
421 if pos <= 0x3fff:
422422 compress[n] = pos
423423 l = len(label)
424424 file.write(chr(l))
4848
4949 s = StringIO.StringIO()
5050 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)
5253 return s.getvalue()[:-1]
5354
5455 def __repr__(self):
338338 dns.rdatatype.AXFR.
339339 @type rdtype: int or string
340340 @param rdclass: The class of the zone transfer. The default is
341 dns.rdatatype.IN.
341 dns.rdataclass.IN.
342342 @type rdclass: int or string
343343 @param timeout: The number of seconds to wait for each response message.
344344 If None, the default, wait forever.
7777 DHCID = 49
7878 NSEC3 = 50
7979 NSEC3PARAM = 51
80 TLSA = 52
8081 HIP = 55
8182 SPF = 99
8283 UNSPEC = 103
139140 'DHCID' : DHCID,
140141 'NSEC3' : NSEC3,
141142 'NSEC3PARAM' : NSEC3PARAM,
143 'TLSA' : TLSA,
142144 'HIP' : HIP,
143145 'SPF' : SPF,
144146 'UNSPEC' : UNSPEC,
113113 prior_rdtype = nrdtype
114114 new_window = nrdtype // 256
115115 if new_window != window:
116 windows.append((window, ''.join(bitmap[0:octets])))
116 if octets != 0:
117 windows.append((window, ''.join(bitmap[0:octets])))
117118 bitmap = ['\0'] * 32
118119 window = new_window
119120 offset = nrdtype % 256
121122 bit = offset % 8
122123 octets = byte + 1
123124 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])))
125127 return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, windows)
126128
127129 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
3232 'NSEC',
3333 'NSEC3',
3434 'NSEC3PARAM',
35 'TLSA',
3536 'PTR',
3637 'RP',
3738 'RRSIG',
304304 def write_header(self):
305305 """Write the DNS message header.
306306
307 Writing the DNS message header is done asfter all sections
307 Writing the DNS message header is done after all sections
308308 have been rendered, but before the optional TSIG signature
309309 is added.
310310 """
2121 import sys
2222 import time
2323
24 try:
25 import threading as _threading
26 except ImportError:
27 import dummy_threading as _threading
28
2429 import dns.exception
2530 import dns.flags
2631 import dns.ipv4
3843
3944 class NXDOMAIN(dns.exception.DNSException):
4045 """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."""
4150 pass
4251
4352 # The definition of the Timeout exception has moved from here to the
211220 self.data = {}
212221 self.cleaning_interval = cleaning_interval
213222 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):
216226 """Clean the cache if it's time to do so."""
217227
218228 now = time.time()
235245 @rtype: dns.resolver.Answer object or None
236246 """
237247
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()
243257
244258 def put(self, key, value):
245259 """Associate key and value in the cache.
250264 @type value: dns.resolver.Answer object
251265 """
252266
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()
255273
256274 def flush(self, key=None):
257275 """Flush the cache.
263281 @type key: (dns.name.Name, int, int) tuple or None
264282 """
265283
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()
272294
273295 class LRUCacheNode(object):
274296 """LRUCache node.
321343 self.data = {}
322344 self.set_max_size(max_size)
323345 self.sentinel = LRUCacheNode(None, None)
346 self.lock = _threading.Lock()
324347
325348 def set_max_size(self, max_size):
326349 if max_size < 1:
335358 query name, rdtype, and rdclass.
336359 @rtype: dns.resolver.Answer object or None
337360 """
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()
349376
350377 def put(self, key, value):
351378 """Associate key and value in the cache.
355382 @param value: The answer being cached
356383 @type value: dns.resolver.Answer object
357384 """
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()
380387 node = self.data.get(key)
381388 if not node is None:
382389 node.unlink()
383390 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()
392427
393428 class Resolver(object):
394429 """DNS stub resolver
424459 @type ednsflags: int
425460 @ivar payload: The EDNS payload size. The default is 0.
426461 @type payload: int
462 @ivar flags: The message flags to use. The default is None (i.e. not overwritten)
463 @type flags: int
427464 @ivar cache: The cache to use. The default is None.
428465 @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
429469 """
430470 def __init__(self, filename='/etc/resolv.conf', configure=True):
431471 """Initialize a resolver instance.
465505 self.ednsflags = 0
466506 self.payload = 0
467507 self.cache = None
508 self.flags = None
509 self.retry_servfail = False
468510
469511 def read_resolv_conf(self, f):
470512 """Process f as a file in the /etc/resolv.conf format. If f is
719761 @rtype: dns.resolver.Answer instance
720762 @raises Timeout: no answers could be found in the specified lifetime
721763 @raises NXDOMAIN: the query name does not exist
764 @raises YXDOMAIN: the query name is too long after DNAME substitution
722765 @raises NoAnswer: the response did not contain an answer and
723766 raise_on_no_answer is True.
724767 @raises NoNameservers: no non-broken nameservers are available to
760803 request.use_tsig(self.keyring, self.keyname,
761804 algorithm=self.keyalgorithm)
762805 request.use_edns(self.edns, self.ednsflags, self.payload)
806 if self.flags is not None:
807 request.flags = self.flags
763808 response = None
764809 #
765810 # make a copy of the servers list so we can alter it later.
822867 response = None
823868 continue
824869 rcode = response.rcode()
870 if rcode == dns.rcode.YXDOMAIN:
871 raise YXDOMAIN
825872 if rcode == dns.rcode.NOERROR or \
826873 rcode == dns.rcode.NXDOMAIN:
827874 break
830877 # rcode in it. Remove the server from the mix if
831878 # the rcode isn't SERVFAIL.
832879 #
833 if rcode != dns.rcode.SERVFAIL:
880 if rcode != dns.rcode.SERVFAIL or not retry_servfail:
834881 nameservers.remove(nameserver)
835882 response = None
836883 if not response is None:
896943 self.edns = edns
897944 self.ednsflags = ednsflags
898945 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
899953
900954 default_resolver = None
901955
1515 """dnspython release version information."""
1616
1717 MAJOR = 1
18 MINOR = 10
18 MINOR = 11
1919 MICRO = 0
2020 RELEASELEVEL = 0x0f
2121 SERIAL = 0
1717 from __future__ import generators
1818
1919 import sys
20 import re
2021
2122 import dns.exception
2223 import dns.name
2728 import dns.rrset
2829 import dns.tokenizer
2930 import dns.ttl
31 import dns.grange
32
3033
3134 class BadZone(dns.exception.DNSException):
3235 """The zone is malformed."""
639642 covers = rd.covers()
640643 rds = n.find_rdataset(rdclass, rdtype, covers, True)
641644 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)
642805
643806 def read(self):
644807 """Read a DNS master file and build a zone object.
681844 self.zone.origin = self.current_origin
682845 elif u == '$INCLUDE' and self.allow_include:
683846 token = self.tok.get()
684 if not token.is_quoted_string():
685 raise dns.exception.SyntaxError("bad filename in $INCLUDE")
686847 filename = token.value
687848 token = self.tok.get()
688849 if token.is_identifier():
702863 self.tok = dns.tokenizer.Tokenizer(self.current_file,
703864 filename)
704865 self.current_origin = new_origin
866 elif u == '$GENERATE':
867 self._generate_line()
705868 else:
706869 raise dns.exception.SyntaxError("Unknown master file directive '" + u + "'")
707870 continue
1717 import sys
1818 from distutils.core import setup
1919
20 version = '1.10.0'
20 version = '1.11.0'
2121
2222 kwargs = {
2323 'name' : 'dnspython',
3939 ttl = dns.ttl.from_text("2147483648")
4040 self.failUnlessRaises(dns.ttl.BadTTL, bad)
4141
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
4247 if __name__ == '__main__':
4348 unittest.main()
2626 abs_keys = { abs_dnspython_org :
2727 dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'DNSKEY',
2828 '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',
2936 '256 3 5 AwEAAdSSghOGjU33IQZgwZM2Hh771VGXX05olJK49FxpSyuEAjDBXY58 LGU9R2Zgeecnk/b9EAhFu/vCV9oECtiTCvwuVAkt9YEweqYDluQInmgP NGMJCKdSLlnX93DkjDw8rMYv5dqXCuSGPlKChfTJOLQxIAxGloS7lL+c 0CTZydAF')
3037 }
3138
94101 def testAbsoluteRSAGood(self):
95102 dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys, None, when)
96103
104 def testDuplicateKeytag(self):
105 dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys_duplicate_keytag, None, when)
106
97107 def testAbsoluteRSABad(self):
98108 def bad():
99109 dns.dnssec.validate(abs_other_soa, abs_soa_rrsig, abs_keys, None,
164164 $TTL 301 ; 5 minutes 1 second
165165 t A 73.80.65.49
166166 $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
167170 txt01 TXT "foo"
168171 txt02 TXT "foo" "bar"
169172 txt03 TXT "foo"
8989 srv02 3600 IN SRV 65535 65535 65535 old-slow-box.example.com.
9090 sshfp1 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab
9191 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
9295 txt01 3600 IN TXT "foo"
9396 txt02 3600 IN TXT "foo" "bar"
9497 txt03 3600 IN TXT "foo"
8989 srv02.example. 3600 IN SRV 65535 65535 65535 old-slow-box.example.com.
9090 sshfp1.example. 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab
9191 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
9295 txt01.example. 3600 IN TXT "foo"
9396 txt02.example. 3600 IN TXT "foo" "bar"
9497 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()
192192 for addr in addrs:
193193 self.failUnlessRaises(dns.exception.SyntaxError, make_bad(addr))
194194
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
195201 if __name__ == '__main__':
196202 unittest.main()