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