1 # -*- test-case-name: twisted.names.test.test_names -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
9 - Better config file format maybe
10 - Make sure to differentiate between different classes
11 - notice truncation bit
13 Important: No additional processing is done on some of the record types.
14 This violates the most basic RFC and is just plain annoying
15 for resolvers to deal with. Fix it.
22 from twisted.internet import protocol
23 from twisted.names import dns, resolve
24 from twisted.python import log
27 class DNSServerFactory(protocol.ServerFactory):
29 Server factory and tracker for L{DNSProtocol} connections. This
30 class also provides records for responses to DNS queries.
32 @ivar connections: A list of all the connected L{DNSProtocol}
33 instances using this object as their controller.
34 @type connections: C{list} of L{DNSProtocol}
37 protocol = dns.DNSProtocol
40 def __init__(self, authorities = None, caches = None, clients = None, verbose = 0):
42 if authorities is not None:
43 resolvers.extend(authorities)
44 if caches is not None:
45 resolvers.extend(caches)
46 if clients is not None:
47 resolvers.extend(clients)
49 self.canRecurse = not not clients
50 self.resolver = resolve.ResolverChain(resolvers)
51 self.verbose = verbose
53 self.cache = caches[-1]
57 def buildProtocol(self, addr):
58 p = self.protocol(self)
63 def connectionMade(self, protocol):
65 Track a newly connected L{DNSProtocol}.
67 self.connections.append(protocol)
70 def connectionLost(self, protocol):
72 Stop tracking a no-longer connected L{DNSProtocol}.
74 self.connections.remove(protocol)
77 def sendReply(self, protocol, message, address):
79 s = ' '.join([str(a.payload) for a in message.answers])
80 auth = ' '.join([str(a.payload) for a in message.authority])
81 add = ' '.join([str(a.payload) for a in message.additional])
83 log.msg("Replying with no answers")
85 log.msg("Answers are " + s)
86 log.msg("Authority is " + auth)
87 log.msg("Additional is " + add)
90 protocol.writeMessage(message)
92 protocol.writeMessage(message, address)
95 log.msg("Processed query in %0.3f seconds" % (time.time() - message.timeReceived))
98 def gotResolverResponse(self, (ans, auth, add), protocol, message, address):
99 message.rCode = dns.OK
100 message.answers = ans
102 if x.isAuthoritative():
105 message.authority = auth
106 message.additional = add
107 self.sendReply(protocol, message, address)
109 l = len(ans) + len(auth) + len(add)
111 log.msg("Lookup found %d record%s" % (l, l != 1 and "s" or ""))
114 self.cache.cacheResult(
115 message.queries[0], (ans, auth, add)
119 def gotResolverError(self, failure, protocol, message, address):
120 if failure.check(dns.DomainError, dns.AuthoritativeDomainError):
121 message.rCode = dns.ENAME
123 message.rCode = dns.ESERVER
126 self.sendReply(protocol, message, address)
128 log.msg("Lookup failed")
131 def handleQuery(self, message, protocol, address):
132 # Discard all but the first query! HOO-AAH HOOOOO-AAAAH
133 # (no other servers implement multi-query messages, so we won't either)
134 query = message.queries[0]
136 return self.resolver.query(query).addCallback(
137 self.gotResolverResponse, protocol, message, address
139 self.gotResolverError, protocol, message, address
143 def handleInverseQuery(self, message, protocol, address):
144 message.rCode = dns.ENOTIMP
145 self.sendReply(protocol, message, address)
147 log.msg("Inverse query from %r" % (address,))
150 def handleStatus(self, message, protocol, address):
151 message.rCode = dns.ENOTIMP
152 self.sendReply(protocol, message, address)
154 log.msg("Status request from %r" % (address,))
157 def handleNotify(self, message, protocol, address):
158 message.rCode = dns.ENOTIMP
159 self.sendReply(protocol, message, address)
161 log.msg("Notify message from %r" % (address,))
164 def handleOther(self, message, protocol, address):
165 message.rCode = dns.ENOTIMP
166 self.sendReply(protocol, message, address)
168 log.msg("Unknown op code (%d) from %r" % (message.opCode, address))
171 def messageReceived(self, message, proto, address = None):
172 message.timeReceived = time.time()
176 s = ' '.join([str(q) for q in message.queries])
177 elif self.verbose > 0:
178 s = ' '.join([dns.QUERY_TYPES.get(q.type, 'UNKNOWN') for q in message.queries])
181 log.msg("Empty query from %r" % ((address or proto.transport.getPeer()),))
183 log.msg("%s query from %r" % (s, address or proto.transport.getPeer()))
185 message.recAv = self.canRecurse
188 if not self.allowQuery(message, proto, address):
189 message.rCode = dns.EREFUSED
190 self.sendReply(proto, message, address)
191 elif message.opCode == dns.OP_QUERY:
192 self.handleQuery(message, proto, address)
193 elif message.opCode == dns.OP_INVERSE:
194 self.handleInverseQuery(message, proto, address)
195 elif message.opCode == dns.OP_STATUS:
196 self.handleStatus(message, proto, address)
197 elif message.opCode == dns.OP_NOTIFY:
198 self.handleNotify(message, proto, address)
200 self.handleOther(message, proto, address)
203 def allowQuery(self, message, protocol, address):
204 # Allow anything but empty queries
205 return len(message.queries)