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