1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """DNS stub resolver.
17
18 @var default_resolver: The default resolver object
19 @type default_resolver: dns.resolver.Resolver object"""
20
21 import socket
22 import sys
23 import time
24
25 import dns.exception
26 import dns.message
27 import dns.name
28 import dns.query
29 import dns.rcode
30 import dns.rdataclass
31 import dns.rdatatype
32
33 if sys.platform == 'win32':
34 import _winreg
35
36 -class NXDOMAIN(dns.exception.DNSException):
37 """The query name does not exist."""
38 pass
39
40
41
42
43
44 Timeout = dns.exception.Timeout
45
46 -class NoAnswer(dns.exception.DNSException):
47 """The response did not contain an answer to the question."""
48 pass
49
51 """No non-broken nameservers are available to answer the query."""
52 pass
53
55 """Raised if an absolute domain name is required but a relative name
56 was provided."""
57 pass
58
60 """Raised if for some reason there is no SOA at the root name.
61 This should never happen!"""
62 pass
63
67
68
70 """DNS stub resolver answer
71
72 Instances of this class bundle up the result of a successful DNS
73 resolution.
74
75 For convenience, the answer object implements much of the sequence
76 protocol, forwarding to its rrset. E.g. "for a in answer" is
77 equivalent to "for a in answer.rrset", "answer[i]" is equivalent
78 to "answer.rrset[i]", and "answer[i:j]" is equivalent to
79 "answer.rrset[i:j]".
80
81 Note that CNAMEs or DNAMEs in the response may mean that answer
82 node's name might not be the query name.
83
84 @ivar qname: The query name
85 @type qname: dns.name.Name object
86 @ivar rdtype: The query type
87 @type rdtype: int
88 @ivar rdclass: The query class
89 @type rdclass: int
90 @ivar response: The response message
91 @type response: dns.message.Message object
92 @ivar rrset: The answer
93 @type rrset: dns.rrset.RRset object
94 @ivar expiration: The time when the answer expires
95 @type expiration: float (seconds since the epoch)
96 @ivar canonical_name: The canonical name of the query name
97 @type canonical_name: dns.name.Name object
98 """
99 - def __init__(self, qname, rdtype, rdclass, response,
100 raise_on_no_answer=True):
101 self.qname = qname
102 self.rdtype = rdtype
103 self.rdclass = rdclass
104 self.response = response
105 min_ttl = -1
106 rrset = None
107 for count in xrange(0, 15):
108 try:
109 rrset = response.find_rrset(response.answer, qname,
110 rdclass, rdtype)
111 if min_ttl == -1 or rrset.ttl < min_ttl:
112 min_ttl = rrset.ttl
113 break
114 except KeyError:
115 if rdtype != dns.rdatatype.CNAME:
116 try:
117 crrset = response.find_rrset(response.answer,
118 qname,
119 rdclass,
120 dns.rdatatype.CNAME)
121 if min_ttl == -1 or crrset.ttl < min_ttl:
122 min_ttl = crrset.ttl
123 for rd in crrset:
124 qname = rd.target
125 break
126 continue
127 except KeyError:
128 if raise_on_no_answer:
129 raise NoAnswer
130 if raise_on_no_answer:
131 raise NoAnswer
132 if rrset is None and raise_on_no_answer:
133 raise NoAnswer
134 self.canonical_name = qname
135 self.rrset = rrset
136 if rrset is None:
137 while 1:
138
139
140 try:
141 srrset = response.find_rrset(response.authority, qname,
142 rdclass, dns.rdatatype.SOA)
143 if min_ttl == -1 or srrset.ttl < min_ttl:
144 min_ttl = srrset.ttl
145 if srrset[0].minimum < min_ttl:
146 min_ttl = srrset[0].minimum
147 break
148 except KeyError:
149 try:
150 qname = qname.parent()
151 except dns.name.NoParent:
152 break
153 self.expiration = time.time() + min_ttl
154
156 if attr == 'name':
157 return self.rrset.name
158 elif attr == 'ttl':
159 return self.rrset.ttl
160 elif attr == 'covers':
161 return self.rrset.covers
162 elif attr == 'rdclass':
163 return self.rrset.rdclass
164 elif attr == 'rdtype':
165 return self.rrset.rdtype
166 else:
167 raise AttributeError(attr)
168
170 return len(self.rrset)
171
173 return iter(self.rrset)
174
177
180
182 return self.rrset[i:j]
183
186
188 """Simple DNS answer cache.
189
190 @ivar data: A dictionary of cached data
191 @type data: dict
192 @ivar cleaning_interval: The number of seconds between cleanings. The
193 default is 300 (5 minutes).
194 @type cleaning_interval: float
195 @ivar next_cleaning: The time the cache should next be cleaned (in seconds
196 since the epoch.)
197 @type next_cleaning: float
198 """
199
200 - def __init__(self, cleaning_interval=300.0):
201 """Initialize a DNS cache.
202
203 @param cleaning_interval: the number of seconds between periodic
204 cleanings. The default is 300.0
205 @type cleaning_interval: float.
206 """
207
208 self.data = {}
209 self.cleaning_interval = cleaning_interval
210 self.next_cleaning = time.time() + self.cleaning_interval
211
213 """Clean the cache if it's time to do so."""
214
215 now = time.time()
216 if self.next_cleaning <= now:
217 keys_to_delete = []
218 for (k, v) in self.data.iteritems():
219 if v.expiration <= now:
220 keys_to_delete.append(k)
221 for k in keys_to_delete:
222 del self.data[k]
223 now = time.time()
224 self.next_cleaning = now + self.cleaning_interval
225
226 - def get(self, key):
227 """Get the answer associated with I{key}. Returns None if
228 no answer is cached for the key.
229 @param key: the key
230 @type key: (dns.name.Name, int, int) tuple whose values are the
231 query name, rdtype, and rdclass.
232 @rtype: dns.resolver.Answer object or None
233 """
234
235 self.maybe_clean()
236 v = self.data.get(key)
237 if v is None or v.expiration <= time.time():
238 return None
239 return v
240
241 - def put(self, key, value):
242 """Associate key and value in the cache.
243 @param key: the key
244 @type key: (dns.name.Name, int, int) tuple whose values are the
245 query name, rdtype, and rdclass.
246 @param value: The answer being cached
247 @type value: dns.resolver.Answer object
248 """
249
250 self.maybe_clean()
251 self.data[key] = value
252
253 - def flush(self, key=None):
254 """Flush the cache.
255
256 If I{key} is specified, only that item is flushed. Otherwise
257 the entire cache is flushed.
258
259 @param key: the key to flush
260 @type key: (dns.name.Name, int, int) tuple or None
261 """
262
263 if not key is None:
264 if self.data.has_key(key):
265 del self.data[key]
266 else:
267 self.data = {}
268 self.next_cleaning = time.time() + self.cleaning_interval
269
271 """DNS stub resolver
272
273 @ivar domain: The domain of this host
274 @type domain: dns.name.Name object
275 @ivar nameservers: A list of nameservers to query. Each nameserver is
276 a string which contains the IP address of a nameserver.
277 @type nameservers: list of strings
278 @ivar search: The search list. If the query name is a relative name,
279 the resolver will construct an absolute query name by appending the search
280 names one by one to the query name.
281 @type search: list of dns.name.Name objects
282 @ivar port: The port to which to send queries. The default is 53.
283 @type port: int
284 @ivar timeout: The number of seconds to wait for a response from a
285 server, before timing out.
286 @type timeout: float
287 @ivar lifetime: The total number of seconds to spend trying to get an
288 answer to the question. If the lifetime expires, a Timeout exception
289 will occur.
290 @type lifetime: float
291 @ivar keyring: The TSIG keyring to use. The default is None.
292 @type keyring: dict
293 @ivar keyname: The TSIG keyname to use. The default is None.
294 @type keyname: dns.name.Name object
295 @ivar keyalgorithm: The TSIG key algorithm to use. The default is
296 dns.tsig.default_algorithm.
297 @type keyalgorithm: string
298 @ivar edns: The EDNS level to use. The default is -1, no Edns.
299 @type edns: int
300 @ivar ednsflags: The EDNS flags
301 @type ednsflags: int
302 @ivar payload: The EDNS payload size. The default is 0.
303 @type payload: int
304 @ivar cache: The cache to use. The default is None.
305 @type cache: dns.resolver.Cache object
306 """
307 - def __init__(self, filename='/etc/resolv.conf', configure=True):
308 """Initialize a resolver instance.
309
310 @param filename: The filename of a configuration file in
311 standard /etc/resolv.conf format. This parameter is meaningful
312 only when I{configure} is true and the platform is POSIX.
313 @type filename: string or file object
314 @param configure: If True (the default), the resolver instance
315 is configured in the normal fashion for the operating system
316 the resolver is running on. (I.e. a /etc/resolv.conf file on
317 POSIX systems and from the registry on Windows systems.)
318 @type configure: bool"""
319
320 self.reset()
321 if configure:
322 if sys.platform == 'win32':
323 self.read_registry()
324 elif filename:
325 self.read_resolv_conf(filename)
326
328 """Reset all resolver configuration to the defaults."""
329 self.domain = \
330 dns.name.Name(dns.name.from_text(socket.gethostname())[1:])
331 if len(self.domain) == 0:
332 self.domain = dns.name.root
333 self.nameservers = []
334 self.search = []
335 self.port = 53
336 self.timeout = 2.0
337 self.lifetime = 30.0
338 self.keyring = None
339 self.keyname = None
340 self.keyalgorithm = dns.tsig.default_algorithm
341 self.edns = -1
342 self.ednsflags = 0
343 self.payload = 0
344 self.cache = None
345
347 """Process f as a file in the /etc/resolv.conf format. If f is
348 a string, it is used as the name of the file to open; otherwise it
349 is treated as the file itself."""
350 if isinstance(f, str) or isinstance(f, unicode):
351 try:
352 f = open(f, 'r')
353 except IOError:
354
355
356 self.nameservers = ['127.0.0.1']
357 return
358 want_close = True
359 else:
360 want_close = False
361 try:
362 for l in f:
363 if len(l) == 0 or l[0] == '#' or l[0] == ';':
364 continue
365 tokens = l.split()
366 if len(tokens) == 0:
367 continue
368 if tokens[0] == 'nameserver':
369 self.nameservers.append(tokens[1])
370 elif tokens[0] == 'domain':
371 self.domain = dns.name.from_text(tokens[1])
372 elif tokens[0] == 'search':
373 for suffix in tokens[1:]:
374 self.search.append(dns.name.from_text(suffix))
375 finally:
376 if want_close:
377 f.close()
378 if len(self.nameservers) == 0:
379 self.nameservers.append('127.0.0.1')
380
382
383
384
385
386
387 if entry.find(' ') >= 0:
388 split_char = ' '
389 elif entry.find(',') >= 0:
390 split_char = ','
391 else:
392
393 split_char = ' '
394 return split_char
395
397 """Configure a NameServer registry entry."""
398
399 nameservers = str(nameservers)
400 split_char = self._determine_split_char(nameservers)
401 ns_list = nameservers.split(split_char)
402 for ns in ns_list:
403 if not ns in self.nameservers:
404 self.nameservers.append(ns)
405
406 - def _config_win32_domain(self, domain):
407 """Configure a Domain registry entry."""
408
409 self.domain = dns.name.from_text(str(domain))
410
412 """Configure a Search registry entry."""
413
414 search = str(search)
415 split_char = self._determine_split_char(search)
416 search_list = search.split(split_char)
417 for s in search_list:
418 if not s in self.search:
419 self.search.append(dns.name.from_text(s))
420
422 """Extract DNS info from a registry key."""
423 try:
424 servers, rtype = _winreg.QueryValueEx(key, 'NameServer')
425 except WindowsError:
426 servers = None
427 if servers:
428 self._config_win32_nameservers(servers)
429 try:
430 dom, rtype = _winreg.QueryValueEx(key, 'Domain')
431 if dom:
432 self._config_win32_domain(dom)
433 except WindowsError:
434 pass
435 else:
436 try:
437 servers, rtype = _winreg.QueryValueEx(key, 'DhcpNameServer')
438 except WindowsError:
439 servers = None
440 if servers:
441 self._config_win32_nameservers(servers)
442 try:
443 dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain')
444 if dom:
445 self._config_win32_domain(dom)
446 except WindowsError:
447 pass
448 try:
449 search, rtype = _winreg.QueryValueEx(key, 'SearchList')
450 except WindowsError:
451 search = None
452 if search:
453 self._config_win32_search(search)
454
456 """Extract resolver configuration from the Windows registry."""
457 lm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
458 want_scan = False
459 try:
460 try:
461
462 tcp_params = _winreg.OpenKey(lm,
463 r'SYSTEM\CurrentControlSet'
464 r'\Services\Tcpip\Parameters')
465 want_scan = True
466 except EnvironmentError:
467
468 tcp_params = _winreg.OpenKey(lm,
469 r'SYSTEM\CurrentControlSet'
470 r'\Services\VxD\MSTCP')
471 try:
472 self._config_win32_fromkey(tcp_params)
473 finally:
474 tcp_params.Close()
475 if want_scan:
476 interfaces = _winreg.OpenKey(lm,
477 r'SYSTEM\CurrentControlSet'
478 r'\Services\Tcpip\Parameters'
479 r'\Interfaces')
480 try:
481 i = 0
482 while True:
483 try:
484 guid = _winreg.EnumKey(interfaces, i)
485 i += 1
486 key = _winreg.OpenKey(interfaces, guid)
487 if not self._win32_is_nic_enabled(lm, guid, key):
488 continue
489 try:
490 self._config_win32_fromkey(key)
491 finally:
492 key.Close()
493 except EnvironmentError:
494 break
495 finally:
496 interfaces.Close()
497 finally:
498 lm.Close()
499
501
502
503
504
505
506 try:
507
508
509 connection_key = _winreg.OpenKey(
510 lm,
511 r'SYSTEM\CurrentControlSet\Control\Network'
512 r'\{4D36E972-E325-11CE-BFC1-08002BE10318}'
513 r'\%s\Connection' % guid)
514
515 try:
516
517 (pnp_id, ttype) = _winreg.QueryValueEx(
518 connection_key, 'PnpInstanceID')
519
520 if ttype != _winreg.REG_SZ:
521 raise ValueError
522
523 device_key = _winreg.OpenKey(
524 lm, r'SYSTEM\CurrentControlSet\Enum\%s' % pnp_id)
525
526 try:
527
528 (flags, ttype) = _winreg.QueryValueEx(
529 device_key, 'ConfigFlags')
530
531 if ttype != _winreg.REG_DWORD:
532 raise ValueError
533
534
535
536 return not (flags & 0x1)
537
538 finally:
539 device_key.Close()
540 finally:
541 connection_key.Close()
542 except (EnvironmentError, ValueError):
543
544
545
546
547
548 try:
549 (nte, ttype) = _winreg.QueryValueEx(interface_key,
550 'NTEContextList')
551 return nte is not None
552 except WindowsError:
553 return False
554
556 now = time.time()
557 if now < start:
558 if start - now > 1:
559
560 raise Timeout
561 else:
562
563
564
565 now = start
566 duration = now - start
567 if duration >= self.lifetime:
568 raise Timeout
569 return min(self.lifetime - duration, self.timeout)
570
573 """Query nameservers to find the answer to the question.
574
575 The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects
576 of the appropriate type, or strings that can be converted into objects
577 of the appropriate type. E.g. For I{rdtype} the integer 2 and the
578 the string 'NS' both mean to query for records with DNS rdata type NS.
579
580 @param qname: the query name
581 @type qname: dns.name.Name object or string
582 @param rdtype: the query type
583 @type rdtype: int or string
584 @param rdclass: the query class
585 @type rdclass: int or string
586 @param tcp: use TCP to make the query (default is False).
587 @type tcp: bool
588 @param source: bind to this IP address (defaults to machine default IP).
589 @type source: IP address in dotted quad notation
590 @param raise_on_no_answer: raise NoAnswer if there's no answer
591 (defaults is True).
592 @type raise_on_no_answer: bool
593 @rtype: dns.resolver.Answer instance
594 @raises Timeout: no answers could be found in the specified lifetime
595 @raises NXDOMAIN: the query name does not exist
596 @raises NoAnswer: the response did not contain an answer and
597 raise_on_no_answer is True.
598 @raises NoNameservers: no non-broken nameservers are available to
599 answer the question."""
600
601 if isinstance(qname, (str, unicode)):
602 qname = dns.name.from_text(qname, None)
603 if isinstance(rdtype, (str, unicode)):
604 rdtype = dns.rdatatype.from_text(rdtype)
605 if dns.rdatatype.is_metatype(rdtype):
606 raise NoMetaqueries
607 if isinstance(rdclass, (str, unicode)):
608 rdclass = dns.rdataclass.from_text(rdclass)
609 if dns.rdataclass.is_metaclass(rdclass):
610 raise NoMetaqueries
611 qnames_to_try = []
612 if qname.is_absolute():
613 qnames_to_try.append(qname)
614 else:
615 if len(qname) > 1:
616 qnames_to_try.append(qname.concatenate(dns.name.root))
617 if self.search:
618 for suffix in self.search:
619 qnames_to_try.append(qname.concatenate(suffix))
620 else:
621 qnames_to_try.append(qname.concatenate(self.domain))
622 all_nxdomain = True
623 start = time.time()
624 for qname in qnames_to_try:
625 if self.cache:
626 answer = self.cache.get((qname, rdtype, rdclass))
627 if answer:
628 return answer
629 request = dns.message.make_query(qname, rdtype, rdclass)
630 if not self.keyname is None:
631 request.use_tsig(self.keyring, self.keyname,
632 algorithm=self.keyalgorithm)
633 request.use_edns(self.edns, self.ednsflags, self.payload)
634 response = None
635
636
637
638 nameservers = self.nameservers[:]
639 backoff = 0.10
640 while response is None:
641 if len(nameservers) == 0:
642 raise NoNameservers
643 for nameserver in nameservers[:]:
644 timeout = self._compute_timeout(start)
645 try:
646 if tcp:
647 response = dns.query.tcp(request, nameserver,
648 timeout, self.port,
649 source=source)
650 else:
651 response = dns.query.udp(request, nameserver,
652 timeout, self.port,
653 source=source)
654 except (socket.error, dns.exception.Timeout):
655
656
657
658
659 response = None
660 continue
661 except dns.query.UnexpectedSource:
662
663
664
665 response = None
666 continue
667 except dns.exception.FormError:
668
669
670
671
672
673 nameservers.remove(nameserver)
674 response = None
675 continue
676 rcode = response.rcode()
677 if rcode == dns.rcode.NOERROR or \
678 rcode == dns.rcode.NXDOMAIN:
679 break
680
681
682
683
684
685 if rcode != dns.rcode.SERVFAIL:
686 nameservers.remove(nameserver)
687 response = None
688 if not response is None:
689 break
690
691
692
693 if len(nameservers) > 0:
694
695
696
697
698 timeout = self._compute_timeout(start)
699 sleep_time = min(timeout, backoff)
700 backoff *= 2
701 time.sleep(sleep_time)
702 if response.rcode() == dns.rcode.NXDOMAIN:
703 continue
704 all_nxdomain = False
705 break
706 if all_nxdomain:
707 raise NXDOMAIN
708 answer = Answer(qname, rdtype, rdclass, response,
709 raise_on_no_answer)
710 if self.cache:
711 self.cache.put((qname, rdtype, rdclass), answer)
712 return answer
713
716 """Add a TSIG signature to the query.
717
718 @param keyring: The TSIG keyring to use; defaults to None.
719 @type keyring: dict
720 @param keyname: The name of the TSIG key to use; defaults to None.
721 The key must be defined in the keyring. If a keyring is specified
722 but a keyname is not, then the key used will be the first key in the
723 keyring. Note that the order of keys in a dictionary is not defined,
724 so applications should supply a keyname when a keyring is used, unless
725 they know the keyring contains only one key.
726 @param algorithm: The TSIG key algorithm to use. The default
727 is dns.tsig.default_algorithm.
728 @type algorithm: string"""
729 self.keyring = keyring
730 if keyname is None:
731 self.keyname = self.keyring.keys()[0]
732 else:
733 self.keyname = keyname
734 self.keyalgorithm = algorithm
735
736 - def use_edns(self, edns, ednsflags, payload):
737 """Configure Edns.
738
739 @param edns: The EDNS level to use. The default is -1, no Edns.
740 @type edns: int
741 @param ednsflags: The EDNS flags
742 @type ednsflags: int
743 @param payload: The EDNS payload size. The default is 0.
744 @type payload: int"""
745
746 if edns is None:
747 edns = -1
748 self.edns = edns
749 self.ednsflags = ednsflags
750 self.payload = payload
751
752 default_resolver = None
753
760
763 """Query nameservers to find the answer to the question.
764
765 This is a convenience function that uses the default resolver
766 object to make the query.
767 @see: L{dns.resolver.Resolver.query} for more information on the
768 parameters."""
769 return get_default_resolver().query(qname, rdtype, rdclass, tcp, source,
770 raise_on_no_answer)
771
803