3 # Unix SMB/CIFS implementation.
4 # Copyright (C) Volker Lendecke 2017
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 # Used by selftest to proxy DNS queries to the correct testenv DC.
20 # See selftest/target/README for more details.
21 # Based on the EchoServer example from python docs
28 from samba.dcerpc import dns
29 import samba.ndr as ndr
31 if sys.version_info[0] < 3:
33 sserver = SocketServer
36 sserver = socketserver
38 DNS_REQUEST_TIMEOUT = 10
41 class DnsHandler(sserver.BaseRequestHandler):
42 dns_qtype_strings = dict((v, k) for k, v in vars(dns).items() if k.startswith('DNS_QTYPE_'))
43 def dns_qtype_string(self, qtype):
44 "Return a readable qtype code"
45 return self.dns_qtype_strings[qtype]
47 dns_rcode_strings = dict((v, k) for k, v in vars(dns).items() if k.startswith('DNS_RCODE_'))
48 def dns_rcode_string(self, rcode):
49 "Return a readable error code"
50 return self.dns_rcode_strings[rcode]
52 def dns_transaction_udp(self, packet, host):
53 "send a DNS query and read the reply"
56 send_packet = ndr.ndr_pack(packet)
57 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
58 s.settimeout(DNS_REQUEST_TIMEOUT)
60 s.sendall(send_packet, 0)
61 recv_packet = s.recv(2048, 0)
62 return ndr.ndr_unpack(dns.name_packet, recv_packet)
63 except socket.error as err:
64 print("Error sending to host %s for name %s: %s\n" %
65 (host, packet.questions[0].name, err.errno))
72 def forwarder(self, name):
75 if lname.endswith('an-address-that-will-not-resolve'):
77 if lname.endswith('dsfsdfs'):
79 if lname.endswith("torture1", 0, len(lname)-2):
80 # CATCH TORTURE100, TORTURE101, ...
82 if lname.endswith('_none_.example.com'):
84 if lname.endswith('torturedom.samba.example.com'):
86 if lname.endswith('adnonssdom.samba.example.com'):
88 if lname.endswith('adnontlmdom.samba.example.com'):
90 if lname.endswith('samba2000.example.com'):
92 if lname.endswith('samba2003.example.com'):
94 if lname.endswith('samba2008r2.example.com'):
96 if lname.endswith('addom.samba.example.com'):
98 if lname.endswith('sub.samba.example.com'):
100 if lname.endswith('chgdcpassword.samba.example.com'):
102 if lname.endswith('backupdom.samba.example.com'):
104 if lname.endswith('renamedom.samba.example.com'):
106 if lname.endswith('labdom.samba.example.com'):
108 if lname.endswith('samba.example.com'):
113 start = time.monotonic()
114 data, sock = self.request
115 query = ndr.ndr_unpack(dns.name_packet, data)
116 name = query.questions[0].name
117 forwarder = self.forwarder(name)
120 if forwarder is 'ignore':
122 elif forwarder is 'fail':
124 elif forwarder in ['torture', None]:
126 response.operation |= dns.DNS_FLAG_REPLY
127 response.operation |= dns.DNS_FLAG_RECURSION_AVAIL
128 response.operation |= dns.DNS_RCODE_NXDOMAIN
130 response = self.dns_transaction_udp(query, forwarder)
134 response.operation |= dns.DNS_FLAG_REPLY
135 response.operation |= dns.DNS_FLAG_RECURSION_AVAIL
136 response.operation |= dns.DNS_RCODE_SERVFAIL
138 send_packet = ndr.ndr_pack(response)
140 end = time.monotonic()
142 errcode = response.operation & dns.DNS_RCODE
143 if tdiff > (DNS_REQUEST_TIMEOUT/5):
148 print("dns_hub: forwarder[%s] client[%s] name[%s][%s] %s response.operation[0x%x] tdiff[%s]\n" %
149 (forwarder, self.client_address, name,
150 self.dns_qtype_string(query.questions[0].question_type),
151 self.dns_rcode_string(errcode), response.operation, tdiff))
154 sock.sendto(send_packet, self.client_address)
155 except socket.error as err:
156 print("dns_hub: Error sending response to client[%s] for name[%s] tdiff[%s]: %s\n" %
157 (self.client_address, name, tdiff, err))
160 class server_thread(threading.Thread):
161 def __init__(self, server):
162 threading.Thread.__init__(self)
166 self.server.serve_forever()
167 print("dns_hub: after serve_forever()")
171 timeout = int(sys.argv[1]) * 1000
172 timeout = min(timeout, 2**31 - 1) # poll with 32-bit int can't take more
174 server = sserver.UDPServer((host, int(53)), DnsHandler)
175 t = server_thread(server)
178 stdin = sys.stdin.fileno()
179 p.register(stdin, select.POLLIN)
181 print("dns_hub: after poll()")
184 print("dns_hub: before exit()")