Package dns :: Module resolver
[hide private]
[frames] | no frames]

Source Code for Module dns.resolver

   1  # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. 
   2  # 
   3  # Permission to use, copy, modify, and distribute this software and its 
   4  # documentation for any purpose with or without fee is hereby granted, 
   5  # provided that the above copyright notice and this permission notice 
   6  # appear in all copies. 
   7  # 
   8  # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES 
   9  # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 
  10  # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR 
  11  # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
  12  # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
  13  # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 
  14  # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
  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  import random 
  25   
  26  try: 
  27      import threading as _threading 
  28  except ImportError: 
  29      import dummy_threading as _threading 
  30   
  31  import dns.exception 
  32  import dns.flags 
  33  import dns.ipv4 
  34  import dns.ipv6 
  35  import dns.message 
  36  import dns.name 
  37  import dns.query 
  38  import dns.rcode 
  39  import dns.rdataclass 
  40  import dns.rdatatype 
  41  import dns.reversename 
  42  import dns.tsig 
  43  from ._compat import xrange, string_types 
  44   
  45  if sys.platform == 'win32': 
  46      try: 
  47          import winreg as _winreg 
  48      except ImportError: 
  49          import _winreg 
  50   
51 -class NXDOMAIN(dns.exception.DNSException):
52 53 """The DNS query name does not exist.""" 54 supp_kwargs = set(['qname']) 55
56 - def __str__(self):
57 if 'qname' not in self.kwargs: 58 return super(NXDOMAIN, self).__str__() 59 60 qname = self.kwargs['qname'] 61 msg = self.__doc__[:-1] 62 if isinstance(qname, (list, set)): 63 if len(qname) > 1: 64 msg = 'None of DNS query names exist' 65 qname = list(map(str, qname)) 66 else: 67 qname = qname[0] 68 return "%s: %s" % (msg, (str(qname)))
69 70
71 -class YXDOMAIN(dns.exception.DNSException):
72 73 """The DNS query name is too long after DNAME substitution."""
74 75 # The definition of the Timeout exception has moved from here to the 76 # dns.exception module. We keep dns.resolver.Timeout defined for 77 # backwards compatibility. 78 79 Timeout = dns.exception.Timeout 80 81
82 -class NoAnswer(dns.exception.DNSException):
83 84 """The DNS response does not contain an answer to the question.""" 85 fmt = 'The DNS response does not contain an answer ' + \ 86 'to the question: {query}' 87 supp_kwargs = set(['response']) 88
89 - def _fmt_kwargs(self, **kwargs):
90 return super(NoAnswer, self)._fmt_kwargs( 91 query=kwargs['response'].question)
92 93
94 -class NoNameservers(dns.exception.DNSException):
95 96 """All nameservers failed to answer the query. 97 98 @param errors: list of servers and respective errors 99 @type errors: [(server ip address, any object convertible to string)] 100 Non-empty errors list will add explanatory message () 101 """ 102 103 msg = "All nameservers failed to answer the query." 104 fmt = "%s {query}: {errors}" % msg[:-1] 105 supp_kwargs = set(['request', 'errors']) 106
107 - def _fmt_kwargs(self, **kwargs):
108 srv_msgs = [] 109 for err in kwargs['errors']: 110 srv_msgs.append('Server %s %s port %s answered %s' % (err[0], 111 'TCP' if err[1] else 'UDP', err[2], err[3])) 112 return super(NoNameservers, self)._fmt_kwargs( 113 query=kwargs['request'].question, errors='; '.join(srv_msgs))
114 115
116 -class NotAbsolute(dns.exception.DNSException):
117 118 """An absolute domain name is required but a relative name was provided."""
119 120
121 -class NoRootSOA(dns.exception.DNSException):
122 123 """There is no SOA RR at the DNS root name. This should never happen!"""
124 125
126 -class NoMetaqueries(dns.exception.DNSException):
127 128 """DNS metaqueries are not allowed."""
129 130
131 -class Answer(object):
132 133 """DNS stub resolver answer 134 135 Instances of this class bundle up the result of a successful DNS 136 resolution. 137 138 For convenience, the answer object implements much of the sequence 139 protocol, forwarding to its rrset. E.g. "for a in answer" is 140 equivalent to "for a in answer.rrset", "answer[i]" is equivalent 141 to "answer.rrset[i]", and "answer[i:j]" is equivalent to 142 "answer.rrset[i:j]". 143 144 Note that CNAMEs or DNAMEs in the response may mean that answer 145 node's name might not be the query name. 146 147 @ivar qname: The query name 148 @type qname: dns.name.Name object 149 @ivar rdtype: The query type 150 @type rdtype: int 151 @ivar rdclass: The query class 152 @type rdclass: int 153 @ivar response: The response message 154 @type response: dns.message.Message object 155 @ivar rrset: The answer 156 @type rrset: dns.rrset.RRset object 157 @ivar expiration: The time when the answer expires 158 @type expiration: float (seconds since the epoch) 159 @ivar canonical_name: The canonical name of the query name 160 @type canonical_name: dns.name.Name object 161 """ 162
163 - def __init__(self, qname, rdtype, rdclass, response, 164 raise_on_no_answer=True):
165 self.qname = qname 166 self.rdtype = rdtype 167 self.rdclass = rdclass 168 self.response = response 169 min_ttl = -1 170 rrset = None 171 for count in xrange(0, 15): 172 try: 173 rrset = response.find_rrset(response.answer, qname, 174 rdclass, rdtype) 175 if min_ttl == -1 or rrset.ttl < min_ttl: 176 min_ttl = rrset.ttl 177 break 178 except KeyError: 179 if rdtype != dns.rdatatype.CNAME: 180 try: 181 crrset = response.find_rrset(response.answer, 182 qname, 183 rdclass, 184 dns.rdatatype.CNAME) 185 if min_ttl == -1 or crrset.ttl < min_ttl: 186 min_ttl = crrset.ttl 187 for rd in crrset: 188 qname = rd.target 189 break 190 continue 191 except KeyError: 192 if raise_on_no_answer: 193 raise NoAnswer(response=response) 194 if raise_on_no_answer: 195 raise NoAnswer(response=response) 196 if rrset is None and raise_on_no_answer: 197 raise NoAnswer(response=response) 198 self.canonical_name = qname 199 self.rrset = rrset 200 if rrset is None: 201 while 1: 202 # Look for a SOA RR whose owner name is a superdomain 203 # of qname. 204 try: 205 srrset = response.find_rrset(response.authority, qname, 206 rdclass, dns.rdatatype.SOA) 207 if min_ttl == -1 or srrset.ttl < min_ttl: 208 min_ttl = srrset.ttl 209 if srrset[0].minimum < min_ttl: 210 min_ttl = srrset[0].minimum 211 break 212 except KeyError: 213 try: 214 qname = qname.parent() 215 except dns.name.NoParent: 216 break 217 self.expiration = time.time() + min_ttl
218
219 - def __getattr__(self, attr):
220 if attr == 'name': 221 return self.rrset.name 222 elif attr == 'ttl': 223 return self.rrset.ttl 224 elif attr == 'covers': 225 return self.rrset.covers 226 elif attr == 'rdclass': 227 return self.rrset.rdclass 228 elif attr == 'rdtype': 229 return self.rrset.rdtype 230 else: 231 raise AttributeError(attr)
232
233 - def __len__(self):
234 return len(self.rrset)
235
236 - def __iter__(self):
237 return iter(self.rrset)
238
239 - def __getitem__(self, i):
240 return self.rrset[i]
241
242 - def __delitem__(self, i):
243 del self.rrset[i]
244
245 - def __getslice__(self, i, j):
246 return self.rrset[i:j]
247
248 - def __delslice__(self, i, j):
249 del self.rrset[i:j]
250 251
252 -class Cache(object):
253 254 """Simple DNS answer cache. 255 256 @ivar data: A dictionary of cached data 257 @type data: dict 258 @ivar cleaning_interval: The number of seconds between cleanings. The 259 default is 300 (5 minutes). 260 @type cleaning_interval: float 261 @ivar next_cleaning: The time the cache should next be cleaned (in seconds 262 since the epoch.) 263 @type next_cleaning: float 264 """ 265
266 - def __init__(self, cleaning_interval=300.0):
267 """Initialize a DNS cache. 268 269 @param cleaning_interval: the number of seconds between periodic 270 cleanings. The default is 300.0 271 @type cleaning_interval: float. 272 """ 273 274 self.data = {} 275 self.cleaning_interval = cleaning_interval 276 self.next_cleaning = time.time() + self.cleaning_interval 277 self.lock = _threading.Lock()
278
279 - def _maybe_clean(self):
280 """Clean the cache if it's time to do so.""" 281 282 now = time.time() 283 if self.next_cleaning <= now: 284 keys_to_delete = [] 285 for (k, v) in self.data.items(): 286 if v.expiration <= now: 287 keys_to_delete.append(k) 288 for k in keys_to_delete: 289 del self.data[k] 290 now = time.time() 291 self.next_cleaning = now + self.cleaning_interval
292
293 - def get(self, key):
294 """Get the answer associated with I{key}. Returns None if 295 no answer is cached for the key. 296 @param key: the key 297 @type key: (dns.name.Name, int, int) tuple whose values are the 298 query name, rdtype, and rdclass. 299 @rtype: dns.resolver.Answer object or None 300 """ 301 302 try: 303 self.lock.acquire() 304 self._maybe_clean() 305 v = self.data.get(key) 306 if v is None or v.expiration <= time.time(): 307 return None 308 return v 309 finally: 310 self.lock.release()
311
312 - def put(self, key, value):
313 """Associate key and value in the cache. 314 @param key: the key 315 @type key: (dns.name.Name, int, int) tuple whose values are the 316 query name, rdtype, and rdclass. 317 @param value: The answer being cached 318 @type value: dns.resolver.Answer object 319 """ 320 321 try: 322 self.lock.acquire() 323 self._maybe_clean() 324 self.data[key] = value 325 finally: 326 self.lock.release()
327
328 - def flush(self, key=None):
329 """Flush the cache. 330 331 If I{key} is specified, only that item is flushed. Otherwise 332 the entire cache is flushed. 333 334 @param key: the key to flush 335 @type key: (dns.name.Name, int, int) tuple or None 336 """ 337 338 try: 339 self.lock.acquire() 340 if key is not None: 341 if key in self.data: 342 del self.data[key] 343 else: 344 self.data = {} 345 self.next_cleaning = time.time() + self.cleaning_interval 346 finally: 347 self.lock.release()
348 349
350 -class LRUCacheNode(object):
351 352 """LRUCache node. 353 """ 354
355 - def __init__(self, key, value):
356 self.key = key 357 self.value = value 358 self.prev = self 359 self.next = self
360 366 372
376 377
378 -class LRUCache(object):
379 380 """Bounded least-recently-used DNS answer cache. 381 382 This cache is better than the simple cache (above) if you're 383 running a web crawler or other process that does a lot of 384 resolutions. The LRUCache has a maximum number of nodes, and when 385 it is full, the least-recently used node is removed to make space 386 for a new one. 387 388 @ivar data: A dictionary of cached data 389 @type data: dict 390 @ivar sentinel: sentinel node for circular doubly linked list of nodes 391 @type sentinel: LRUCacheNode object 392 @ivar max_size: The maximum number of nodes 393 @type max_size: int 394 """ 395
396 - def __init__(self, max_size=100000):
397 """Initialize a DNS cache. 398 399 @param max_size: The maximum number of nodes to cache; the default is 400 100000. Must be > 1. 401 @type max_size: int 402 """ 403 self.data = {} 404 self.set_max_size(max_size) 405 self.sentinel = LRUCacheNode(None, None) 406 self.lock = _threading.Lock()
407
408 - def set_max_size(self, max_size):
409 if max_size < 1: 410 max_size = 1 411 self.max_size = max_size
412
413 - def get(self, key):
414 """Get the answer associated with I{key}. Returns None if 415 no answer is cached for the key. 416 @param key: the key 417 @type key: (dns.name.Name, int, int) tuple whose values are the 418 query name, rdtype, and rdclass. 419 @rtype: dns.resolver.Answer object or None 420 """ 421 try: 422 self.lock.acquire() 423 node = self.data.get(key) 424 if node is None: 425 return None 426 # Unlink because we're either going to move the node to the front 427 # of the LRU list or we're going to free it. 428 node.unlink() 429 if node.value.expiration <= time.time(): 430 del self.data[node.key] 431 return None 432 node.link_after(self.sentinel) 433 return node.value 434 finally: 435 self.lock.release()
436
437 - def put(self, key, value):
438 """Associate key and value in the cache. 439 @param key: the key 440 @type key: (dns.name.Name, int, int) tuple whose values are the 441 query name, rdtype, and rdclass. 442 @param value: The answer being cached 443 @type value: dns.resolver.Answer object 444 """ 445 try: 446 self.lock.acquire() 447 node = self.data.get(key) 448 if node is not None: 449 node.unlink() 450 del self.data[node.key] 451 while len(self.data) >= self.max_size: 452 node = self.sentinel.prev 453 node.unlink() 454 del self.data[node.key] 455 node = LRUCacheNode(key, value) 456 node.link_after(self.sentinel) 457 self.data[key] = node 458 finally: 459 self.lock.release()
460
461 - def flush(self, key=None):
462 """Flush the cache. 463 464 If I{key} is specified, only that item is flushed. Otherwise 465 the entire cache is flushed. 466 467 @param key: the key to flush 468 @type key: (dns.name.Name, int, int) tuple or None 469 """ 470 try: 471 self.lock.acquire() 472 if key is not None: 473 node = self.data.get(key) 474 if node is not None: 475 node.unlink() 476 del self.data[node.key] 477 else: 478 node = self.sentinel.next 479 while node != self.sentinel: 480 next = node.next 481 node.prev = None 482 node.next = None 483 node = next 484 self.data = {} 485 finally: 486 self.lock.release()
487 488
489 -class Resolver(object):
490 491 """DNS stub resolver 492 493 @ivar domain: The domain of this host 494 @type domain: dns.name.Name object 495 @ivar nameservers: A list of nameservers to query. Each nameserver is 496 a string which contains the IP address of a nameserver. 497 @type nameservers: list of strings 498 @ivar search: The search list. If the query name is a relative name, 499 the resolver will construct an absolute query name by appending the search 500 names one by one to the query name. 501 @type search: list of dns.name.Name objects 502 @ivar port: The port to which to send queries. The default is 53. 503 @type port: int 504 @ivar timeout: The number of seconds to wait for a response from a 505 server, before timing out. 506 @type timeout: float 507 @ivar lifetime: The total number of seconds to spend trying to get an 508 answer to the question. If the lifetime expires, a Timeout exception 509 will occur. 510 @type lifetime: float 511 @ivar keyring: The TSIG keyring to use. The default is None. 512 @type keyring: dict 513 @ivar keyname: The TSIG keyname to use. The default is None. 514 @type keyname: dns.name.Name object 515 @ivar keyalgorithm: The TSIG key algorithm to use. The default is 516 dns.tsig.default_algorithm. 517 @type keyalgorithm: string 518 @ivar edns: The EDNS level to use. The default is -1, no Edns. 519 @type edns: int 520 @ivar ednsflags: The EDNS flags 521 @type ednsflags: int 522 @ivar payload: The EDNS payload size. The default is 0. 523 @type payload: int 524 @ivar flags: The message flags to use. The default is None (i.e. not 525 overwritten) 526 @type flags: int 527 @ivar cache: The cache to use. The default is None. 528 @type cache: dns.resolver.Cache object 529 @ivar retry_servfail: should we retry a nameserver if it says SERVFAIL? 530 The default is 'false'. 531 @type retry_servfail: bool 532 """ 533
534 - def __init__(self, filename='/etc/resolv.conf', configure=True):
535 """Initialize a resolver instance. 536 537 @param filename: The filename of a configuration file in 538 standard /etc/resolv.conf format. This parameter is meaningful 539 only when I{configure} is true and the platform is POSIX. 540 @type filename: string or file object 541 @param configure: If True (the default), the resolver instance 542 is configured in the normal fashion for the operating system 543 the resolver is running on. (I.e. a /etc/resolv.conf file on 544 POSIX systems and from the registry on Windows systems.) 545 @type configure: bool""" 546 547 self.reset() 548 if configure: 549 if sys.platform == 'win32': 550 self.read_registry() 551 elif filename: 552 self.read_resolv_conf(filename)
553
554 - def reset(self):
555 """Reset all resolver configuration to the defaults.""" 556 self.domain = \ 557 dns.name.Name(dns.name.from_text(socket.gethostname())[1:]) 558 if len(self.domain) == 0: 559 self.domain = dns.name.root 560 self.nameservers = [] 561 self.nameserver_ports = {} 562 self.port = 53 563 self.search = [] 564 self.timeout = 2.0 565 self.lifetime = 30.0 566 self.keyring = None 567 self.keyname = None 568 self.keyalgorithm = dns.tsig.default_algorithm 569 self.edns = -1 570 self.ednsflags = 0 571 self.payload = 0 572 self.cache = None 573 self.flags = None 574 self.retry_servfail = False 575 self.rotate = False
576
577 - def read_resolv_conf(self, f):
578 """Process f as a file in the /etc/resolv.conf format. If f is 579 a string, it is used as the name of the file to open; otherwise it 580 is treated as the file itself.""" 581 if isinstance(f, string_types): 582 try: 583 f = open(f, 'r') 584 except IOError: 585 # /etc/resolv.conf doesn't exist, can't be read, etc. 586 # We'll just use the default resolver configuration. 587 self.nameservers = ['127.0.0.1'] 588 return 589 want_close = True 590 else: 591 want_close = False 592 try: 593 for l in f: 594 if len(l) == 0 or l[0] == '#' or l[0] == ';': 595 continue 596 tokens = l.split() 597 598 # Any line containing less than 2 tokens is malformed 599 if len(tokens) < 2: 600 continue 601 602 if tokens[0] == 'nameserver': 603 self.nameservers.append(tokens[1]) 604 elif tokens[0] == 'domain': 605 self.domain = dns.name.from_text(tokens[1]) 606 elif tokens[0] == 'search': 607 for suffix in tokens[1:]: 608 self.search.append(dns.name.from_text(suffix)) 609 elif tokens[0] == 'options': 610 if 'rotate' in tokens[1:]: 611 self.rotate = True 612 finally: 613 if want_close: 614 f.close() 615 if len(self.nameservers) == 0: 616 self.nameservers.append('127.0.0.1')
617
618 - def _determine_split_char(self, entry):
619 # 620 # The windows registry irritatingly changes the list element 621 # delimiter in between ' ' and ',' (and vice-versa) in various 622 # versions of windows. 623 # 624 if entry.find(' ') >= 0: 625 split_char = ' ' 626 elif entry.find(',') >= 0: 627 split_char = ',' 628 else: 629 # probably a singleton; treat as a space-separated list. 630 split_char = ' ' 631 return split_char
632
633 - def _config_win32_nameservers(self, nameservers):
634 """Configure a NameServer registry entry.""" 635 # we call str() on nameservers to convert it from unicode to ascii 636 nameservers = str(nameservers) 637 split_char = self._determine_split_char(nameservers) 638 ns_list = nameservers.split(split_char) 639 for ns in ns_list: 640 if ns not in self.nameservers: 641 self.nameservers.append(ns)
642
643 - def _config_win32_domain(self, domain):
644 """Configure a Domain registry entry.""" 645 # we call str() on domain to convert it from unicode to ascii 646 self.domain = dns.name.from_text(str(domain))
647
648 - def _config_win32_search(self, search):
649 """Configure a Search registry entry.""" 650 # we call str() on search to convert it from unicode to ascii 651 search = str(search) 652 split_char = self._determine_split_char(search) 653 search_list = search.split(split_char) 654 for s in search_list: 655 if s not in self.search: 656 self.search.append(dns.name.from_text(s))
657
658 - def _config_win32_fromkey(self, key):
659 """Extract DNS info from a registry key.""" 660 try: 661 servers, rtype = _winreg.QueryValueEx(key, 'NameServer') 662 except WindowsError: 663 servers = None 664 if servers: 665 self._config_win32_nameservers(servers) 666 try: 667 dom, rtype = _winreg.QueryValueEx(key, 'Domain') 668 if dom: 669 self._config_win32_domain(dom) 670 except WindowsError: 671 pass 672 else: 673 try: 674 servers, rtype = _winreg.QueryValueEx(key, 'DhcpNameServer') 675 except WindowsError: 676 servers = None 677 if servers: 678 self._config_win32_nameservers(servers) 679 try: 680 dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain') 681 if dom: 682 self._config_win32_domain(dom) 683 except WindowsError: 684 pass 685 try: 686 search, rtype = _winreg.QueryValueEx(key, 'SearchList') 687 except WindowsError: 688 search = None 689 if search: 690 self._config_win32_search(search)
691
692 - def read_registry(self):
693 """Extract resolver configuration from the Windows registry.""" 694 lm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) 695 want_scan = False 696 try: 697 try: 698 # XP, 2000 699 tcp_params = _winreg.OpenKey(lm, 700 r'SYSTEM\CurrentControlSet' 701 r'\Services\Tcpip\Parameters') 702 want_scan = True 703 except EnvironmentError: 704 # ME 705 tcp_params = _winreg.OpenKey(lm, 706 r'SYSTEM\CurrentControlSet' 707 r'\Services\VxD\MSTCP') 708 try: 709 self._config_win32_fromkey(tcp_params) 710 finally: 711 tcp_params.Close() 712 if want_scan: 713 interfaces = _winreg.OpenKey(lm, 714 r'SYSTEM\CurrentControlSet' 715 r'\Services\Tcpip\Parameters' 716 r'\Interfaces') 717 try: 718 i = 0 719 while True: 720 try: 721 guid = _winreg.EnumKey(interfaces, i) 722 i += 1 723 key = _winreg.OpenKey(interfaces, guid) 724 if not self._win32_is_nic_enabled(lm, guid, key): 725 continue 726 try: 727 self._config_win32_fromkey(key) 728 finally: 729 key.Close() 730 except EnvironmentError: 731 break 732 finally: 733 interfaces.Close() 734 finally: 735 lm.Close()
736
737 - def _win32_is_nic_enabled(self, lm, guid, interface_key):
738 # Look in the Windows Registry to determine whether the network 739 # interface corresponding to the given guid is enabled. 740 # 741 # (Code contributed by Paul Marks, thanks!) 742 # 743 try: 744 # This hard-coded location seems to be consistent, at least 745 # from Windows 2000 through Vista. 746 connection_key = _winreg.OpenKey( 747 lm, 748 r'SYSTEM\CurrentControlSet\Control\Network' 749 r'\{4D36E972-E325-11CE-BFC1-08002BE10318}' 750 r'\%s\Connection' % guid) 751 752 try: 753 # The PnpInstanceID points to a key inside Enum 754 (pnp_id, ttype) = _winreg.QueryValueEx( 755 connection_key, 'PnpInstanceID') 756 757 if ttype != _winreg.REG_SZ: 758 raise ValueError 759 760 device_key = _winreg.OpenKey( 761 lm, r'SYSTEM\CurrentControlSet\Enum\%s' % pnp_id) 762 763 try: 764 # Get ConfigFlags for this device 765 (flags, ttype) = _winreg.QueryValueEx( 766 device_key, 'ConfigFlags') 767 768 if ttype != _winreg.REG_DWORD: 769 raise ValueError 770 771 # Based on experimentation, bit 0x1 indicates that the 772 # device is disabled. 773 return not (flags & 0x1) 774 775 finally: 776 device_key.Close() 777 finally: 778 connection_key.Close() 779 except (EnvironmentError, ValueError): 780 # Pre-vista, enabled interfaces seem to have a non-empty 781 # NTEContextList; this was how dnspython detected enabled 782 # nics before the code above was contributed. We've retained 783 # the old method since we don't know if the code above works 784 # on Windows 95/98/ME. 785 try: 786 (nte, ttype) = _winreg.QueryValueEx(interface_key, 787 'NTEContextList') 788 return nte is not None 789 except WindowsError: 790 return False
791
792 - def _compute_timeout(self, start):
793 now = time.time() 794 duration = now - start 795 if duration < 0: 796 if duration < -1: 797 # Time going backwards is bad. Just give up. 798 raise Timeout(timeout=duration) 799 else: 800 # Time went backwards, but only a little. This can 801 # happen, e.g. under vmware with older linux kernels. 802 # Pretend it didn't happen. 803 now = start 804 if duration >= self.lifetime: 805 raise Timeout(timeout=duration) 806 return min(self.lifetime - duration, self.timeout)
807
808 - def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, 809 tcp=False, source=None, raise_on_no_answer=True, source_port=0):
810 """Query nameservers to find the answer to the question. 811 812 The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects 813 of the appropriate type, or strings that can be converted into objects 814 of the appropriate type. E.g. For I{rdtype} the integer 2 and the 815 the string 'NS' both mean to query for records with DNS rdata type NS. 816 817 @param qname: the query name 818 @type qname: dns.name.Name object or string 819 @param rdtype: the query type 820 @type rdtype: int or string 821 @param rdclass: the query class 822 @type rdclass: int or string 823 @param tcp: use TCP to make the query (default is False). 824 @type tcp: bool 825 @param source: bind to this IP address (defaults to machine default 826 IP). 827 @type source: IP address in dotted quad notation 828 @param raise_on_no_answer: raise NoAnswer if there's no answer 829 (defaults is True). 830 @type raise_on_no_answer: bool 831 @param source_port: The port from which to send the message. 832 The default is 0. 833 @type source_port: int 834 @rtype: dns.resolver.Answer instance 835 @raises Timeout: no answers could be found in the specified lifetime 836 @raises NXDOMAIN: the query name does not exist 837 @raises YXDOMAIN: the query name is too long after DNAME substitution 838 @raises NoAnswer: the response did not contain an answer and 839 raise_on_no_answer is True. 840 @raises NoNameservers: no non-broken nameservers are available to 841 answer the question.""" 842 843 if isinstance(qname, string_types): 844 qname = dns.name.from_text(qname, None) 845 if isinstance(rdtype, string_types): 846 rdtype = dns.rdatatype.from_text(rdtype) 847 if dns.rdatatype.is_metatype(rdtype): 848 raise NoMetaqueries 849 if isinstance(rdclass, string_types): 850 rdclass = dns.rdataclass.from_text(rdclass) 851 if dns.rdataclass.is_metaclass(rdclass): 852 raise NoMetaqueries 853 qnames_to_try = [] 854 if qname.is_absolute(): 855 qnames_to_try.append(qname) 856 else: 857 if len(qname) > 1: 858 qnames_to_try.append(qname.concatenate(dns.name.root)) 859 if self.search: 860 for suffix in self.search: 861 qnames_to_try.append(qname.concatenate(suffix)) 862 else: 863 qnames_to_try.append(qname.concatenate(self.domain)) 864 all_nxdomain = True 865 start = time.time() 866 for qname in qnames_to_try: 867 if self.cache: 868 answer = self.cache.get((qname, rdtype, rdclass)) 869 if answer is not None: 870 if answer.rrset is None and raise_on_no_answer: 871 raise NoAnswer 872 else: 873 return answer 874 request = dns.message.make_query(qname, rdtype, rdclass) 875 if self.keyname is not None: 876 request.use_tsig(self.keyring, self.keyname, 877 algorithm=self.keyalgorithm) 878 request.use_edns(self.edns, self.ednsflags, self.payload) 879 if self.flags is not None: 880 request.flags = self.flags 881 response = None 882 # 883 # make a copy of the servers list so we can alter it later. 884 # 885 nameservers = self.nameservers[:] 886 errors = [] 887 if self.rotate: 888 random.shuffle(nameservers) 889 backoff = 0.10 890 while response is None: 891 if len(nameservers) == 0: 892 raise NoNameservers(request=request, errors=errors) 893 for nameserver in nameservers[:]: 894 timeout = self._compute_timeout(start) 895 port = self.nameserver_ports.get(nameserver, self.port) 896 try: 897 tcp_attempt = tcp 898 if tcp: 899 response = dns.query.tcp(request, nameserver, 900 timeout, port, 901 source=source, 902 source_port=source_port) 903 else: 904 response = dns.query.udp(request, nameserver, 905 timeout, port, 906 source=source, 907 source_port=source_port) 908 if response.flags & dns.flags.TC: 909 # Response truncated; retry with TCP. 910 tcp_attempt = True 911 timeout = self._compute_timeout(start) 912 response = \ 913 dns.query.tcp(request, nameserver, 914 timeout, port, 915 source=source, 916 source_port=source_port) 917 except (socket.error, dns.exception.Timeout) as ex: 918 # 919 # Communication failure or timeout. Go to the 920 # next server 921 # 922 errors.append((nameserver, tcp_attempt, port, ex, 923 response)) 924 response = None 925 continue 926 except dns.query.UnexpectedSource as ex: 927 # 928 # Who knows? Keep going. 929 # 930 errors.append((nameserver, tcp_attempt, port, ex, 931 response)) 932 response = None 933 continue 934 except dns.exception.FormError as ex: 935 # 936 # We don't understand what this server is 937 # saying. Take it out of the mix and 938 # continue. 939 # 940 nameservers.remove(nameserver) 941 errors.append((nameserver, tcp_attempt, port, ex, 942 response)) 943 response = None 944 continue 945 except EOFError as ex: 946 # 947 # We're using TCP and they hung up on us. 948 # Probably they don't support TCP (though 949 # they're supposed to!). Take it out of the 950 # mix and continue. 951 # 952 nameservers.remove(nameserver) 953 errors.append((nameserver, tcp_attempt, port, ex, 954 response)) 955 response = None 956 continue 957 rcode = response.rcode() 958 if rcode == dns.rcode.YXDOMAIN: 959 ex = YXDOMAIN() 960 errors.append((nameserver, tcp_attempt, port, ex, 961 response)) 962 raise ex 963 if rcode == dns.rcode.NOERROR or \ 964 rcode == dns.rcode.NXDOMAIN: 965 break 966 # 967 # We got a response, but we're not happy with the 968 # rcode in it. Remove the server from the mix if 969 # the rcode isn't SERVFAIL. 970 # 971 if rcode != dns.rcode.SERVFAIL or not self.retry_servfail: 972 nameservers.remove(nameserver) 973 errors.append((nameserver, tcp_attempt, port, 974 dns.rcode.to_text(rcode), response)) 975 response = None 976 if response is not None: 977 break 978 # 979 # All nameservers failed! 980 # 981 if len(nameservers) > 0: 982 # 983 # But we still have servers to try. Sleep a bit 984 # so we don't pound them! 985 # 986 timeout = self._compute_timeout(start) 987 sleep_time = min(timeout, backoff) 988 backoff *= 2 989 time.sleep(sleep_time) 990 if response.rcode() == dns.rcode.NXDOMAIN: 991 continue 992 all_nxdomain = False 993 break 994 if all_nxdomain: 995 raise NXDOMAIN(qname=qnames_to_try) 996 answer = Answer(qname, rdtype, rdclass, response, 997 raise_on_no_answer) 998 if self.cache: 999 self.cache.put((qname, rdtype, rdclass), answer) 1000 return answer
1001
1002 - def use_tsig(self, keyring, keyname=None, 1003 algorithm=dns.tsig.default_algorithm):
1004 """Add a TSIG signature to the query. 1005 1006 @param keyring: The TSIG keyring to use; defaults to None. 1007 @type keyring: dict 1008 @param keyname: The name of the TSIG key to use; defaults to None. 1009 The key must be defined in the keyring. If a keyring is specified 1010 but a keyname is not, then the key used will be the first key in the 1011 keyring. Note that the order of keys in a dictionary is not defined, 1012 so applications should supply a keyname when a keyring is used, unless 1013 they know the keyring contains only one key. 1014 @param algorithm: The TSIG key algorithm to use. The default 1015 is dns.tsig.default_algorithm. 1016 @type algorithm: string""" 1017 self.keyring = keyring 1018 if keyname is None: 1019 self.keyname = list(self.keyring.keys())[0] 1020 else: 1021 self.keyname = keyname 1022 self.keyalgorithm = algorithm
1023
1024 - def use_edns(self, edns, ednsflags, payload):
1025 """Configure Edns. 1026 1027 @param edns: The EDNS level to use. The default is -1, no Edns. 1028 @type edns: int 1029 @param ednsflags: The EDNS flags 1030 @type ednsflags: int 1031 @param payload: The EDNS payload size. The default is 0. 1032 @type payload: int""" 1033 1034 if edns is None: 1035 edns = -1 1036 self.edns = edns 1037 self.ednsflags = ednsflags 1038 self.payload = payload
1039
1040 - def set_flags(self, flags):
1041 """Overrides the default flags with your own 1042 1043 @param flags: The flags to overwrite the default with 1044 @type flags: int""" 1045 self.flags = flags
1046 1047 default_resolver = None 1048 1049
1050 -def get_default_resolver():
1051 """Get the default resolver, initializing it if necessary.""" 1052 global default_resolver 1053 if default_resolver is None: 1054 default_resolver = Resolver() 1055 return default_resolver
1056 1057
1058 -def query(qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, 1059 tcp=False, source=None, raise_on_no_answer=True, 1060 source_port=0):
1061 """Query nameservers to find the answer to the question. 1062 1063 This is a convenience function that uses the default resolver 1064 object to make the query. 1065 @see: L{dns.resolver.Resolver.query} for more information on the 1066 parameters.""" 1067 return get_default_resolver().query(qname, rdtype, rdclass, tcp, source, 1068 raise_on_no_answer, source_port)
1069 1070
1071 -def zone_for_name(name, rdclass=dns.rdataclass.IN, tcp=False, resolver=None):
1072 """Find the name of the zone which contains the specified name. 1073 1074 @param name: the query name 1075 @type name: absolute dns.name.Name object or string 1076 @param rdclass: The query class 1077 @type rdclass: int 1078 @param tcp: use TCP to make the query (default is False). 1079 @type tcp: bool 1080 @param resolver: the resolver to use 1081 @type resolver: dns.resolver.Resolver object or None 1082 @rtype: dns.name.Name""" 1083 1084 if isinstance(name, string_types): 1085 name = dns.name.from_text(name, dns.name.root) 1086 if resolver is None: 1087 resolver = get_default_resolver() 1088 if not name.is_absolute(): 1089 raise NotAbsolute(name) 1090 while 1: 1091 try: 1092 answer = resolver.query(name, dns.rdatatype.SOA, rdclass, tcp) 1093 if answer.rrset.name == name: 1094 return name 1095 # otherwise we were CNAMEd or DNAMEd and need to look higher 1096 except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): 1097 pass 1098 try: 1099 name = name.parent() 1100 except dns.name.NoParent: 1101 raise NoRootSOA
1102 1103 # 1104 # Support for overriding the system resolver for all python code in the 1105 # running process. 1106 # 1107 1108 _protocols_for_socktype = { 1109 socket.SOCK_DGRAM: [socket.SOL_UDP], 1110 socket.SOCK_STREAM: [socket.SOL_TCP], 1111 } 1112 1113 _resolver = None 1114 _original_getaddrinfo = socket.getaddrinfo 1115 _original_getnameinfo = socket.getnameinfo 1116 _original_getfqdn = socket.getfqdn 1117 _original_gethostbyname = socket.gethostbyname 1118 _original_gethostbyname_ex = socket.gethostbyname_ex 1119 _original_gethostbyaddr = socket.gethostbyaddr 1120 1121
1122 -def _getaddrinfo(host=None, service=None, family=socket.AF_UNSPEC, socktype=0, 1123 proto=0, flags=0):
1124 if flags & (socket.AI_ADDRCONFIG | socket.AI_V4MAPPED) != 0: 1125 raise NotImplementedError 1126 if host is None and service is None: 1127 raise socket.gaierror(socket.EAI_NONAME) 1128 v6addrs = [] 1129 v4addrs = [] 1130 canonical_name = None 1131 try: 1132 # Is host None or a V6 address literal? 1133 if host is None: 1134 canonical_name = 'localhost' 1135 if flags & socket.AI_PASSIVE != 0: 1136 v6addrs.append('::') 1137 v4addrs.append('0.0.0.0') 1138 else: 1139 v6addrs.append('::1') 1140 v4addrs.append('127.0.0.1') 1141 else: 1142 parts = host.split('%') 1143 if len(parts) == 2: 1144 ahost = parts[0] 1145 else: 1146 ahost = host 1147 addr = dns.ipv6.inet_aton(ahost) 1148 v6addrs.append(host) 1149 canonical_name = host 1150 except: 1151 try: 1152 # Is it a V4 address literal? 1153 addr = dns.ipv4.inet_aton(host) 1154 v4addrs.append(host) 1155 canonical_name = host 1156 except: 1157 if flags & socket.AI_NUMERICHOST == 0: 1158 try: 1159 if family == socket.AF_INET6 or family == socket.AF_UNSPEC: 1160 v6 = _resolver.query(host, dns.rdatatype.AAAA, 1161 raise_on_no_answer=False) 1162 # Note that setting host ensures we query the same name 1163 # for A as we did for AAAA. 1164 host = v6.qname 1165 canonical_name = v6.canonical_name.to_text(True) 1166 if v6.rrset is not None: 1167 for rdata in v6.rrset: 1168 v6addrs.append(rdata.address) 1169 if family == socket.AF_INET or family == socket.AF_UNSPEC: 1170 v4 = _resolver.query(host, dns.rdatatype.A, 1171 raise_on_no_answer=False) 1172 host = v4.qname 1173 canonical_name = v4.canonical_name.to_text(True) 1174 if v4.rrset is not None: 1175 for rdata in v4.rrset: 1176 v4addrs.append(rdata.address) 1177 except dns.resolver.NXDOMAIN: 1178 raise socket.gaierror(socket.EAI_NONAME) 1179 except: 1180 raise socket.gaierror(socket.EAI_SYSTEM) 1181 port = None 1182 try: 1183 # Is it a port literal? 1184 if service is None: 1185 port = 0 1186 else: 1187 port = int(service) 1188 except: 1189 if flags & socket.AI_NUMERICSERV == 0: 1190 try: 1191 port = socket.getservbyname(service) 1192 except: 1193 pass 1194 if port is None: 1195 raise socket.gaierror(socket.EAI_NONAME) 1196 tuples = [] 1197 if socktype == 0: 1198 socktypes = [socket.SOCK_DGRAM, socket.SOCK_STREAM] 1199 else: 1200 socktypes = [socktype] 1201 if flags & socket.AI_CANONNAME != 0: 1202 cname = canonical_name 1203 else: 1204 cname = '' 1205 if family == socket.AF_INET6 or family == socket.AF_UNSPEC: 1206 for addr in v6addrs: 1207 for socktype in socktypes: 1208 for proto in _protocols_for_socktype[socktype]: 1209 tuples.append((socket.AF_INET6, socktype, proto, 1210 cname, (addr, port, 0, 0))) 1211 if family == socket.AF_INET or family == socket.AF_UNSPEC: 1212 for addr in v4addrs: 1213 for socktype in socktypes: 1214 for proto in _protocols_for_socktype[socktype]: 1215 tuples.append((socket.AF_INET, socktype, proto, 1216 cname, (addr, port))) 1217 if len(tuples) == 0: 1218 raise socket.gaierror(socket.EAI_NONAME) 1219 return tuples
1220 1221
1222 -def _getnameinfo(sockaddr, flags=0):
1223 host = sockaddr[0] 1224 port = sockaddr[1] 1225 if len(sockaddr) == 4: 1226 scope = sockaddr[3] 1227 family = socket.AF_INET6 1228 else: 1229 scope = None 1230 family = socket.AF_INET 1231 tuples = _getaddrinfo(host, port, family, socket.SOCK_STREAM, 1232 socket.SOL_TCP, 0) 1233 if len(tuples) > 1: 1234 raise socket.error('sockaddr resolved to multiple addresses') 1235 addr = tuples[0][4][0] 1236 if flags & socket.NI_DGRAM: 1237 pname = 'udp' 1238 else: 1239 pname = 'tcp' 1240 qname = dns.reversename.from_address(addr) 1241 if flags & socket.NI_NUMERICHOST == 0: 1242 try: 1243 answer = _resolver.query(qname, 'PTR') 1244 hostname = answer.rrset[0].target.to_text(True) 1245 except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): 1246 if flags & socket.NI_NAMEREQD: 1247 raise socket.gaierror(socket.EAI_NONAME) 1248 hostname = addr 1249 if scope is not None: 1250 hostname += '%' + str(scope) 1251 else: 1252 hostname = addr 1253 if scope is not None: 1254 hostname += '%' + str(scope) 1255 if flags & socket.NI_NUMERICSERV: 1256 service = str(port) 1257 else: 1258 service = socket.getservbyport(port, pname) 1259 return (hostname, service)
1260 1261
1262 -def _getfqdn(name=None):
1263 if name is None: 1264 name = socket.gethostname() 1265 try: 1266 return _getnameinfo(_getaddrinfo(name, 80)[0][4])[0] 1267 except: 1268 return name
1269 1270
1271 -def _gethostbyname(name):
1272 return _gethostbyname_ex(name)[2][0]
1273 1274
1275 -def _gethostbyname_ex(name):
1276 aliases = [] 1277 addresses = [] 1278 tuples = _getaddrinfo(name, 0, socket.AF_INET, socket.SOCK_STREAM, 1279 socket.SOL_TCP, socket.AI_CANONNAME) 1280 canonical = tuples[0][3] 1281 for item in tuples: 1282 addresses.append(item[4][0]) 1283 # XXX we just ignore aliases 1284 return (canonical, aliases, addresses)
1285 1286
1287 -def _gethostbyaddr(ip):
1288 try: 1289 dns.ipv6.inet_aton(ip) 1290 sockaddr = (ip, 80, 0, 0) 1291 family = socket.AF_INET6 1292 except: 1293 sockaddr = (ip, 80) 1294 family = socket.AF_INET 1295 (name, port) = _getnameinfo(sockaddr, socket.NI_NAMEREQD) 1296 aliases = [] 1297 addresses = [] 1298 tuples = _getaddrinfo(name, 0, family, socket.SOCK_STREAM, socket.SOL_TCP, 1299 socket.AI_CANONNAME) 1300 canonical = tuples[0][3] 1301 for item in tuples: 1302 addresses.append(item[4][0]) 1303 # XXX we just ignore aliases 1304 return (canonical, aliases, addresses)
1305 1306
1307 -def override_system_resolver(resolver=None):
1308 """Override the system resolver routines in the socket module with 1309 versions which use dnspython's resolver. 1310 1311 This can be useful in testing situations where you want to control 1312 the resolution behavior of python code without having to change 1313 the system's resolver settings (e.g. /etc/resolv.conf). 1314 1315 The resolver to use may be specified; if it's not, the default 1316 resolver will be used. 1317 1318 @param resolver: the resolver to use 1319 @type resolver: dns.resolver.Resolver object or None 1320 """ 1321 if resolver is None: 1322 resolver = get_default_resolver() 1323 global _resolver 1324 _resolver = resolver 1325 socket.getaddrinfo = _getaddrinfo 1326 socket.getnameinfo = _getnameinfo 1327 socket.getfqdn = _getfqdn 1328 socket.gethostbyname = _gethostbyname 1329 socket.gethostbyname_ex = _gethostbyname_ex 1330 socket.gethostbyaddr = _gethostbyaddr
1331 1332
1333 -def restore_system_resolver():
1334 """Undo the effects of override_system_resolver(). 1335 """ 1336 global _resolver 1337 _resolver = None 1338 socket.getaddrinfo = _original_getaddrinfo 1339 socket.getnameinfo = _original_getnameinfo 1340 socket.getfqdn = _original_getfqdn 1341 socket.gethostbyname = _original_gethostbyname 1342 socket.gethostbyname_ex = _original_gethostbyname_ex 1343 socket.gethostbyaddr = _original_gethostbyaddr
1344