update copyrights
[third_party/dnspython] / dns / resolver.py
1 # Copyright (C) 2003-2007, 2009 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 edns: The EDNS level to use.  The default is -1, no Edns.
269     @type edns: int
270     @ivar ednsflags: The EDNS flags
271     @type ednsflags: int
272     @ivar payload: The EDNS payload size.  The default is 0.
273     @type payload: int
274     @ivar cache: The cache to use.  The default is None.
275     @type cache: dns.resolver.Cache object
276     """
277     def __init__(self, filename='/etc/resolv.conf', configure=True):
278         """Initialize a resolver instance.
279
280         @param filename: The filename of a configuration file in
281         standard /etc/resolv.conf format.  This parameter is meaningful
282         only when I{configure} is true and the platform is POSIX.
283         @type filename: string or file object
284         @param configure: If True (the default), the resolver instance
285         is configured in the normal fashion for the operating system
286         the resolver is running on.  (I.e. a /etc/resolv.conf file on
287         POSIX systems and from the registry on Windows systems.)
288         @type configure: bool"""
289
290         self.reset()
291         if configure:
292             if sys.platform == 'win32':
293                 self.read_registry()
294             elif filename:
295                 self.read_resolv_conf(filename)
296
297     def reset(self):
298         """Reset all resolver configuration to the defaults."""
299         self.domain = \
300             dns.name.Name(dns.name.from_text(socket.gethostname())[1:])
301         if len(self.domain) == 0:
302             self.domain = dns.name.root
303         self.nameservers = []
304         self.search = []
305         self.port = 53
306         self.timeout = 2.0
307         self.lifetime = 30.0
308         self.keyring = None
309         self.keyname = None
310         self.edns = -1
311         self.ednsflags = 0
312         self.payload = 0
313         self.cache = None
314
315     def read_resolv_conf(self, f):
316         """Process f as a file in the /etc/resolv.conf format.  If f is
317         a string, it is used as the name of the file to open; otherwise it
318         is treated as the file itself."""
319         if isinstance(f, str) or isinstance(f, unicode):
320             try:
321                 f = open(f, 'r')
322             except IOError:
323                 # /etc/resolv.conf doesn't exist, can't be read, etc.
324                 # We'll just use the default resolver configuration.
325                 self.nameservers = ['127.0.0.1']
326                 return
327             want_close = True
328         else:
329             want_close = False
330         try:
331             for l in f:
332                 if len(l) == 0 or l[0] == '#' or l[0] == ';':
333                     continue
334                 tokens = l.split()
335                 if len(tokens) == 0:
336                     continue
337                 if tokens[0] == 'nameserver':
338                     self.nameservers.append(tokens[1])
339                 elif tokens[0] == 'domain':
340                     self.domain = dns.name.from_text(tokens[1])
341                 elif tokens[0] == 'search':
342                     for suffix in tokens[1:]:
343                         self.search.append(dns.name.from_text(suffix))
344         finally:
345             if want_close:
346                 f.close()
347         if len(self.nameservers) == 0:
348             self.nameservers.append('127.0.0.1')
349
350     def _determine_split_char(self, entry):
351         #
352         # The windows registry irritatingly changes the list element
353         # delimiter in between ' ' and ',' (and vice-versa) in various
354         # versions of windows.
355         #
356         if entry.find(' ') >= 0:
357             split_char = ' '
358         elif entry.find(',') >= 0:
359             split_char = ','
360         else:
361             # probably a singleton; treat as a space-separated list.
362             split_char = ' '
363         return split_char
364
365     def _config_win32_nameservers(self, nameservers):
366         """Configure a NameServer registry entry."""
367         # we call str() on nameservers to convert it from unicode to ascii
368         nameservers = str(nameservers)
369         split_char = self._determine_split_char(nameservers)
370         ns_list = nameservers.split(split_char)
371         for ns in ns_list:
372             if not ns in self.nameservers:
373                 self.nameservers.append(ns)
374
375     def _config_win32_domain(self, domain):
376         """Configure a Domain registry entry."""
377         # we call str() on domain to convert it from unicode to ascii
378         self.domain = dns.name.from_text(str(domain))
379
380     def _config_win32_search(self, search):
381         """Configure a Search registry entry."""
382         # we call str() on search to convert it from unicode to ascii
383         search = str(search)
384         split_char = self._determine_split_char(search)
385         search_list = search.split(split_char)
386         for s in search_list:
387             if not s in self.search:
388                 self.search.append(dns.name.from_text(s))
389
390     def _config_win32_fromkey(self, key):
391         """Extract DNS info from a registry key."""
392         try:
393             servers, rtype = _winreg.QueryValueEx(key, 'NameServer')
394         except WindowsError:
395             servers = None
396         if servers:
397             self._config_win32_nameservers(servers)
398             try:
399                 dom, rtype = _winreg.QueryValueEx(key, 'Domain')
400                 if dom:
401                     self._config_win32_domain(servers)
402             except WindowsError:
403                 pass
404         else:
405             try:
406                 servers, rtype = _winreg.QueryValueEx(key, 'DhcpNameServer')
407             except WindowsError:
408                 servers = None
409             if servers:
410                 self._config_win32_nameservers(servers)
411                 try:
412                     dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain')
413                     if dom:
414                         self._config_win32_domain(servers)
415                 except WindowsError:
416                     pass
417         try:
418             search, rtype = _winreg.QueryValueEx(key, 'SearchList')
419         except WindowsError:
420             search = None
421         if search:
422             self._config_win32_search(search)
423
424     def read_registry(self):
425         """Extract resolver configuration from the Windows registry."""
426         lm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
427         want_scan = False
428         try:
429             try:
430                 # XP, 2000
431                 tcp_params = _winreg.OpenKey(lm,
432                                              r'SYSTEM\CurrentControlSet'
433                                              r'\Services\Tcpip\Parameters')
434                 want_scan = True
435             except EnvironmentError:
436                 # ME
437                 tcp_params = _winreg.OpenKey(lm,
438                                              r'SYSTEM\CurrentControlSet'
439                                              r'\Services\VxD\MSTCP')
440             try:
441                 self._config_win32_fromkey(tcp_params)
442             finally:
443                 tcp_params.Close()
444             if want_scan:
445                 interfaces = _winreg.OpenKey(lm,
446                                              r'SYSTEM\CurrentControlSet'
447                                              r'\Services\Tcpip\Parameters'
448                                              r'\Interfaces')
449                 try:
450                     i = 0
451                     while True:
452                         try:
453                             guid = _winreg.EnumKey(interfaces, i)
454                             i += 1
455                             key = _winreg.OpenKey(interfaces, guid)
456                             if not self._win32_is_nic_enabled(lm, guid, key):
457                                 continue
458                             try:
459                                 self._config_win32_fromkey(key)
460                             finally:
461                                 key.Close()
462                         except EnvironmentError:
463                             break
464                 finally:
465                     interfaces.Close()
466         finally:
467             lm.Close()
468
469     def _win32_is_nic_enabled(self, lm, guid, interface_key):
470          # Look in the Windows Registry to determine whether the network
471          # interface corresponding to the given guid is enabled.
472          #
473          # (Code contributed by Paul Marks, thanks!)
474          #
475          try:
476              # This hard-coded location seems to be consistent, at least
477              # from Windows 2000 through Vista.
478              connection_key = _winreg.OpenKey(
479                  lm,
480                  r'SYSTEM\CurrentControlSet\Control\Network'
481                  r'\{4D36E972-E325-11CE-BFC1-08002BE10318}'
482                  r'\%s\Connection' % guid)
483
484              try:
485                  # The PnpInstanceID points to a key inside Enum
486                  (pnp_id, ttype) = _winreg.QueryValueEx(
487                      connection_key, 'PnpInstanceID')
488
489                  if ttype != _winreg.REG_SZ:
490                      raise ValueError
491
492                  device_key = _winreg.OpenKey(
493                      lm, r'SYSTEM\CurrentControlSet\Enum\%s' % pnp_id)
494
495                  try:
496                      # Get ConfigFlags for this device
497                      (flags, ttype) = _winreg.QueryValueEx(
498                          device_key, 'ConfigFlags')
499
500                      if ttype != _winreg.REG_DWORD:
501                          raise ValueError
502
503                      # Based on experimentation, bit 0x1 indicates that the
504                      # device is disabled.
505                      return not (flags & 0x1)
506
507                  finally:
508                      device_key.Close()
509              finally:
510                  connection_key.Close()
511          except (EnvironmentError, ValueError):
512              # Pre-vista, enabled interfaces seem to have a non-empty
513              # NTEContextList; this was how dnspython detected enabled
514              # nics before the code above was contributed.  We've retained
515              # the old method since we don't know if the code above works
516              # on Windows 95/98/ME.
517              try:
518                  (nte, ttype) = _winreg.QueryValueEx(interface_key,
519                                                      'NTEContextList')
520                  return nte is not None
521              except WindowsError:
522                  return False
523
524     def _compute_timeout(self, start):
525         now = time.time()
526         if now < start:
527             if start - now > 1:
528                 # Time going backwards is bad.  Just give up.
529                 raise Timeout
530             else:
531                 # Time went backwards, but only a little.  This can
532                 # happen, e.g. under vmware with older linux kernels.
533                 # Pretend it didn't happen.
534                 now = start
535         duration = now - start
536         if duration >= self.lifetime:
537             raise Timeout
538         return min(self.lifetime - duration, self.timeout)
539
540     def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
541               tcp=False):
542         """Query nameservers to find the answer to the question.
543
544         The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects
545         of the appropriate type, or strings that can be converted into objects
546         of the appropriate type.  E.g. For I{rdtype} the integer 2 and the
547         the string 'NS' both mean to query for records with DNS rdata type NS.
548         
549         @param qname: the query name
550         @type qname: dns.name.Name object or string
551         @param rdtype: the query type
552         @type rdtype: int or string
553         @param rdclass: the query class
554         @type rdclass: int or string
555         @param tcp: use TCP to make the query (default is False).
556         @type tcp: bool
557         @rtype: dns.resolver.Answer instance
558         @raises Timeout: no answers could be found in the specified lifetime
559         @raises NXDOMAIN: the query name does not exist
560         @raises NoAnswer: the response did not contain an answer
561         @raises NoNameservers: no non-broken nameservers are available to
562         answer the question."""
563         
564         if isinstance(qname, (str, unicode)):
565             qname = dns.name.from_text(qname, None)
566         if isinstance(rdtype, str):
567             rdtype = dns.rdatatype.from_text(rdtype)
568         if isinstance(rdclass, str):
569             rdclass = dns.rdataclass.from_text(rdclass)
570         qnames_to_try = []
571         if qname.is_absolute():
572             qnames_to_try.append(qname)
573         else:
574             if len(qname) > 1:
575                 qnames_to_try.append(qname.concatenate(dns.name.root))
576             if self.search:
577                 for suffix in self.search:
578                     qnames_to_try.append(qname.concatenate(suffix))
579             else:
580                 qnames_to_try.append(qname.concatenate(self.domain))
581         all_nxdomain = True
582         start = time.time()
583         for qname in qnames_to_try:
584             if self.cache:
585                 answer = self.cache.get((qname, rdtype, rdclass))
586                 if answer:
587                     return answer
588             request = dns.message.make_query(qname, rdtype, rdclass)
589             if not self.keyname is None:
590                 request.use_tsig(self.keyring, self.keyname)
591             request.use_edns(self.edns, self.ednsflags, self.payload)
592             response = None
593             #
594             # make a copy of the servers list so we can alter it later.
595             #
596             nameservers = self.nameservers[:]
597             backoff = 0.10
598             while response is None:
599                 if len(nameservers) == 0:
600                     raise NoNameservers
601                 for nameserver in nameservers[:]:
602                     timeout = self._compute_timeout(start)
603                     try:
604                         if tcp:
605                             response = dns.query.tcp(request, nameserver,
606                                                      timeout, self.port)
607                         else:
608                             response = dns.query.udp(request, nameserver,
609                                                      timeout, self.port)
610                     except (socket.error, dns.exception.Timeout):
611                         #
612                         # Communication failure or timeout.  Go to the
613                         # next server
614                         #
615                         response = None
616                         continue
617                     except dns.query.UnexpectedSource:
618                         #
619                         # Who knows?  Keep going.
620                         #
621                         response = None
622                         continue
623                     except dns.exception.FormError:
624                         #
625                         # We don't understand what this server is
626                         # saying.  Take it out of the mix and
627                         # continue.
628                         #
629                         nameservers.remove(nameserver)
630                         response = None
631                         continue
632                     rcode = response.rcode()
633                     if rcode == dns.rcode.NOERROR or \
634                            rcode == dns.rcode.NXDOMAIN:
635                         break
636                     #
637                     # We got a response, but we're not happy with the
638                     # rcode in it.  Remove the server from the mix if
639                     # the rcode isn't SERVFAIL.
640                     #
641                     if rcode != dns.rcode.SERVFAIL:
642                         nameservers.remove(nameserver)
643                     response = None
644                 if not response is None:
645                     break
646                 #
647                 # All nameservers failed!
648                 #
649                 if len(nameservers) > 0:
650                     #
651                     # But we still have servers to try.  Sleep a bit
652                     # so we don't pound them!
653                     #
654                     timeout = self._compute_timeout(start)
655                     sleep_time = min(timeout, backoff)
656                     backoff *= 2
657                     time.sleep(sleep_time)
658             if response.rcode() == dns.rcode.NXDOMAIN:
659                 continue
660             all_nxdomain = False
661             break
662         if all_nxdomain:
663             raise NXDOMAIN
664         answer = Answer(qname, rdtype, rdclass, response)
665         if self.cache:
666             self.cache.put((qname, rdtype, rdclass), answer)
667         return answer
668
669     def use_tsig(self, keyring, keyname=None):
670         """Add a TSIG signature to the query.
671
672         @param keyring: The TSIG keyring to use; defaults to None.
673         @type keyring: dict
674         @param keyname: The name of the TSIG key to use; defaults to None.
675         The key must be defined in the keyring.  If a keyring is specified
676         but a keyname is not, then the key used will be the first key in the
677         keyring.  Note that the order of keys in a dictionary is not defined,
678         so applications should supply a keyname when a keyring is used, unless
679         they know the keyring contains only one key."""
680         self.keyring = keyring
681         if keyname is None:
682             self.keyname = self.keyring.keys()[0]
683         else:
684             self.keyname = keyname
685
686     def use_edns(self, edns, ednsflags, payload):
687         """Configure Edns.
688
689         @param edns: The EDNS level to use.  The default is -1, no Edns.
690         @type edns: int
691         @param ednsflags: The EDNS flags
692         @type ednsflags: int
693         @param payload: The EDNS payload size.  The default is 0.
694         @type payload: int"""
695
696         if edns is None:
697             edns = -1
698         self.edns = edns
699         self.ednsflags = ednsflags
700         self.payload = payload
701
702 default_resolver = None
703
704 def get_default_resolver():
705     """Get the default resolver, initializing it if necessary."""
706     global default_resolver
707     if default_resolver is None:
708         default_resolver = Resolver()
709     return default_resolver
710
711 def query(qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
712           tcp=False):
713     """Query nameservers to find the answer to the question.
714
715     This is a convenience function that uses the default resolver
716     object to make the query.
717     @see: L{dns.resolver.Resolver.query} for more information on the
718     parameters."""
719     return get_default_resolver().query(qname, rdtype, rdclass, tcp)
720
721 def zone_for_name(name, rdclass=dns.rdataclass.IN, tcp=False, resolver=None):
722     """Find the name of the zone which contains the specified name.
723
724     @param name: the query name
725     @type name: absolute dns.name.Name object or string
726     @param rdclass: The query class
727     @type rdclass: int
728     @param tcp: use TCP to make the query (default is False).
729     @type tcp: bool
730     @param resolver: the resolver to use
731     @type resolver: dns.resolver.Resolver object or None
732     @rtype: dns.name.Name"""
733
734     if isinstance(name, (str, unicode)):
735         name = dns.name.from_text(name, dns.name.root)
736     if resolver is None:
737         resolver = get_default_resolver()
738     if not name.is_absolute():
739         raise NotAbsolute, name
740     while 1:
741         try:
742             answer = resolver.query(name, dns.rdatatype.SOA, rdclass, tcp)
743             return name
744         except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
745             try:
746                 name = name.parent()
747             except dns.name.NoParent:
748                 raise NoRootSOA