OLD | NEW |
| (Empty) |
1 # Copyright (c) 2001-2006 Twisted Matrix Laboratories. | |
2 # See LICENSE for details. | |
3 | |
4 from twisted.internet import task, defer | |
5 from twisted.names import dns | |
6 from twisted.names import common | |
7 from twisted.names import client | |
8 from twisted.names import resolve | |
9 from twisted.python import log, failure | |
10 from twisted.application import service | |
11 | |
12 class SecondaryAuthorityService(service.Service): | |
13 calls = None | |
14 | |
15 def __init__(self, primary, domains): | |
16 """ | |
17 @param primary: The IP address of the server from which to perform | |
18 zone transfers. | |
19 | |
20 @param domains: A sequence of domain names for which to perform | |
21 zone transfers. | |
22 """ | |
23 self.primary = primary | |
24 self.domains = [SecondaryAuthority(primary, d) for d in domains] | |
25 | |
26 def getAuthority(self): | |
27 return resolve.ResolverChain(self.domains) | |
28 | |
29 def startService(self): | |
30 service.Service.startService(self) | |
31 self.calls = [task.LoopingCall(d.transfer) for d in self.domains] | |
32 i = 0 | |
33 from twisted.internet import reactor | |
34 for c in self.calls: | |
35 # XXX Add errbacks, respect proper timeouts | |
36 reactor.callLater(i, c.start, 60 * 60) | |
37 i += 1 | |
38 | |
39 def stopService(self): | |
40 service.Service.stopService(self) | |
41 for c in self.calls: | |
42 c.stop() | |
43 | |
44 | |
45 from twisted.names.authority import FileAuthority | |
46 | |
47 class SecondaryAuthority(common.ResolverBase): | |
48 """An Authority that keeps itself updated by performing zone transfers""" | |
49 | |
50 transferring = False | |
51 | |
52 soa = records = None | |
53 def __init__(self, primaryIP, domain): | |
54 common.ResolverBase.__init__(self) | |
55 self.primary = primaryIP | |
56 self.domain = domain | |
57 | |
58 def transfer(self): | |
59 if self.transferring: | |
60 return | |
61 self.transfering = True | |
62 return client.Resolver(servers=[(self.primary, dns.PORT)] | |
63 ).lookupZone(self.domain | |
64 ).addCallback(self._cbZone | |
65 ).addErrback(self._ebZone | |
66 ) | |
67 | |
68 | |
69 def _lookup(self, name, cls, type, timeout=None): | |
70 if not self.soa or not self.records: | |
71 return defer.fail(failure.Failure(dns.DomainError(name))) | |
72 | |
73 | |
74 return FileAuthority.__dict__['_lookup'](self, name, cls, type, timeout) | |
75 | |
76 #shouldn't we just subclass? :P | |
77 | |
78 lookupZone = FileAuthority.__dict__['lookupZone'] | |
79 | |
80 def _cbZone(self, zone): | |
81 ans, _, _ = zone | |
82 self.records = r = {} | |
83 for rec in ans: | |
84 if not self.soa and rec.type == dns.SOA: | |
85 self.soa = (str(rec.name).lower(), rec.payload) | |
86 else: | |
87 r.setdefault(str(rec.name).lower(), []).append(rec.payload) | |
88 | |
89 def _ebZone(self, failure): | |
90 log.msg("Updating %s from %s failed during zone transfer" % (self.domain
, self.primary)) | |
91 log.err(failure) | |
92 | |
93 def update(self): | |
94 self.transfer().addCallbacks(self._cbTransferred, self._ebTransferred) | |
95 | |
96 def _cbTransferred(self, result): | |
97 self.transferring = False | |
98 | |
99 def _ebTransferred(self, failure): | |
100 self.transferred = False | |
101 log.msg("Transferring %s from %s failed after zone transfer" % (self.dom
ain, self.primary)) | |
102 log.err(failure) | |
OLD | NEW |