root / trunk / twisted / names / root.py

Revision 26569, 6.3 kB (checked in by exarkun, 3 months ago)

Merge root-main-3712

Author: cary
Reviewer: thijs, exarkun
Fixes: #3712

Remove the __main__ from twisted/names/root.py.

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