Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / names / client.py
1 # -*- test-case-name: twisted.names.test.test_names -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 Asynchronous client DNS
7
8 The functions exposed in this module can be used for asynchronous name
9 resolution and dns queries.
10
11 If you need to create a resolver with specific requirements, such as needing to
12 do queries against a particular host, the L{createResolver} function will
13 return an C{IResolver}.
14
15 Future plans: Proper nameserver acquisition on Windows/MacOS,
16 better caching, respect timeouts
17
18 @author: Jp Calderone
19 """
20
21 import os
22 import errno
23 import warnings
24
25 from zope.interface import implements
26
27 # Twisted imports
28 from twisted.python.runtime import platform
29 from twisted.internet import error, defer, protocol, interfaces
30 from twisted.python import log, failure
31 from twisted.python.deprecate import getWarningMethod
32 from twisted.names import dns, common
33
34
35 class Resolver(common.ResolverBase):
36     """
37     @ivar _waiting: A C{dict} mapping tuple keys of query name/type/class to
38         Deferreds which will be called back with the result of those queries.
39         This is used to avoid issuing the same query more than once in
40         parallel.  This is more efficient on the network and helps avoid a
41         "birthday paradox" attack by keeping the number of outstanding requests
42         for a particular query fixed at one instead of allowing the attacker to
43         raise it to an arbitrary number.
44
45     @ivar _reactor: A provider of L{IReactorTCP}, L{IReactorUDP}, and
46         L{IReactorTime} which will be used to set up network resources and
47         track timeouts.
48     """
49     implements(interfaces.IResolver)
50
51     index = 0
52     timeout = None
53
54     factory = None
55     servers = None
56     dynServers = ()
57     pending = None
58     connections = None
59
60     resolv = None
61     _lastResolvTime = None
62     _resolvReadInterval = 60
63
64     def _getProtocol(self):
65         getWarningMethod()(
66             "Resolver.protocol is deprecated; use Resolver.queryUDP instead.",
67             PendingDeprecationWarning,
68             stacklevel=0)
69         self.protocol = dns.DNSDatagramProtocol(self)
70         return self.protocol
71     protocol = property(_getProtocol)
72
73
74     def __init__(self, resolv=None, servers=None, timeout=(1, 3, 11, 45), reactor=None):
75         """
76         Construct a resolver which will query domain name servers listed in
77         the C{resolv.conf(5)}-format file given by C{resolv} as well as
78         those in the given C{servers} list.  Servers are queried in a
79         round-robin fashion.  If given, C{resolv} is periodically checked
80         for modification and re-parsed if it is noticed to have changed.
81
82         @type servers: C{list} of C{(str, int)} or C{None}
83         @param servers: If not None, interpreted as a list of (host, port)
84             pairs specifying addresses of domain name servers to attempt to use
85             for this lookup.  Host addresses should be in IPv4 dotted-quad
86             form.  If specified, overrides C{resolv}.
87
88         @type resolv: C{str}
89         @param resolv: Filename to read and parse as a resolver(5)
90             configuration file.
91
92         @type timeout: Sequence of C{int}
93         @param timeout: Default number of seconds after which to reissue the
94             query.  When the last timeout expires, the query is considered
95             failed.
96
97         @param reactor: A provider of L{IReactorTime}, L{IReactorUDP}, and
98             L{IReactorTCP} which will be used to establish connections, listen
99             for DNS datagrams, and enforce timeouts.  If not provided, the
100             global reactor will be used.
101
102         @raise ValueError: Raised if no nameserver addresses can be found.
103         """
104         common.ResolverBase.__init__(self)
105
106         if reactor is None:
107             from twisted.internet import reactor
108         self._reactor = reactor
109
110         self.timeout = timeout
111
112         if servers is None:
113             self.servers = []
114         else:
115             self.servers = servers
116
117         self.resolv = resolv
118
119         if not len(self.servers) and not resolv:
120             raise ValueError, "No nameservers specified"
121
122         self.factory = DNSClientFactory(self, timeout)
123         self.factory.noisy = 0   # Be quiet by default
124
125         self.connections = []
126         self.pending = []
127
128         self._waiting = {}
129
130         self.maybeParseConfig()
131
132
133     def __getstate__(self):
134         d = self.__dict__.copy()
135         d['connections'] = []
136         d['_parseCall'] = None
137         return d
138
139
140     def __setstate__(self, state):
141         self.__dict__.update(state)
142         self.maybeParseConfig()
143
144
145     def maybeParseConfig(self):
146         if self.resolv is None:
147             # Don't try to parse it, don't set up a call loop
148             return
149
150         try:
151             resolvConf = file(self.resolv)
152         except IOError, e:
153             if e.errno == errno.ENOENT:
154                 # Missing resolv.conf is treated the same as an empty resolv.conf
155                 self.parseConfig(())
156             else:
157                 raise
158         else:
159             mtime = os.fstat(resolvConf.fileno()).st_mtime
160             if mtime != self._lastResolvTime:
161                 log.msg('%s changed, reparsing' % (self.resolv,))
162                 self._lastResolvTime = mtime
163                 self.parseConfig(resolvConf)
164
165         # Check again in a little while
166         self._parseCall = self._reactor.callLater(
167             self._resolvReadInterval, self.maybeParseConfig)
168
169
170     def parseConfig(self, resolvConf):
171         servers = []
172         for L in resolvConf:
173             L = L.strip()
174             if L.startswith('nameserver'):
175                 resolver = (L.split()[1], dns.PORT)
176                 servers.append(resolver)
177                 log.msg("Resolver added %r to server list" % (resolver,))
178             elif L.startswith('domain'):
179                 try:
180                     self.domain = L.split()[1]
181                 except IndexError:
182                     self.domain = ''
183                 self.search = None
184             elif L.startswith('search'):
185                 try:
186                     self.search = L.split()[1:]
187                 except IndexError:
188                     self.search = ''
189                 self.domain = None
190         if not servers:
191             servers.append(('127.0.0.1', dns.PORT))
192         self.dynServers = servers
193
194
195     def pickServer(self):
196         """
197         Return the address of a nameserver.
198
199         TODO: Weight servers for response time so faster ones can be
200         preferred.
201         """
202         if not self.servers and not self.dynServers:
203             return None
204         serverL = len(self.servers)
205         dynL = len(self.dynServers)
206
207         self.index += 1
208         self.index %= (serverL + dynL)
209         if self.index < serverL:
210             return self.servers[self.index]
211         else:
212             return self.dynServers[self.index - serverL]
213
214
215     def _connectedProtocol(self):
216         """
217         Return a new L{DNSDatagramProtocol} bound to a randomly selected port
218         number.
219         """
220         if 'protocol' in self.__dict__:
221             # Some code previously asked for or set the deprecated `protocol`
222             # attribute, so it probably expects that object to be used for
223             # queries.  Give it back and skip the super awesome source port
224             # randomization logic.  This is actually a really good reason to
225             # remove this deprecated backward compatibility as soon as
226             # possible. -exarkun
227             return self.protocol
228         proto = dns.DNSDatagramProtocol(self)
229         while True:
230             try:
231                 self._reactor.listenUDP(dns.randomSource(), proto)
232             except error.CannotListenError:
233                 pass
234             else:
235                 return proto
236
237
238     def connectionMade(self, protocol):
239         """
240         Called by associated L{dns.DNSProtocol} instances when they connect.
241         """
242         self.connections.append(protocol)
243         for (d, q, t) in self.pending:
244             self.queryTCP(q, t).chainDeferred(d)
245         del self.pending[:]
246
247
248     def connectionLost(self, protocol):
249         """
250         Called by associated L{dns.DNSProtocol} instances when they disconnect.
251         """
252         if protocol in self.connections:
253             self.connections.remove(protocol)
254
255
256     def messageReceived(self, message, protocol, address = None):
257         log.msg("Unexpected message (%d) received from %r" % (message.id, address))
258
259
260     def _query(self, *args):
261         """
262         Get a new L{DNSDatagramProtocol} instance from L{_connectedProtocol},
263         issue a query to it using C{*args}, and arrange for it to be
264         disconnected from its transport after the query completes.
265
266         @param *args: Positional arguments to be passed to
267             L{DNSDatagramProtocol.query}.
268
269         @return: A L{Deferred} which will be called back with the result of the
270             query.
271         """
272         protocol = self._connectedProtocol()
273         d = protocol.query(*args)
274         def cbQueried(result):
275             protocol.transport.stopListening()
276             return result
277         d.addBoth(cbQueried)
278         return d
279
280
281     def queryUDP(self, queries, timeout = None):
282         """
283         Make a number of DNS queries via UDP.
284
285         @type queries: A C{list} of C{dns.Query} instances
286         @param queries: The queries to make.
287
288         @type timeout: Sequence of C{int}
289         @param timeout: Number of seconds after which to reissue the query.
290         When the last timeout expires, the query is considered failed.
291
292         @rtype: C{Deferred}
293         @raise C{twisted.internet.defer.TimeoutError}: When the query times
294         out.
295         """
296         if timeout is None:
297             timeout = self.timeout
298
299         addresses = self.servers + list(self.dynServers)
300         if not addresses:
301             return defer.fail(IOError("No domain name servers available"))
302
303         # Make sure we go through servers in the list in the order they were
304         # specified.
305         addresses.reverse()
306
307         used = addresses.pop()
308         d = self._query(used, queries, timeout[0])
309         d.addErrback(self._reissue, addresses, [used], queries, timeout)
310         return d
311
312
313     def _reissue(self, reason, addressesLeft, addressesUsed, query, timeout):
314         reason.trap(dns.DNSQueryTimeoutError)
315
316         # If there are no servers left to be tried, adjust the timeout
317         # to the next longest timeout period and move all the
318         # "used" addresses back to the list of addresses to try.
319         if not addressesLeft:
320             addressesLeft = addressesUsed
321             addressesLeft.reverse()
322             addressesUsed = []
323             timeout = timeout[1:]
324
325         # If all timeout values have been used this query has failed.  Tell the
326         # protocol we're giving up on it and return a terminal timeout failure
327         # to our caller.
328         if not timeout:
329             return failure.Failure(defer.TimeoutError(query))
330
331         # Get an address to try.  Take it out of the list of addresses
332         # to try and put it ino the list of already tried addresses.
333         address = addressesLeft.pop()
334         addressesUsed.append(address)
335
336         # Issue a query to a server.  Use the current timeout.  Add this
337         # function as a timeout errback in case another retry is required.
338         d = self._query(address, query, timeout[0], reason.value.id)
339         d.addErrback(self._reissue, addressesLeft, addressesUsed, query, timeout)
340         return d
341
342
343     def queryTCP(self, queries, timeout = 10):
344         """
345         Make a number of DNS queries via TCP.
346
347         @type queries: Any non-zero number of C{dns.Query} instances
348         @param queries: The queries to make.
349
350         @type timeout: C{int}
351         @param timeout: The number of seconds after which to fail.
352
353         @rtype: C{Deferred}
354         """
355         if not len(self.connections):
356             address = self.pickServer()
357             if address is None:
358                 return defer.fail(IOError("No domain name servers available"))
359             host, port = address
360             self._reactor.connectTCP(host, port, self.factory)
361             self.pending.append((defer.Deferred(), queries, timeout))
362             return self.pending[-1][0]
363         else:
364             return self.connections[0].query(queries, timeout)
365
366
367     def filterAnswers(self, message):
368         """
369         Extract results from the given message.
370
371         If the message was truncated, re-attempt the query over TCP and return
372         a Deferred which will fire with the results of that query.
373
374         If the message's result code is not L{dns.OK}, return a Failure
375         indicating the type of error which occurred.
376
377         Otherwise, return a three-tuple of lists containing the results from
378         the answers section, the authority section, and the additional section.
379         """
380         if message.trunc:
381             return self.queryTCP(message.queries).addCallback(self.filterAnswers)
382         if message.rCode != dns.OK:
383             return failure.Failure(self.exceptionForCode(message.rCode)(message))
384         return (message.answers, message.authority, message.additional)
385
386
387     def _lookup(self, name, cls, type, timeout):
388         """
389         Build a L{dns.Query} for the given parameters and dispatch it via UDP.
390
391         If this query is already outstanding, it will not be re-issued.
392         Instead, when the outstanding query receives a response, that response
393         will be re-used for this query as well.
394
395         @type name: C{str}
396         @type type: C{int}
397         @type cls: C{int}
398
399         @return: A L{Deferred} which fires with a three-tuple giving the
400             answer, authority, and additional sections of the response or with
401             a L{Failure} if the response code is anything other than C{dns.OK}.
402         """
403         key = (name, type, cls)
404         waiting = self._waiting.get(key)
405         if waiting is None:
406             self._waiting[key] = []
407             d = self.queryUDP([dns.Query(name, type, cls)], timeout)
408             def cbResult(result):
409                 for d in self._waiting.pop(key):
410                     d.callback(result)
411                 return result
412             d.addCallback(self.filterAnswers)
413             d.addBoth(cbResult)
414         else:
415             d = defer.Deferred()
416             waiting.append(d)
417         return d
418
419
420     # This one doesn't ever belong on UDP
421     def lookupZone(self, name, timeout = 10):
422         """
423         Perform an AXFR request. This is quite different from usual
424         DNS requests. See http://cr.yp.to/djbdns/axfr-notes.html for
425         more information.
426         """
427         address = self.pickServer()
428         if address is None:
429             return defer.fail(IOError('No domain name servers available'))
430         host, port = address
431         d = defer.Deferred()
432         controller = AXFRController(name, d)
433         factory = DNSClientFactory(controller, timeout)
434         factory.noisy = False #stfu
435
436         connector = self._reactor.connectTCP(host, port, factory)
437         controller.timeoutCall = self._reactor.callLater(
438             timeout or 10, self._timeoutZone, d, controller,
439             connector, timeout or 10)
440         return d.addCallback(self._cbLookupZone, connector)
441
442     def _timeoutZone(self, d, controller, connector, seconds):
443         connector.disconnect()
444         controller.timeoutCall = None
445         controller.deferred = None
446         d.errback(error.TimeoutError("Zone lookup timed out after %d seconds" % (seconds,)))
447
448     def _cbLookupZone(self, result, connector):
449         connector.disconnect()
450         return (result, [], [])
451
452
453 class AXFRController:
454     timeoutCall = None
455
456     def __init__(self, name, deferred):
457         self.name = name
458         self.deferred = deferred
459         self.soa = None
460         self.records = []
461
462     def connectionMade(self, protocol):
463         # dig saids recursion-desired to 0, so I will too
464         message = dns.Message(protocol.pickID(), recDes=0)
465         message.queries = [dns.Query(self.name, dns.AXFR, dns.IN)]
466         protocol.writeMessage(message)
467
468
469     def connectionLost(self, protocol):
470         # XXX Do something here - see #3428
471         pass
472
473
474     def messageReceived(self, message, protocol):
475         # Caveat: We have to handle two cases: All records are in 1
476         # message, or all records are in N messages.
477
478         # According to http://cr.yp.to/djbdns/axfr-notes.html,
479         # 'authority' and 'additional' are always empty, and only
480         # 'answers' is present.
481         self.records.extend(message.answers)
482         if not self.records:
483             return
484         if not self.soa:
485             if self.records[0].type == dns.SOA:
486                 #print "first SOA!"
487                 self.soa = self.records[0]
488         if len(self.records) > 1 and self.records[-1].type == dns.SOA:
489             #print "It's the second SOA! We're done."
490             if self.timeoutCall is not None:
491                 self.timeoutCall.cancel()
492                 self.timeoutCall = None
493             if self.deferred is not None:
494                 self.deferred.callback(self.records)
495                 self.deferred = None
496
497
498
499 from twisted.internet.base import ThreadedResolver as _ThreadedResolverImpl
500
501 class ThreadedResolver(_ThreadedResolverImpl):
502     def __init__(self, reactor=None):
503         if reactor is None:
504             from twisted.internet import reactor
505         _ThreadedResolverImpl.__init__(self, reactor)
506         warnings.warn(
507             "twisted.names.client.ThreadedResolver is deprecated since "
508             "Twisted 9.0, use twisted.internet.base.ThreadedResolver "
509             "instead.",
510             category=DeprecationWarning, stacklevel=2)
511
512 class DNSClientFactory(protocol.ClientFactory):
513     def __init__(self, controller, timeout = 10):
514         self.controller = controller
515         self.timeout = timeout
516
517
518     def clientConnectionLost(self, connector, reason):
519         pass
520
521
522     def buildProtocol(self, addr):
523         p = dns.DNSProtocol(self.controller)
524         p.factory = self
525         return p
526
527
528
529 def createResolver(servers=None, resolvconf=None, hosts=None):
530     """
531     Create and return a Resolver.
532
533     @type servers: C{list} of C{(str, int)} or C{None}
534
535     @param servers: If not C{None}, interpreted as a list of domain name servers
536     to attempt to use. Each server is a tuple of address in C{str} dotted-quad
537     form and C{int} port number.
538
539     @type resolvconf: C{str} or C{None}
540     @param resolvconf: If not C{None}, on posix systems will be interpreted as
541     an alternate resolv.conf to use. Will do nothing on windows systems. If
542     C{None}, /etc/resolv.conf will be used.
543
544     @type hosts: C{str} or C{None}
545     @param hosts: If not C{None}, an alternate hosts file to use. If C{None}
546     on posix systems, /etc/hosts will be used. On windows, C:\windows\hosts
547     will be used.
548
549     @rtype: C{IResolver}
550     """
551     from twisted.names import resolve, cache, root, hosts as hostsModule
552     if platform.getType() == 'posix':
553         if resolvconf is None:
554             resolvconf = '/etc/resolv.conf'
555         if hosts is None:
556             hosts = '/etc/hosts'
557         theResolver = Resolver(resolvconf, servers)
558         hostResolver = hostsModule.Resolver(hosts)
559     else:
560         if hosts is None:
561             hosts = r'c:\windows\hosts'
562         from twisted.internet import reactor
563         bootstrap = _ThreadedResolverImpl(reactor)
564         hostResolver = hostsModule.Resolver(hosts)
565         theResolver = root.bootstrap(bootstrap)
566
567     L = [hostResolver, cache.CacheResolver(), theResolver]
568     return resolve.ResolverChain(L)
569
570 theResolver = None
571 def getResolver():
572     """
573     Get a Resolver instance.
574
575     Create twisted.names.client.theResolver if it is C{None}, and then return
576     that value.
577
578     @rtype: C{IResolver}
579     """
580     global theResolver
581     if theResolver is None:
582         try:
583             theResolver = createResolver()
584         except ValueError:
585             theResolver = createResolver(servers=[('127.0.0.1', 53)])
586     return theResolver
587
588 def getHostByName(name, timeout=None, effort=10):
589     """
590     Resolve a name to a valid ipv4 or ipv6 address.
591
592     Will errback with C{DNSQueryTimeoutError} on a timeout, C{DomainError} or
593     C{AuthoritativeDomainError} (or subclasses) on other errors.
594
595     @type name: C{str}
596     @param name: DNS name to resolve.
597
598     @type timeout: Sequence of C{int}
599     @param timeout: Number of seconds after which to reissue the query.
600     When the last timeout expires, the query is considered failed.
601
602     @type effort: C{int}
603     @param effort: How many times CNAME and NS records to follow while
604     resolving this name.
605
606     @rtype: C{Deferred}
607     """
608     return getResolver().getHostByName(name, timeout, effort)
609
610 def lookupAddress(name, timeout=None):
611     """
612     Perform an A record lookup.
613
614     @type name: C{str}
615     @param name: DNS name to resolve.
616
617     @type timeout: Sequence of C{int}
618     @param timeout: Number of seconds after which to reissue the query.
619     When the last timeout expires, the query is considered failed.
620
621     @rtype: C{Deferred}
622     """
623     return getResolver().lookupAddress(name, timeout)
624
625 def lookupIPV6Address(name, timeout=None):
626     """
627     Perform an AAAA record lookup.
628
629     @type name: C{str}
630     @param name: DNS name to resolve.
631
632     @type timeout: Sequence of C{int}
633     @param timeout: Number of seconds after which to reissue the query.
634     When the last timeout expires, the query is considered failed.
635
636     @rtype: C{Deferred}
637     """
638     return getResolver().lookupIPV6Address(name, timeout)
639
640 def lookupAddress6(name, timeout=None):
641     """
642     Perform an A6 record lookup.
643
644     @type name: C{str}
645     @param name: DNS name to resolve.
646
647     @type timeout: Sequence of C{int}
648     @param timeout: Number of seconds after which to reissue the query.
649     When the last timeout expires, the query is considered failed.
650
651     @rtype: C{Deferred}
652     """
653     return getResolver().lookupAddress6(name, timeout)
654
655 def lookupMailExchange(name, timeout=None):
656     """
657     Perform an MX record lookup.
658
659     @type name: C{str}
660     @param name: DNS name to resolve.
661
662     @type timeout: Sequence of C{int}
663     @param timeout: Number of seconds after which to reissue the query.
664     When the last timeout expires, the query is considered failed.
665
666     @rtype: C{Deferred}
667     """
668     return getResolver().lookupMailExchange(name, timeout)
669
670 def lookupNameservers(name, timeout=None):
671     """
672     Perform an NS record lookup.
673
674     @type name: C{str}
675     @param name: DNS name to resolve.
676
677     @type timeout: Sequence of C{int}
678     @param timeout: Number of seconds after which to reissue the query.
679     When the last timeout expires, the query is considered failed.
680
681     @rtype: C{Deferred}
682     """
683     return getResolver().lookupNameservers(name, timeout)
684
685 def lookupCanonicalName(name, timeout=None):
686     """
687     Perform a CNAME record lookup.
688
689     @type name: C{str}
690     @param name: DNS name to resolve.
691
692     @type timeout: Sequence of C{int}
693     @param timeout: Number of seconds after which to reissue the query.
694     When the last timeout expires, the query is considered failed.
695
696     @rtype: C{Deferred}
697     """
698     return getResolver().lookupCanonicalName(name, timeout)
699
700 def lookupMailBox(name, timeout=None):
701     """
702     Perform an MB record lookup.
703
704     @type name: C{str}
705     @param name: DNS name to resolve.
706
707     @type timeout: Sequence of C{int}
708     @param timeout: Number of seconds after which to reissue the query.
709     When the last timeout expires, the query is considered failed.
710
711     @rtype: C{Deferred}
712     """
713     return getResolver().lookupMailBox(name, timeout)
714
715 def lookupMailGroup(name, timeout=None):
716     """
717     Perform an MG record lookup.
718
719     @type name: C{str}
720     @param name: DNS name to resolve.
721
722     @type timeout: Sequence of C{int}
723     @param timeout: Number of seconds after which to reissue the query.
724     When the last timeout expires, the query is considered failed.
725
726     @rtype: C{Deferred}
727     """
728     return getResolver().lookupMailGroup(name, timeout)
729
730 def lookupMailRename(name, timeout=None):
731     """
732     Perform an MR record lookup.
733
734     @type name: C{str}
735     @param name: DNS name to resolve.
736
737     @type timeout: Sequence of C{int}
738     @param timeout: Number of seconds after which to reissue the query.
739     When the last timeout expires, the query is considered failed.
740
741     @rtype: C{Deferred}
742     """
743     return getResolver().lookupMailRename(name, timeout)
744
745 def lookupPointer(name, timeout=None):
746     """
747     Perform a PTR record lookup.
748
749     @type name: C{str}
750     @param name: DNS name to resolve.
751
752     @type timeout: Sequence of C{int}
753     @param timeout: Number of seconds after which to reissue the query.
754     When the last timeout expires, the query is considered failed.
755
756     @rtype: C{Deferred}
757     """
758     return getResolver().lookupPointer(name, timeout)
759
760 def lookupAuthority(name, timeout=None):
761     """
762     Perform an SOA record lookup.
763
764     @type name: C{str}
765     @param name: DNS name to resolve.
766
767     @type timeout: Sequence of C{int}
768     @param timeout: Number of seconds after which to reissue the query.
769     When the last timeout expires, the query is considered failed.
770
771     @rtype: C{Deferred}
772     """
773     return getResolver().lookupAuthority(name, timeout)
774
775 def lookupNull(name, timeout=None):
776     """
777     Perform a NULL record lookup.
778
779     @type name: C{str}
780     @param name: DNS name to resolve.
781
782     @type timeout: Sequence of C{int}
783     @param timeout: Number of seconds after which to reissue the query.
784     When the last timeout expires, the query is considered failed.
785
786     @rtype: C{Deferred}
787     """
788     return getResolver().lookupNull(name, timeout)
789
790 def lookupWellKnownServices(name, timeout=None):
791     """
792     Perform a WKS record lookup.
793
794     @type name: C{str}
795     @param name: DNS name to resolve.
796
797     @type timeout: Sequence of C{int}
798     @param timeout: Number of seconds after which to reissue the query.
799     When the last timeout expires, the query is considered failed.
800
801     @rtype: C{Deferred}
802     """
803     return getResolver().lookupWellKnownServices(name, timeout)
804
805 def lookupService(name, timeout=None):
806     """
807     Perform an SRV record lookup.
808
809     @type name: C{str}
810     @param name: DNS name to resolve.
811
812     @type timeout: Sequence of C{int}
813     @param timeout: Number of seconds after which to reissue the query.
814     When the last timeout expires, the query is considered failed.
815
816     @rtype: C{Deferred}
817     """
818     return getResolver().lookupService(name, timeout)
819
820 def lookupHostInfo(name, timeout=None):
821     """
822     Perform a HINFO record lookup.
823
824     @type name: C{str}
825     @param name: DNS name to resolve.
826
827     @type timeout: Sequence of C{int}
828     @param timeout: Number of seconds after which to reissue the query.
829     When the last timeout expires, the query is considered failed.
830
831     @rtype: C{Deferred}
832     """
833     return getResolver().lookupHostInfo(name, timeout)
834
835 def lookupMailboxInfo(name, timeout=None):
836     """
837     Perform an MINFO record lookup.
838
839     @type name: C{str}
840     @param name: DNS name to resolve.
841
842     @type timeout: Sequence of C{int}
843     @param timeout: Number of seconds after which to reissue the query.
844     When the last timeout expires, the query is considered failed.
845
846     @rtype: C{Deferred}
847     """
848     return getResolver().lookupMailboxInfo(name, timeout)
849
850 def lookupText(name, timeout=None):
851     """
852     Perform a TXT record lookup.
853
854     @type name: C{str}
855     @param name: DNS name to resolve.
856
857     @type timeout: Sequence of C{int}
858     @param timeout: Number of seconds after which to reissue the query.
859     When the last timeout expires, the query is considered failed.
860
861     @rtype: C{Deferred}
862     """
863     return getResolver().lookupText(name, timeout)
864
865 def lookupSenderPolicy(name, timeout=None):
866     """
867     Perform a SPF record lookup.
868
869     @type name: C{str}
870     @param name: DNS name to resolve.
871
872     @type timeout: Sequence of C{int}
873     @param timeout: Number of seconds after which to reissue the query.
874     When the last timeout expires, the query is considered failed.
875
876     @rtype: C{Deferred}
877     """
878     return getResolver().lookupSenderPolicy(name, timeout)
879
880 def lookupResponsibility(name, timeout=None):
881     """
882     Perform an RP record lookup.
883
884     @type name: C{str}
885     @param name: DNS name to resolve.
886
887     @type timeout: Sequence of C{int}
888     @param timeout: Number of seconds after which to reissue the query.
889     When the last timeout expires, the query is considered failed.
890
891     @rtype: C{Deferred}
892     """
893     return getResolver().lookupResponsibility(name, timeout)
894
895 def lookupAFSDatabase(name, timeout=None):
896     """
897     Perform an AFSDB record lookup.
898
899     @type name: C{str}
900     @param name: DNS name to resolve.
901
902     @type timeout: Sequence of C{int}
903     @param timeout: Number of seconds after which to reissue the query.
904     When the last timeout expires, the query is considered failed.
905
906     @rtype: C{Deferred}
907     """
908     return getResolver().lookupAFSDatabase(name, timeout)
909
910 def lookupZone(name, timeout=None):
911     """
912     Perform an AXFR record lookup.
913
914     @type name: C{str}
915     @param name: DNS name to resolve.
916
917     @type timeout: C{int}
918     @param timeout: When this timeout expires, the query is considered failed.
919
920     @rtype: C{Deferred}
921     """
922     # XXX: timeout here is not a list of ints, it is a single int.
923     return getResolver().lookupZone(name, timeout)
924
925 def lookupAllRecords(name, timeout=None):
926     """
927     ALL_RECORD lookup.
928
929     @type name: C{str}
930     @param name: DNS name to resolve.
931
932     @type timeout: Sequence of C{int}
933     @param timeout: Number of seconds after which to reissue the query.
934     When the last timeout expires, the query is considered failed.
935
936     @rtype: C{Deferred}
937     """
938     return getResolver().lookupAllRecords(name, timeout)
939
940
941
942 def lookupNamingAuthorityPointer(name, timeout=None):
943     """
944     NAPTR lookup.
945
946     @type name: C{str}
947     @param name: DNS name to resolve.
948
949     @type timeout: Sequence of C{int}
950     @param timeout: Number of seconds after which to reissue the query.
951         When the last timeout expires, the query is considered failed.
952
953     @rtype: C{Deferred}
954     """
955     return getResolver().lookupNamingAuthorityPointer(name, timeout)