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