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

# Source Code for Module dns.resolver

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