source: trunk/packages/sipb-xen-dns/code/dnsserver.py @ 919

Last change on this file since 919 was 913, checked in by geofft, 16 years ago

Add "passup" and "parent" options to the DNS config, to allow forwarding
certain names as CNAME to a parent domain. This was implemented for the Vista
activation server SRV record, but can be used to pass on any well-known
reserved names.

  • Property svn:executable set to *
File size: 5.0 KB
RevLine 
[181]1#!/usr/bin/python
2from twisted.internet import reactor
3from twisted.names import server
4from twisted.names import dns
5from twisted.names import common
6from twisted.internet import defer
7from twisted.python import failure
8
[851]9from invirt.config import structs as config
10import invirt.database
[302]11import psycopg2
12import sqlalchemy
13import time
[181]14
15class DatabaseAuthority(common.ResolverBase):
16    """An Authority that is loaded from a file."""
17
18    soa = None
19
[851]20    def __init__(self, domains=None, database=None):
[181]21        common.ResolverBase.__init__(self)
22        if database is not None:
[851]23            invirt.database.connect(database)
24        else:
25            invirt.database.connect()
26        if domains is not None:
27            self.domains = domains
28        else:
29            self.domains = config.dns.domains
30        ns = config.dns.nameservers[0]
31        self.soa = dns.Record_SOA(mname=ns.hostname,
32                                  rname=config.dns.contact.replace('@','.',1),
[181]33                                  serial=1, refresh=3600, retry=900,
34                                  expire=3600000, minimum=21600, ttl=3600)
[851]35        self.ns = dns.Record_NS(name=ns.hostname, ttl=3600)
36        record = dns.Record_A(address=ns.ip, ttl=3600)
37        self.ns1 = dns.RRHeader(ns.hostname, dns.A, dns.IN,
[645]38                                3600, record, auth=True)
39
[582]40   
[181]41    def _lookup(self, name, cls, type, timeout = None):
[302]42        for i in range(3):
43            try:
44                value = self._lookup_unsafe(name, cls, type, timeout = None)
45            except (psycopg2.OperationalError, sqlalchemy.exceptions.SQLError):
46                if i == 2:
47                    raise
48                print "Reloading database"
49                time.sleep(0.5)
50                continue
51            else:
52                return value
53
54    def _lookup_unsafe(self, name, cls, type, timeout):
[851]55        invirt.database.clear_cache()
[582]56       
57        ttl = 900
[646]58        name = name.lower()
59        if name in self.domains:
60            domain = name
[505]61        else:
[582]62            # This works because domain will remain bound after breaking out of the loop
[505]63            for domain in self.domains:
[646]64                if name.endswith('.'+domain):
[505]65                    break
[508]66            else: #Not us
[505]67                return defer.fail(failure.Failure(dns.DomainError(name)))
[181]68        results = []
69        authority = []
[645]70        additional = [self.ns1]
[541]71        authority.append(dns.RRHeader(domain, dns.NS, dns.IN,
[582]72                                      3600, self.ns, auth=True))
73        if cls == dns.IN:
[651]74            host = name[:-len(domain)-1]
75            if not host:
76                if type in (dns.A, dns.ALL_RECORDS):
[851]77                    record = dns.Record_A(config.dns.nameservers[0].ip, ttl)
[643]78                    results.append(dns.RRHeader(name, dns.A, dns.IN, 
[582]79                                                ttl, record, auth=True))
[651]80                elif type == dns.NS:
81                    results.append(dns.RRHeader(domain, dns.NS, dns.IN,
82                                                ttl, self.ns, auth=True))
83                    authority = []
84                elif type == dns.SOA:
85                    results.append(dns.RRHeader(domain, dns.SOA, dns.IN,
86                                                ttl, self.soa, auth=True))
87            else:
[913]88                if 'passup' in dir(config.dns) and host in config.dns.passup:
89                    record = dns.Record_CNAME('%s.%s' % (host, config.dns.parent), ttl)
90                    return defer.succeed((
91                        [dns.RRHeader(name, dns.CNAME, dns.IN, ttl, record, auth=True)],
92                        [], []))
93
[651]94                if host:
[851]95                    value = invirt.database.Machine.get_by(name=host)
[582]96                    if value is None or not value.nics:
97                        return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name)))
98                    ip = value.nics[0].ip
99                    if ip is None:  #Deactivated?
100                        return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name)))
[651]101                if type in (dns.A, dns.ALL_RECORDS):
[582]102                    record = dns.Record_A(ip, ttl)
103                    results.append(dns.RRHeader(name, dns.A, dns.IN, 
104                                                ttl, record, auth=True))
[651]105                elif type == dns.SOA:
106                    results.append(dns.RRHeader(domain, dns.SOA, dns.IN,
107                                                ttl, self.soa, auth=True))
[650]108            if len(results) == 0:
109                authority = []
110                additional = []
[582]111            return defer.succeed((results, authority, additional))
112        else:
113            #Doesn't exist
114            return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name)))
[181]115
116if '__main__' == __name__:
[851]117    resolver = DatabaseAuthority()
[181]118
119    verbosity = 0
120    f = server.DNSServerFactory(authorities=[resolver], verbose=verbosity)
121    p = dns.DNSDatagramProtocol(f)
122    f.noisy = p.noisy = verbosity
123   
124    reactor.listenUDP(53, p)
125    reactor.listenTCP(53, f)
126    reactor.run()
Note: See TracBrowser for help on using the repository browser.