s3: smbd: Move check_fsp_open() and check_fsp() to smb1_reply.c
[Samba.git] / python / samba / tests / dns_forwarder.py
blob42995d1f2aa980ce8580de0f506ccc697ceeaa02
1 # Unix SMB/CIFS implementation.
2 # Copyright (C) Kai Blin <kai@samba.org> 2011
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 import os
19 import sys
20 import random
21 import socket
22 import samba
23 import time
24 import errno
25 import samba.ndr as ndr
26 from samba import credentials
27 from samba.tests import TestCase
28 from samba.dcerpc import dns, dnsp
29 from samba.tests.subunitrun import SubunitOptions, TestProgram
30 import samba.getopt as options
31 import optparse
32 import subprocess
34 DNS_PORT2 = 54
36 parser = optparse.OptionParser("dns_forwarder.py <server name> <server ip> (dns forwarder)+ [options]")
37 sambaopts = options.SambaOptions(parser)
38 parser.add_option_group(sambaopts)
40 # This timeout only has relevance when testing against Windows
41 # Format errors tend to return patchy responses, so a timeout is needed.
42 parser.add_option("--timeout", type="int", dest="timeout",
43 help="Specify timeout for DNS requests")
45 # use command line creds if available
46 credopts = options.CredentialsOptions(parser)
47 parser.add_option_group(credopts)
48 subunitopts = SubunitOptions(parser)
49 parser.add_option_group(subunitopts)
51 opts, args = parser.parse_args()
53 lp = sambaopts.get_loadparm()
54 creds = credopts.get_credentials(lp)
56 timeout = opts.timeout
58 if len(args) < 3:
59 parser.print_usage()
60 sys.exit(1)
62 server_name = args[0]
63 server_ip = args[1]
64 dns_servers = args[2:]
66 creds.set_krb_forwardable(credentials.NO_KRB_FORWARDABLE)
69 class DNSTest(TestCase):
71 errcodes = dict((v, k) for k, v in vars(dns).items() if k.startswith('DNS_RCODE_'))
73 def assert_dns_rcode_equals(self, packet, rcode):
74 "Helper function to check return code"
75 p_errcode = packet.operation & dns.DNS_RCODE
76 self.assertEqual(p_errcode, rcode, "Expected RCODE %s, got %s" %
77 (self.errcodes[rcode], self.errcodes[p_errcode]))
79 def assert_dns_opcode_equals(self, packet, opcode):
80 "Helper function to check opcode"
81 p_opcode = packet.operation & dns.DNS_OPCODE
82 self.assertEqual(p_opcode, opcode, "Expected OPCODE %s, got %s" %
83 (opcode, p_opcode))
85 def make_name_packet(self, opcode, qid=None):
86 "Helper creating a dns.name_packet"
87 p = dns.name_packet()
88 if qid is None:
89 p.id = random.randint(0x0, 0xffff)
90 p.operation = opcode
91 p.questions = []
92 return p
94 def finish_name_packet(self, packet, questions):
95 "Helper to finalize a dns.name_packet"
96 packet.qdcount = len(questions)
97 packet.questions = questions
99 def make_name_question(self, name, qtype, qclass):
100 "Helper creating a dns.name_question"
101 q = dns.name_question()
102 q.name = name
103 q.question_type = qtype
104 q.question_class = qclass
105 return q
107 def get_dns_domain(self):
108 "Helper to get dns domain"
109 return self.creds.get_realm().lower()
111 def dns_transaction_udp(self, packet, host=server_ip,
112 dump=False, timeout=timeout):
113 "send a DNS query and read the reply"
114 s = None
115 try:
116 send_packet = ndr.ndr_pack(packet)
117 if dump:
118 print(self.hexdump(send_packet))
119 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
120 s.settimeout(timeout)
121 s.connect((host, 53))
122 s.send(send_packet, 0)
123 recv_packet = s.recv(2048, 0)
124 if dump:
125 print(self.hexdump(recv_packet))
126 return ndr.ndr_unpack(dns.name_packet, recv_packet)
127 finally:
128 if s is not None:
129 s.close()
131 def make_cname_update(self, key, value):
132 p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
134 name = self.get_dns_domain()
135 u = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
136 self.finish_name_packet(p, [u])
138 r = dns.res_rec()
139 r.name = key
140 r.rr_type = dns.DNS_QTYPE_CNAME
141 r.rr_class = dns.DNS_QCLASS_IN
142 r.ttl = 900
143 r.length = 0xffff
144 rdata = value
145 r.rdata = rdata
146 p.nscount = 1
147 p.nsrecs = [r]
148 response = self.dns_transaction_udp(p)
149 self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
152 def contact_real_server(host, port):
153 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
154 s.connect((host, port))
155 return s
158 class TestDnsForwarding(DNSTest):
159 def __init__(self, *args, **kwargs):
160 super(TestDnsForwarding, self).__init__(*args, **kwargs)
161 self.subprocesses = []
163 def setUp(self):
164 super(TestDnsForwarding, self).setUp()
165 self.server = server_name
166 self.server_ip = server_ip
167 self.lp = lp
168 self.creds = creds
170 def start_toy_server(self, host, port, id):
171 python = sys.executable
172 p = subprocess.Popen([python,
173 os.path.join(samba.source_tree_topdir(),
174 'python/samba/tests/'
175 'dns_forwarder_helpers/server.py'),
176 host, str(port), id])
177 self.subprocesses.append(p)
178 if (host.find(':') != -1):
179 s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, 0)
180 else:
181 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
182 for i in range(300):
183 time.sleep(0.05)
184 s.connect((host, port))
185 try:
186 s.send(b'timeout 0', 0)
187 except socket.error as e:
188 if e.errno in (errno.ECONNREFUSED, errno.EHOSTUNREACH):
189 continue
191 if p.returncode is not None:
192 self.fail("Toy server has managed to die already!")
194 return s
196 def tearDown(self):
197 super(TestDnsForwarding, self).tearDown()
198 for p in self.subprocesses:
199 p.kill()
201 def test_comatose_forwarder(self):
202 s = self.start_toy_server(dns_servers[0], 53, 'forwarder1')
203 s.send(b"timeout 1000000", 0)
205 # make DNS query
206 name = "an-address-that-will-not-resolve"
207 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
208 questions = []
210 q = self.make_name_question(name, dns.DNS_QTYPE_TXT, dns.DNS_QCLASS_IN)
211 questions.append(q)
213 self.finish_name_packet(p, questions)
214 send_packet = ndr.ndr_pack(p)
216 s.send(send_packet, 0)
217 s.settimeout(1)
218 try:
219 s.recv(0xffff + 2, 0)
220 self.fail("DNS forwarder should have been inactive")
221 except socket.timeout:
222 # Expected forwarder to be dead
223 pass
225 def test_no_active_forwarder(self):
226 ad = contact_real_server(server_ip, 53)
228 name = "dsfsfds.dsfsdfs"
229 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
230 questions = []
232 q = self.make_name_question(name, dns.DNS_QTYPE_TXT, dns.DNS_QCLASS_IN)
233 questions.append(q)
235 self.finish_name_packet(p, questions)
236 send_packet = ndr.ndr_pack(p)
238 self.finish_name_packet(p, questions)
239 p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
240 send_packet = ndr.ndr_pack(p)
242 ad.send(send_packet, 0)
243 ad.settimeout(timeout)
244 try:
245 data = ad.recv(0xffff + 2, 0)
246 data = ndr.ndr_unpack(dns.name_packet, data)
247 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_SERVFAIL)
248 self.assertEqual(data.ancount, 0)
249 except socket.timeout:
250 self.fail("DNS server is too slow (timeout %s)" % timeout)
252 def test_no_flag_recursive_forwarder(self):
253 ad = contact_real_server(server_ip, 53)
255 name = "dsfsfds.dsfsdfs"
256 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
257 questions = []
259 q = self.make_name_question(name, dns.DNS_QTYPE_TXT, dns.DNS_QCLASS_IN)
260 questions.append(q)
262 self.finish_name_packet(p, questions)
263 send_packet = ndr.ndr_pack(p)
265 self.finish_name_packet(p, questions)
266 # Leave off the recursive flag
267 send_packet = ndr.ndr_pack(p)
269 ad.send(send_packet, 0)
270 ad.settimeout(timeout)
271 try:
272 data = ad.recv(0xffff + 2, 0)
273 data = ndr.ndr_unpack(dns.name_packet, data)
274 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_NXDOMAIN)
275 self.assertEqual(data.ancount, 0)
276 except socket.timeout:
277 self.fail("DNS server is too slow (timeout %s)" % timeout)
279 def test_single_forwarder(self):
280 s = self.start_toy_server(dns_servers[0], 53, 'forwarder1')
281 ad = contact_real_server(server_ip, 53)
282 name = "dsfsfds.dsfsdfs"
283 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
284 questions = []
286 q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
287 dns.DNS_QCLASS_IN)
288 questions.append(q)
290 self.finish_name_packet(p, questions)
291 p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
292 send_packet = ndr.ndr_pack(p)
294 ad.send(send_packet, 0)
295 ad.settimeout(timeout)
296 try:
297 data = ad.recv(0xffff + 2, 0)
298 data = ndr.ndr_unpack(dns.name_packet, data)
299 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
300 self.assertEqual('forwarder1', data.answers[0].rdata)
301 except socket.timeout:
302 self.fail("DNS server is too slow (timeout %s)" % timeout)
304 def test_single_forwarder_not_actually_there(self):
305 ad = contact_real_server(server_ip, 53)
306 name = "dsfsfds.dsfsdfs"
307 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
308 questions = []
310 q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
311 dns.DNS_QCLASS_IN)
312 questions.append(q)
314 self.finish_name_packet(p, questions)
315 p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
316 send_packet = ndr.ndr_pack(p)
318 ad.send(send_packet, 0)
319 ad.settimeout(timeout)
320 try:
321 data = ad.recv(0xffff + 2, 0)
322 data = ndr.ndr_unpack(dns.name_packet, data)
323 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_SERVFAIL)
324 except socket.timeout:
325 self.fail("DNS server is too slow (timeout %s)" % timeout)
327 def test_single_forwarder_waiting_forever(self):
328 s = self.start_toy_server(dns_servers[0], 53, 'forwarder1')
329 s.send(b'timeout 10000', 0)
330 ad = contact_real_server(server_ip, 53)
331 name = "dsfsfds.dsfsdfs"
332 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
333 questions = []
335 q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
336 dns.DNS_QCLASS_IN)
337 questions.append(q)
339 self.finish_name_packet(p, questions)
340 p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
341 send_packet = ndr.ndr_pack(p)
343 ad.send(send_packet, 0)
344 ad.settimeout(timeout)
345 try:
346 data = ad.recv(0xffff + 2, 0)
347 data = ndr.ndr_unpack(dns.name_packet, data)
348 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_SERVFAIL)
349 except socket.timeout:
350 self.fail("DNS server is too slow (timeout %s)" % timeout)
352 def test_double_forwarder_first_frozen(self):
353 if len(dns_servers) < 2:
354 print("Ignoring test_double_forwarder_first_frozen")
355 return
356 s1 = self.start_toy_server(dns_servers[0], 53, 'forwarder1')
357 s2 = self.start_toy_server(dns_servers[1], DNS_PORT2, 'forwarder2')
358 s1.send(b'timeout 1000', 0)
359 ad = contact_real_server(server_ip, 53)
360 name = "dsfsfds.dsfsdfs"
361 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
362 questions = []
364 q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
365 dns.DNS_QCLASS_IN)
366 questions.append(q)
368 self.finish_name_packet(p, questions)
369 p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
370 send_packet = ndr.ndr_pack(p)
372 ad.send(send_packet, 0)
373 ad.settimeout(timeout)
374 try:
375 data = ad.recv(0xffff + 2, 0)
376 data = ndr.ndr_unpack(dns.name_packet, data)
377 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
378 self.assertEqual('forwarder2', data.answers[0].rdata)
379 except socket.timeout:
380 self.fail("DNS server is too slow (timeout %s)" % timeout)
382 def test_double_forwarder_first_down(self):
383 if len(dns_servers) < 2:
384 print("Ignoring test_double_forwarder_first_down")
385 return
386 s2 = self.start_toy_server(dns_servers[1], DNS_PORT2, 'forwarder2')
387 ad = contact_real_server(server_ip, 53)
388 name = "dsfsfds.dsfsdfs"
389 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
390 questions = []
392 q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
393 dns.DNS_QCLASS_IN)
394 questions.append(q)
396 self.finish_name_packet(p, questions)
397 p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
398 send_packet = ndr.ndr_pack(p)
400 ad.send(send_packet, 0)
401 ad.settimeout(timeout)
402 try:
403 data = ad.recv(0xffff + 2, 0)
404 data = ndr.ndr_unpack(dns.name_packet, data)
405 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
406 self.assertEqual('forwarder2', data.answers[0].rdata)
407 except socket.timeout:
408 self.fail("DNS server is too slow (timeout %s)" % timeout)
410 def test_double_forwarder_both_slow(self):
411 if len(dns_servers) < 2:
412 print("Ignoring test_double_forwarder_both_slow")
413 return
414 s1 = self.start_toy_server(dns_servers[0], 53, 'forwarder1')
415 s2 = self.start_toy_server(dns_servers[1], DNS_PORT2, 'forwarder2')
416 s1.send(b'timeout 1.5', 0)
417 s2.send(b'timeout 1.5', 0)
418 ad = contact_real_server(server_ip, 53)
419 name = "dsfsfds.dsfsdfs"
420 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
421 questions = []
423 q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
424 dns.DNS_QCLASS_IN)
425 questions.append(q)
427 self.finish_name_packet(p, questions)
428 p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
429 send_packet = ndr.ndr_pack(p)
431 ad.send(send_packet, 0)
432 ad.settimeout(timeout)
433 try:
434 data = ad.recv(0xffff + 2, 0)
435 data = ndr.ndr_unpack(dns.name_packet, data)
436 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
437 self.assertEqual('forwarder1', data.answers[0].rdata)
438 except socket.timeout:
439 self.fail("DNS server is too slow (timeout %s)" % timeout)
441 def test_cname(self):
442 s1 = self.start_toy_server(dns_servers[0], 53, 'forwarder1')
444 ad = contact_real_server(server_ip, 53)
445 name = "resolve.cname"
446 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
447 questions = []
449 q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
450 dns.DNS_QCLASS_IN)
451 questions.append(q)
453 self.finish_name_packet(p, questions)
454 p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
455 send_packet = ndr.ndr_pack(p)
457 ad.send(send_packet, 0)
458 ad.settimeout(timeout)
459 try:
460 data = ad.recv(0xffff + 2, 0)
461 data = ndr.ndr_unpack(dns.name_packet, data)
462 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
463 self.assertEqual(len(data.answers), 1)
464 self.assertEqual('forwarder1', data.answers[0].rdata)
465 except socket.timeout:
466 self.fail("DNS server is too slow (timeout %s)" % timeout)
468 def test_double_cname(self):
469 s1 = self.start_toy_server(dns_servers[0], 53, 'forwarder1')
471 name = 'resolve.cname.%s' % self.get_dns_domain()
472 self.make_cname_update(name, "dsfsfds.dsfsdfs")
474 ad = contact_real_server(server_ip, 53)
476 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
477 questions = []
478 q = self.make_name_question(name, dns.DNS_QTYPE_A,
479 dns.DNS_QCLASS_IN)
480 questions.append(q)
482 self.finish_name_packet(p, questions)
483 p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
484 send_packet = ndr.ndr_pack(p)
486 ad.send(send_packet, 0)
487 ad.settimeout(timeout)
488 try:
489 data = ad.recv(0xffff + 2, 0)
490 data = ndr.ndr_unpack(dns.name_packet, data)
491 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
492 self.assertEqual('forwarder1', data.answers[1].rdata)
493 except socket.timeout:
494 self.fail("DNS server is too slow (timeout %s)" % timeout)
496 def test_cname_forwarding_with_slow_server(self):
497 if len(dns_servers) < 2:
498 print("Ignoring test_cname_forwarding_with_slow_server")
499 return
500 s1 = self.start_toy_server(dns_servers[0], 53, 'forwarder1')
501 s2 = self.start_toy_server(dns_servers[1], DNS_PORT2, 'forwarder2')
502 s1.send(b'timeout 10000', 0)
504 name = 'resolve.cname.%s' % self.get_dns_domain()
505 self.make_cname_update(name, "dsfsfds.dsfsdfs")
507 ad = contact_real_server(server_ip, 53)
509 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
510 questions = []
511 q = self.make_name_question(name, dns.DNS_QTYPE_A,
512 dns.DNS_QCLASS_IN)
513 questions.append(q)
515 self.finish_name_packet(p, questions)
516 p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
517 send_packet = ndr.ndr_pack(p)
519 ad.send(send_packet, 0)
520 ad.settimeout(timeout)
521 try:
522 data = ad.recv(0xffff + 2, 0)
523 data = ndr.ndr_unpack(dns.name_packet, data)
524 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
525 self.assertEqual('forwarder2', data.answers[-1].rdata)
526 except socket.timeout:
527 self.fail("DNS server is too slow (timeout %s)" % timeout)
529 def test_cname_forwarding_with_server_down(self):
530 if len(dns_servers) < 2:
531 print("Ignoring test_cname_forwarding_with_server_down")
532 return
533 s2 = self.start_toy_server(dns_servers[1], DNS_PORT2, 'forwarder2')
535 name1 = 'resolve1.cname.%s' % self.get_dns_domain()
536 name2 = 'resolve2.cname.%s' % self.get_dns_domain()
537 self.make_cname_update(name1, name2)
538 self.make_cname_update(name2, "dsfsfds.dsfsdfs")
540 ad = contact_real_server(server_ip, 53)
542 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
543 questions = []
544 q = self.make_name_question(name1, dns.DNS_QTYPE_A,
545 dns.DNS_QCLASS_IN)
546 questions.append(q)
548 self.finish_name_packet(p, questions)
549 p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
550 send_packet = ndr.ndr_pack(p)
552 ad.send(send_packet, 0)
553 ad.settimeout(timeout)
554 try:
555 data = ad.recv(0xffff + 2, 0)
556 data = ndr.ndr_unpack(dns.name_packet, data)
557 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
558 self.assertEqual('forwarder2', data.answers[-1].rdata)
559 except socket.timeout:
560 self.fail("DNS server is too slow (timeout %s)" % timeout)
562 def test_cname_forwarding_with_lots_of_cnames(self):
563 name3 = 'resolve3.cname.%s' % self.get_dns_domain()
564 s1 = self.start_toy_server(dns_servers[0], 53, name3)
566 name1 = 'resolve1.cname.%s' % self.get_dns_domain()
567 name2 = 'resolve2.cname.%s' % self.get_dns_domain()
568 self.make_cname_update(name1, name2)
569 self.make_cname_update(name3, name1)
570 self.make_cname_update(name2, "dsfsfds.dsfsdfs")
572 ad = contact_real_server(server_ip, 53)
574 p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
575 questions = []
576 q = self.make_name_question(name1, dns.DNS_QTYPE_A,
577 dns.DNS_QCLASS_IN)
578 questions.append(q)
580 self.finish_name_packet(p, questions)
581 p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
582 send_packet = ndr.ndr_pack(p)
584 ad.send(send_packet, 0)
585 ad.settimeout(timeout)
586 try:
587 data = ad.recv(0xffff + 2, 0)
588 data = ndr.ndr_unpack(dns.name_packet, data)
589 # This should cause a loop in Windows
590 # (which is restricted by a 20 CNAME limit)
592 # The reason it doesn't here is because forwarded CNAME have no
593 # additional processing in the internal DNS server.
594 self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
595 self.assertEqual(name3, data.answers[-1].rdata)
596 except socket.timeout:
597 self.fail("DNS server is too slow (timeout %s)" % timeout)
600 TestProgram(module=__name__, opts=subunitopts)