| 1 | #!/usr/bin/python | 
|---|
| 2 | from twisted.internet import reactor | 
|---|
| 3 | from twisted.names import server | 
|---|
| 4 | from twisted.names import dns | 
|---|
| 5 | from twisted.names import common | 
|---|
| 6 | from twisted.internet import defer | 
|---|
| 7 | from twisted.python import failure | 
|---|
| 8 |  | 
|---|
| 9 | import sipb_xen_database | 
|---|
| 10 | import psycopg2 | 
|---|
| 11 | import sqlalchemy | 
|---|
| 12 | import time | 
|---|
| 13 |  | 
|---|
| 14 | class DatabaseAuthority(common.ResolverBase): | 
|---|
| 15 |     """An Authority that is loaded from a file.""" | 
|---|
| 16 |  | 
|---|
| 17 |     soa = None | 
|---|
| 18 |  | 
|---|
| 19 |     def __init__(self, domains, database=None): | 
|---|
| 20 |         common.ResolverBase.__init__(self) | 
|---|
| 21 |         if database is not None: | 
|---|
| 22 |             sipb_xen_database.connect(database) | 
|---|
| 23 |         self.domains = domains | 
|---|
| 24 |         self.soa = dns.Record_SOA(mname='sipb-xen-dev.mit.edu',  | 
|---|
| 25 |                                   rname='sipb-xen.mit.edu', | 
|---|
| 26 |                                   serial=1, refresh=3600, retry=900, | 
|---|
| 27 |                                   expire=3600000, minimum=21600, ttl=3600) | 
|---|
| 28 |     def _lookup(self, name, cls, type, timeout = None): | 
|---|
| 29 |         for i in range(3): | 
|---|
| 30 |             try: | 
|---|
| 31 |                 value = self._lookup_unsafe(name, cls, type, timeout = None) | 
|---|
| 32 |             except (psycopg2.OperationalError, sqlalchemy.exceptions.SQLError): | 
|---|
| 33 |                 if i == 2: | 
|---|
| 34 |                     raise | 
|---|
| 35 |                 print "Reloading database" | 
|---|
| 36 |                 time.sleep(0.5) | 
|---|
| 37 |                 continue | 
|---|
| 38 |             else: | 
|---|
| 39 |                 return value | 
|---|
| 40 |  | 
|---|
| 41 |     def _lookup_unsafe(self, name, cls, type, timeout): | 
|---|
| 42 |         sipb_xen_database.clear_cache() | 
|---|
| 43 |         if name.lower() in self.domains: | 
|---|
| 44 |             domain = name.lower() | 
|---|
| 45 |         else: | 
|---|
| 46 |             for domain in self.domains: | 
|---|
| 47 |                 if name.lower().endswith('.'+domain): | 
|---|
| 48 |                     break | 
|---|
| 49 |             else: #Not us | 
|---|
| 50 |                 return defer.fail(failure.Failure(dns.DomainError(name))) | 
|---|
| 51 |         results = [] | 
|---|
| 52 |         if cls == dns.IN and type in (dns.A, dns.ALL_RECORDS): | 
|---|
| 53 |             host = name[:-len(domain)-1] | 
|---|
| 54 |             if not host: | 
|---|
| 55 |                 ttl = 900 | 
|---|
| 56 |                 record = dns.Record_CNAME('sipb-xen-dev.mit.edu', ttl) | 
|---|
| 57 |                 results.append(dns.RRHeader(name, dns.CNAME, dns.IN,  | 
|---|
| 58 |                                             ttl, record, auth=True)) | 
|---|
| 59 |             else: | 
|---|
| 60 |                 value = sipb_xen_database.Machine.get_by(name=host) | 
|---|
| 61 |                 if value is None or not value.nics: | 
|---|
| 62 |                     return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name))) | 
|---|
| 63 |                 ip = value.nics[0].ip | 
|---|
| 64 |                 if ip is None:  #Deactivated? | 
|---|
| 65 |                     return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name))) | 
|---|
| 66 |                 ttl = 900 | 
|---|
| 67 |                 record = dns.Record_A(ip, ttl) | 
|---|
| 68 |                 results.append(dns.RRHeader(name, dns.A, dns.IN,  | 
|---|
| 69 |                                             ttl, record, auth=True)) | 
|---|
| 70 |         authority = [] | 
|---|
| 71 |         auth_record = dns.Record_NS(name='ns1.xvm.mit.edu', ttl=3600) | 
|---|
| 72 |         authority.append(dns.RRHeader(domain, dns.NS, dns.IN, | 
|---|
| 73 |                                       3600, auth_record, auth=True)) | 
|---|
| 74 |         return defer.succeed((results, authority, [])) | 
|---|
| 75 |         #Doesn't exist | 
|---|
| 76 |         return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name))) | 
|---|
| 77 |  | 
|---|
| 78 | if '__main__' == __name__: | 
|---|
| 79 |     resolver = DatabaseAuthority(['servers.csail.mit.edu', | 
|---|
| 80 |                                   'xvm.mit.edu'], | 
|---|
| 81 |                                  'postgres://sipb-xen@sipb-xen-dev/sipb_xen') | 
|---|
| 82 |  | 
|---|
| 83 |     verbosity = 0 | 
|---|
| 84 |     f = server.DNSServerFactory(authorities=[resolver], verbose=verbosity) | 
|---|
| 85 |     p = dns.DNSDatagramProtocol(f) | 
|---|
| 86 |     f.noisy = p.noisy = verbosity | 
|---|
| 87 |      | 
|---|
| 88 |     reactor.listenUDP(53, p) | 
|---|
| 89 |     reactor.listenTCP(53, f) | 
|---|
| 90 |     reactor.run() | 
|---|