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