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