| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: twisted.names.test.test_rootresolve -*- | |
| 2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 3 # See LICENSE for details. | |
| 4 | |
| 5 | |
| 6 """ | |
| 7 Resolver implementation for querying successive authoritative servers to | |
| 8 lookup a record, starting from the root nameservers. | |
| 9 | |
| 10 @author: U{Jp Calderone<mailto:exarkun@twistedmatrix.com>} | |
| 11 | |
| 12 todo:: | |
| 13 robustify it | |
| 14 break discoverAuthority into several smaller functions | |
| 15 documentation | |
| 16 """ | |
| 17 | |
| 18 from __future__ import generators | |
| 19 | |
| 20 import sys | |
| 21 | |
| 22 from twisted.python import log | |
| 23 from twisted.internet import defer | |
| 24 from twisted.names import dns | |
| 25 from twisted.names import common | |
| 26 | |
| 27 def retry(t, p, *args): | |
| 28 assert t, "Timeout is required" | |
| 29 t = list(t) | |
| 30 def errback(failure): | |
| 31 failure.trap(defer.TimeoutError) | |
| 32 if not t: | |
| 33 return failure | |
| 34 return p.query(timeout=t.pop(0), *args | |
| 35 ).addErrback(errback | |
| 36 ) | |
| 37 return p.query(timeout=t.pop(0), *args | |
| 38 ).addErrback(errback | |
| 39 ) | |
| 40 | |
| 41 class _DummyController: | |
| 42 def messageReceived(self, *args): | |
| 43 pass | |
| 44 | |
| 45 class Resolver(common.ResolverBase): | |
| 46 def __init__(self, hints): | |
| 47 common.ResolverBase.__init__(self) | |
| 48 self.hints = hints | |
| 49 | |
| 50 def _lookup(self, name, cls, type, timeout): | |
| 51 d = discoverAuthority(name, self.hints | |
| 52 ).addCallback(self.discoveredAuthority, name, cls, type, timeout | |
| 53 ) | |
| 54 return d | |
| 55 | |
| 56 def discoveredAuthority(self, auth, name, cls, type, timeout): | |
| 57 from twisted.names import client | |
| 58 q = dns.Query(name, type, cls) | |
| 59 r = client.Resolver(servers=[(auth, dns.PORT)]) | |
| 60 d = r.queryUDP([q], timeout) | |
| 61 d.addCallback(r.filterAnswers) | |
| 62 return d | |
| 63 | |
| 64 def lookupNameservers(host, atServer, p=None): | |
| 65 # print 'Nameserver lookup for', host, 'at', atServer, 'with', p | |
| 66 if p is None: | |
| 67 p = dns.DNSDatagramProtocol(_DummyController()) | |
| 68 p.noisy = False | |
| 69 return retry( | |
| 70 (1, 3, 11, 45), # Timeouts | |
| 71 p, # Protocol instance | |
| 72 (atServer, dns.PORT), # Server to query | |
| 73 [dns.Query(host, dns.NS, dns.IN)] # Question to ask | |
| 74 ) | |
| 75 | |
| 76 def lookupAddress(host, atServer, p=None): | |
| 77 # print 'Address lookup for', host, 'at', atServer, 'with', p | |
| 78 if p is None: | |
| 79 p = dns.DNSDatagramProtocol(_DummyController()) | |
| 80 p.noisy = False | |
| 81 return retry( | |
| 82 (1, 3, 11, 45), # Timeouts | |
| 83 p, # Protocol instance | |
| 84 (atServer, dns.PORT), # Server to query | |
| 85 [dns.Query(host, dns.A, dns.IN)] # Question to ask | |
| 86 ) | |
| 87 | |
| 88 def extractAuthority(msg, cache): | |
| 89 records = msg.answers + msg.authority + msg.additional | |
| 90 nameservers = [r for r in records if r.type == dns.NS] | |
| 91 | |
| 92 # print 'Records for', soFar, ':', records | |
| 93 # print 'NS for', soFar, ':', nameservers | |
| 94 | |
| 95 if not nameservers: | |
| 96 return None, nameservers | |
| 97 if not records: | |
| 98 raise IOError("No records") | |
| 99 for r in records: | |
| 100 if r.type == dns.A: | |
| 101 cache[str(r.name)] = r.payload.dottedQuad() | |
| 102 for r in records: | |
| 103 if r.type == dns.NS: | |
| 104 if str(r.payload.name) in cache: | |
| 105 return cache[str(r.payload.name)], nameservers | |
| 106 for addr in records: | |
| 107 if addr.type == dns.A and addr.name == r.name: | |
| 108 return addr.payload.dottedQuad(), nameservers | |
| 109 return None, nameservers | |
| 110 | |
| 111 def discoverAuthority(host, roots, cache=None, p=None): | |
| 112 if cache is None: | |
| 113 cache = {} | |
| 114 | |
| 115 rootAuths = list(roots) | |
| 116 | |
| 117 parts = host.rstrip('.').split('.') | |
| 118 parts.reverse() | |
| 119 | |
| 120 authority = rootAuths.pop() | |
| 121 | |
| 122 soFar = '' | |
| 123 for part in parts: | |
| 124 soFar = part + '.' + soFar | |
| 125 # print '///////', soFar, authority, p | |
| 126 msg = defer.waitForDeferred(lookupNameservers(soFar, authority, p)) | |
| 127 yield msg | |
| 128 msg = msg.getResult() | |
| 129 | |
| 130 newAuth, nameservers = extractAuthority(msg, cache) | |
| 131 | |
| 132 if newAuth is not None: | |
| 133 # print "newAuth is not None" | |
| 134 authority = newAuth | |
| 135 else: | |
| 136 if nameservers: | |
| 137 r = str(nameservers[0].payload.name) | |
| 138 # print 'Recursively discovering authority for', r | |
| 139 authority = defer.waitForDeferred(discoverAuthority(r, roots, ca
che, p)) | |
| 140 yield authority | |
| 141 authority = authority.getResult() | |
| 142 # print 'Discovered to be', authority, 'for', r | |
| 143 ## else: | |
| 144 ## # print 'Doing address lookup for', soFar, 'at', authority | |
| 145 ## msg = defer.waitForDeferred(lookupAddress(soFar, authority, p)
) | |
| 146 ## yield msg | |
| 147 ## msg = msg.getResult() | |
| 148 ## records = msg.answers + msg.authority + msg.additional | |
| 149 ## addresses = [r for r in records if r.type == dns.A] | |
| 150 ## if addresses: | |
| 151 ## authority = addresses[0].payload.dottedQuad() | |
| 152 ## else: | |
| 153 ## raise IOError("Resolution error") | |
| 154 # print "Yielding authority", authority | |
| 155 yield authority | |
| 156 | |
| 157 discoverAuthority = defer.deferredGenerator(discoverAuthority) | |
| 158 | |
| 159 def makePlaceholder(deferred, name): | |
| 160 def placeholder(*args, **kw): | |
| 161 deferred.addCallback(lambda r: getattr(r, name)(*args, **kw)) | |
| 162 return deferred | |
| 163 return placeholder | |
| 164 | |
| 165 class DeferredResolver: | |
| 166 def __init__(self, resolverDeferred): | |
| 167 self.waiting = [] | |
| 168 resolverDeferred.addCallback(self.gotRealResolver) | |
| 169 | |
| 170 def gotRealResolver(self, resolver): | |
| 171 w = self.waiting | |
| 172 self.__dict__ = resolver.__dict__ | |
| 173 self.__class__ = resolver.__class__ | |
| 174 for d in w: | |
| 175 d.callback(resolver) | |
| 176 | |
| 177 def __getattr__(self, name): | |
| 178 if name.startswith('lookup') or name in ('getHostByName', 'query'): | |
| 179 self.waiting.append(defer.Deferred()) | |
| 180 return makePlaceholder(self.waiting[-1], name) | |
| 181 raise AttributeError(name) | |
| 182 | |
| 183 def bootstrap(resolver): | |
| 184 """Lookup the root nameserver addresses using the given resolver | |
| 185 | |
| 186 Return a Resolver which will eventually become a C{root.Resolver} | |
| 187 instance that has references to all the root servers that we were able | |
| 188 to look up. | |
| 189 """ | |
| 190 domains = [chr(ord('a') + i) for i in range(13)] | |
| 191 # f = lambda r: (log.msg('Root server address: ' + str(r)), r)[1] | |
| 192 f = lambda r: r | |
| 193 L = [resolver.getHostByName('%s.root-servers.net' % d).addCallback(f) for d
in domains] | |
| 194 d = defer.DeferredList(L) | |
| 195 d.addCallback(lambda r: Resolver([e[1] for e in r if e[0]])) | |
| 196 return DeferredResolver(d) | |
| 197 | |
| 198 if __name__ == '__main__': | |
| 199 if len(sys.argv) < 2: | |
| 200 print 'Specify a domain' | |
| 201 else: | |
| 202 log.startLogging(sys.stdout) | |
| 203 from twisted.names.client import ThreadedResolver | |
| 204 r = bootstrap(ThreadedResolver()) | |
| 205 d = r.lookupAddress(sys.argv[1]) | |
| 206 d.addCallbacks(log.msg, log.err).addBoth(lambda _: reactor.stop()) | |
| 207 from twisted.internet import reactor | |
| 208 reactor.run() | |
| OLD | NEW |