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