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

# Source Code for Module dns.resolver

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