source: package_tags/invirt-dns/0.0.5/invirt-dns @ 2941

Last change on this file since 2941 was 1478, checked in by broder, 16 years ago

Punt the passup option from the DNS since the zone file can be used
for the same thing

  • Property svn:executable set to *
File size: 5.4 KB
Line 
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.names import authority
7from twisted.internet import defer
8from twisted.python import failure
9
10from invirt.config import structs as config
11import invirt.database
12import psycopg2
13import sqlalchemy
14import time
15
16class DatabaseAuthority(common.ResolverBase):
17    """An Authority that is loaded from a file."""
18
19    soa = None
20
21    def __init__(self, domains=None, database=None):
22        common.ResolverBase.__init__(self)
23        if database is not None:
24            invirt.database.connect(database)
25        else:
26            invirt.database.connect()
27        if domains is not None:
28            self.domains = domains
29        else:
30            self.domains = config.dns.domains
31        ns = config.dns.nameservers[0]
32        self.soa = dns.Record_SOA(mname=ns.hostname,
33                                  rname=config.dns.contact.replace('@','.',1),
34                                  serial=1, refresh=3600, retry=900,
35                                  expire=3600000, minimum=21600, ttl=3600)
36        self.ns = dns.Record_NS(name=ns.hostname, ttl=3600)
37        record = dns.Record_A(address=ns.ip, ttl=3600)
38        self.ns1 = dns.RRHeader(ns.hostname, dns.A, dns.IN,
39                                3600, record, auth=True)
40
41   
42    def _lookup(self, name, cls, type, timeout = None):
43        for i in range(3):
44            try:
45                value = self._lookup_unsafe(name, cls, type, timeout = None)
46            except (psycopg2.OperationalError, sqlalchemy.exceptions.SQLError):
47                if i == 2:
48                    raise
49                print "Reloading database"
50                time.sleep(0.5)
51                continue
52            else:
53                return value
54
55    def _lookup_unsafe(self, name, cls, type, timeout):
56        invirt.database.clear_cache()
57       
58        ttl = 900
59        name = name.lower()
60
61        if name in self.domains:
62            domain = name
63        else:
64            # Look for the longest-matching domain.  (This works because domain
65            # will remain bound after breaking out of the loop.)
66            best_domain = ''
67            for domain in self.domains:
68                if name.endswith('.'+domain) and len(domain) > len(best_domain):
69                    best_domain = domain
70            if best_domain == '':
71                return defer.fail(failure.Failure(dns.DomainError(name)))
72            domain = best_domain
73        results = []
74        authority = []
75        additional = [self.ns1]
76        authority.append(dns.RRHeader(domain, dns.NS, dns.IN,
77                                      3600, self.ns, auth=True))
78
79        if cls == dns.IN:
80            host = name[:-len(domain)-1]
81            if not host: # Request for the domain itself.
82                if type in (dns.A, dns.ALL_RECORDS):
83                    record = dns.Record_A(config.dns.nameservers[0].ip, ttl)
84                    results.append(dns.RRHeader(name, dns.A, dns.IN, 
85                                                ttl, record, auth=True))
86                elif type == dns.NS:
87                    results.append(dns.RRHeader(domain, dns.NS, dns.IN,
88                                                ttl, self.ns, auth=True))
89                    authority = []
90                elif type == dns.SOA:
91                    results.append(dns.RRHeader(domain, dns.SOA, dns.IN,
92                                                ttl, self.soa, auth=True))
93            else: # Request for a subdomain.
94                value = invirt.database.Machine.query().filter_by(name=host).first()
95                if value is None or not value.nics:
96                    return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name)))
97                ip = value.nics[0].ip
98                if ip is None:  #Deactivated?
99                    return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name)))
100
101                if type in (dns.A, dns.ALL_RECORDS):
102                    record = dns.Record_A(ip, ttl)
103                    results.append(dns.RRHeader(name, dns.A, dns.IN, 
104                                                ttl, record, auth=True))
105                elif type == dns.SOA:
106                    results.append(dns.RRHeader(domain, dns.SOA, dns.IN,
107                                                ttl, self.soa, auth=True))
108            if len(results) == 0:
109                authority = []
110                additional = []
111            return defer.succeed((results, authority, additional))
112        else:
113            #Doesn't exist
114            return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name)))
115
116if '__main__' == __name__:
117    resolvers = []
118    for zone in config.dns.zone_files:
119        for origin in config.dns.domains:
120            r = authority.BindAuthority(zone)
121            # This sucks, but if I want a generic zone file, I have to
122            # reload the information by hand
123            r.origin = origin
124            lines = open(zone).readlines()
125            lines = r.collapseContinuations(r.stripComments(lines))
126            r.parseLines(lines)
127           
128            resolvers.append(r)
129    resolvers.append(DatabaseAuthority())
130
131    verbosity = 0
132    f = server.DNSServerFactory(authorities=resolvers, verbose=verbosity)
133    p = dns.DNSDatagramProtocol(f)
134    f.noisy = p.noisy = verbosity
135   
136    reactor.listenUDP(53, p)
137    reactor.listenTCP(53, f)
138    reactor.run()
Note: See TracBrowser for help on using the repository browser.