1 # -*- test-case-name: twisted.names.test.test_tap -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
11 from twisted.python import usage
12 from twisted.names import dns
13 from twisted.application import internet, service
15 from twisted.names import server
16 from twisted.names import authority
17 from twisted.names import secondary
19 class Options(usage.Options):
21 ["interface", "i", "", "The interface to which to bind"],
22 ["port", "p", "53", "The port on which to listen"],
23 ["resolv-conf", None, None,
24 "Override location of resolv.conf (implies --recursive)"],
25 ["hosts-file", None, None, "Perform lookups with a hosts file"],
29 ["cache", "c", "Enable record caching"],
30 ["recursive", "r", "Perform recursive lookups"],
31 ["verbose", "v", "Log verbosely"],
34 compData = usage.Completions(
35 optActions={"interface" : usage.CompleteNetInterfaces()}
42 usage.Options.__init__(self)
49 def opt_pyzone(self, filename):
50 """Specify the filename of a Python syntax zone definition"""
51 if not os.path.exists(filename):
52 raise usage.UsageError(filename + ": No such file")
53 self.zonefiles.append(filename)
55 def opt_bindzone(self, filename):
56 """Specify the filename of a BIND9 syntax zone definition"""
57 if not os.path.exists(filename):
58 raise usage.UsageError(filename + ": No such file")
59 self.bindfiles.append(filename)
62 def opt_secondary(self, ip_domain):
63 """Act as secondary for the specified domain, performing
64 zone transfers from the specified IP (IP/domain)
66 args = ip_domain.split('/', 1)
68 raise usage.UsageError("Argument must be of the form IP[:port]/domain")
69 address = args[0].split(':')
71 address = (address[0], dns.PORT)
74 port = int(address[1])
76 raise usage.UsageError(
77 "Specify an integer port number, not %r" % (address[1],))
78 address = (address[0], port)
79 self.secondaries.append((address, [args[1]]))
82 def opt_verbose(self):
83 """Increment verbosity level"""
87 def postOptions(self):
88 if self['resolv-conf']:
89 self['recursive'] = True
93 for f in self.zonefiles:
95 self.zones.append(authority.PySourceAuthority(f))
98 raise usage.UsageError("Invalid syntax in " + f)
99 for f in self.bindfiles:
101 self.zones.append(authority.BindAuthority(f))
103 traceback.print_exc()
104 raise usage.UsageError("Invalid syntax in " + f)
105 for f in self.secondaries:
106 svc = secondary.SecondaryAuthorityService.fromServerAddressAndDomains(*f)
107 self.svcs.append(svc)
108 self.zones.append(self.svcs[-1].getAuthority())
110 self['port'] = int(self['port'])
112 raise usage.UsageError("Invalid port: %r" % (self['port'],))
115 def _buildResolvers(config):
117 Build DNS resolver instances in an order which leaves recursive
118 resolving as a last resort.
120 @type config: L{Options} instance
121 @param config: Parsed command-line configuration
123 @return: Two-item tuple of a list of cache resovers and a list of client
126 from twisted.names import client, cache, hosts
130 ca.append(cache.CacheResolver(verbose=config['verbose']))
131 if config['hosts-file']:
132 cl.append(hosts.Resolver(file=config['hosts-file']))
133 if config['recursive']:
134 cl.append(client.createResolver(resolvconf=config['resolv-conf']))
138 def makeService(config):
139 ca, cl = _buildResolvers(config)
141 f = server.DNSServerFactory(config.zones, ca, cl, config['verbose'])
142 p = dns.DNSDatagramProtocol(f)
144 ret = service.MultiService()
145 for (klass, arg) in [(internet.TCPServer, f), (internet.UDPServer, p)]:
146 s = klass(config['port'], arg, interface=config['interface'])
147 s.setServiceParent(ret)
148 for svc in config.svcs:
149 svc.setServiceParent(ret)