| 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 |         self.ns = dns.Record_NS(name='ns1.xvm.mit.edu', ttl=3600) | 
|---|
| 29 |         record = dns.Record_A(address='18.181.0.62', ttl=3600) | 
|---|
| 30 |         self.ns1 = dns.RRHeader('ns1.xvm.mit.edu', dns.A, dns.IN, | 
|---|
| 31 |                                 3600, record, auth=True) | 
|---|
| 32 |  | 
|---|
| 33 |      | 
|---|
| 34 |     def _lookup(self, name, cls, type, timeout = None): | 
|---|
| 35 |         for i in range(3): | 
|---|
| 36 |             try: | 
|---|
| 37 |                 value = self._lookup_unsafe(name, cls, type, timeout = None) | 
|---|
| 38 |             except (psycopg2.OperationalError, sqlalchemy.exceptions.SQLError): | 
|---|
| 39 |                 if i == 2: | 
|---|
| 40 |                     raise | 
|---|
| 41 |                 print "Reloading database" | 
|---|
| 42 |                 time.sleep(0.5) | 
|---|
| 43 |                 continue | 
|---|
| 44 |             else: | 
|---|
| 45 |                 return value | 
|---|
| 46 |  | 
|---|
| 47 |     def _lookup_unsafe(self, name, cls, type, timeout): | 
|---|
| 48 |         sipb_xen_database.clear_cache() | 
|---|
| 49 |          | 
|---|
| 50 |         ttl = 900 | 
|---|
| 51 |         name = name.lower() | 
|---|
| 52 |         if name in self.domains: | 
|---|
| 53 |             domain = name | 
|---|
| 54 |         else: | 
|---|
| 55 |             # This works because domain will remain bound after breaking out of the loop | 
|---|
| 56 |             for domain in self.domains: | 
|---|
| 57 |                 if name.endswith('.'+domain): | 
|---|
| 58 |                     break | 
|---|
| 59 |             else: #Not us | 
|---|
| 60 |                 return defer.fail(failure.Failure(dns.DomainError(name))) | 
|---|
| 61 |         results = [] | 
|---|
| 62 |         authority = [] | 
|---|
| 63 |         additional = [self.ns1] | 
|---|
| 64 |         authority.append(dns.RRHeader(domain, dns.NS, dns.IN, | 
|---|
| 65 |                                       3600, self.ns, auth=True)) | 
|---|
| 66 |         if cls == dns.IN: | 
|---|
| 67 |             host = name[:-len(domain)-1] | 
|---|
| 68 |             if not host: | 
|---|
| 69 |                 if type in (dns.A, dns.ALL_RECORDS): | 
|---|
| 70 |                     record = dns.Record_A('18.181.0.62', ttl) | 
|---|
| 71 |                     results.append(dns.RRHeader(name, dns.A, dns.IN,  | 
|---|
| 72 |                                                 ttl, record, auth=True)) | 
|---|
| 73 |                 elif type == dns.NS: | 
|---|
| 74 |                     results.append(dns.RRHeader(domain, dns.NS, dns.IN, | 
|---|
| 75 |                                                 ttl, self.ns, auth=True)) | 
|---|
| 76 |                     authority = [] | 
|---|
| 77 |                 elif type == dns.SOA: | 
|---|
| 78 |                     results.append(dns.RRHeader(domain, dns.SOA, dns.IN, | 
|---|
| 79 |                                                 ttl, self.soa, auth=True)) | 
|---|
| 80 |             else: | 
|---|
| 81 |                 if host: | 
|---|
| 82 |                     value = sipb_xen_database.Machine.get_by(name=host) | 
|---|
| 83 |                     if value is None or not value.nics: | 
|---|
| 84 |                         return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name))) | 
|---|
| 85 |                     ip = value.nics[0].ip | 
|---|
| 86 |                     if ip is None:  #Deactivated? | 
|---|
| 87 |                         return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name))) | 
|---|
| 88 |                 if type in (dns.A, dns.ALL_RECORDS): | 
|---|
| 89 |                     record = dns.Record_A(ip, ttl) | 
|---|
| 90 |                     results.append(dns.RRHeader(name, dns.A, dns.IN,  | 
|---|
| 91 |                                                 ttl, record, auth=True)) | 
|---|
| 92 |                 elif type == dns.SOA: | 
|---|
| 93 |                     results.append(dns.RRHeader(domain, dns.SOA, dns.IN, | 
|---|
| 94 |                                                 ttl, self.soa, auth=True)) | 
|---|
| 95 |             if len(results) == 0: | 
|---|
| 96 |                 authority = [] | 
|---|
| 97 |                 additional = [] | 
|---|
| 98 |             return defer.succeed((results, authority, additional)) | 
|---|
| 99 |         else: | 
|---|
| 100 |             #Doesn't exist | 
|---|
| 101 |             return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name))) | 
|---|
| 102 |  | 
|---|
| 103 | if '__main__' == __name__: | 
|---|
| 104 |     resolver = DatabaseAuthority(['servers.csail.mit.edu', | 
|---|
| 105 |                                   'xvm.mit.edu'], | 
|---|
| 106 |                                  'postgres://sipb-xen@sipb-xen-dev/sipb_xen') | 
|---|
| 107 |  | 
|---|
| 108 |     verbosity = 0 | 
|---|
| 109 |     f = server.DNSServerFactory(authorities=[resolver], verbose=verbosity) | 
|---|
| 110 |     p = dns.DNSDatagramProtocol(f) | 
|---|
| 111 |     f.noisy = p.noisy = verbosity | 
|---|
| 112 |      | 
|---|
| 113 |     reactor.listenUDP(53, p) | 
|---|
| 114 |     reactor.listenTCP(53, f) | 
|---|
| 115 |     reactor.run() | 
|---|