tests/dns_forwarder: Add testing for DNS forwarding
authorGarming Sam <garming@catalyst.net.nz>
Thu, 17 Mar 2016 04:13:28 +0000 (17:13 +1300)
committerGarming Sam <garming@samba.org>
Tue, 3 May 2016 06:10:09 +0000 (08:10 +0200)
The new tests show that single and multiple forwarders work as expected.
They also describe the behaviour encountered when the DNS server
encounters a CNAME from a forwarded request (which is not to pursue any
further).

Signed-off-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Pair-programmed-with: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>

python/samba/tests/dns_forwarder.py [new file with mode: 0644]
python/samba/tests/dns_forwarder_helpers/server.py [new file with mode: 0644]
selftest/selftest.pl
selftest/target/Samba.pm
selftest/target/Samba4.pm
source4/selftest/tests.py

diff --git a/python/samba/tests/dns_forwarder.py b/python/samba/tests/dns_forwarder.py
new file mode 100644 (file)
index 0000000..1212086
--- /dev/null
@@ -0,0 +1,531 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Kai Blin  <kai@samba.org> 2011
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import sys
+import struct
+import random
+import socket
+import samba
+import time
+import errno
+import samba.ndr as ndr
+from samba import credentials, param
+from samba.tests import TestCase
+from samba.dcerpc import dns, dnsp, dnsserver
+from samba.netcmd.dns import TXTRecord, dns_record_match, data_to_dns_record
+from samba.tests.subunitrun import SubunitOptions, TestProgram
+import samba.getopt as options
+import optparse
+import subprocess
+
+parser = optparse.OptionParser("dns_forwarder.py <server name> <server ip> [options]")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+
+# This timeout only has relevance when testing against Windows
+# Format errors tend to return patchy responses, so a timeout is needed.
+parser.add_option("--timeout", type="int", dest="timeout",
+                  help="Specify timeout for DNS requests")
+
+# use command line creds if available
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+subunitopts = SubunitOptions(parser)
+parser.add_option_group(subunitopts)
+
+opts, args = parser.parse_args()
+
+lp = sambaopts.get_loadparm()
+creds = credopts.get_credentials(lp)
+
+timeout = opts.timeout
+
+if len(args) < 2:
+    parser.print_usage()
+    sys.exit(1)
+
+server_name = args[0]
+server_ip = args[1]
+creds.set_krb_forwardable(credentials.NO_KRB_FORWARDABLE)
+
+def make_txt_record(records):
+    rdata_txt = dns.txt_record()
+    s_list = dnsp.string_list()
+    s_list.count = len(records)
+    s_list.str = records
+    rdata_txt.txt = s_list
+    return rdata_txt
+
+
+class DNSTest(TestCase):
+
+    errcodes = {v: k for k, v in vars(dns).items() if k.startswith('DNS_RCODE_')}
+
+    def assert_dns_rcode_equals(self, packet, rcode):
+        "Helper function to check return code"
+        p_errcode = packet.operation & 0x000F
+        self.assertEquals(p_errcode, rcode, "Expected RCODE %s, got %s" %
+                          (self.errcodes[rcode], self.errcodes[p_errcode]))
+
+    def assert_dns_opcode_equals(self, packet, opcode):
+        "Helper function to check opcode"
+        p_opcode = packet.operation & 0x7800
+        self.assertEquals(p_opcode, opcode, "Expected OPCODE %s, got %s" %
+                            (opcode, p_opcode))
+
+    def make_name_packet(self, opcode, qid=None):
+        "Helper creating a dns.name_packet"
+        p = dns.name_packet()
+        if qid is None:
+            p.id = random.randint(0x0, 0xffff)
+        p.operation = opcode
+        p.questions = []
+        return p
+
+    def finish_name_packet(self, packet, questions):
+        "Helper to finalize a dns.name_packet"
+        packet.qdcount = len(questions)
+        packet.questions = questions
+
+    def make_name_question(self, name, qtype, qclass):
+        "Helper creating a dns.name_question"
+        q = dns.name_question()
+        q.name = name
+        q.question_type = qtype
+        q.question_class = qclass
+        return q
+
+    def get_dns_domain(self):
+        "Helper to get dns domain"
+        return self.creds.get_realm().lower()
+
+    def dns_transaction_udp(self, packet, host=server_ip,
+                            dump=False, timeout=timeout):
+        "send a DNS query and read the reply"
+        s = None
+        try:
+            send_packet = ndr.ndr_pack(packet)
+            if dump:
+                print self.hexdump(send_packet)
+            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+            s.settimeout(timeout)
+            s.connect((host, 53))
+            s.send(send_packet, 0)
+            recv_packet = s.recv(2048, 0)
+            if dump:
+                print self.hexdump(recv_packet)
+            return ndr.ndr_unpack(dns.name_packet, recv_packet)
+        finally:
+            if s is not None:
+                s.close()
+
+    def make_cname_update(self, key, value):
+        p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
+
+        name = self.get_dns_domain()
+        u = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
+        self.finish_name_packet(p, [u])
+
+        r = dns.res_rec()
+        r.name = key
+        r.rr_type = dns.DNS_QTYPE_CNAME
+        r.rr_class = dns.DNS_QCLASS_IN
+        r.ttl = 900
+        r.length = 0xffff
+        rdata = value
+        r.rdata = rdata
+        p.nscount = 1
+        p.nsrecs = [r]
+        response = self.dns_transaction_udp(p)
+        self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+
+
+
+def contact_real_server(host, port):
+    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+    s.connect((host, port))
+    return s
+
+
+class TestDnsForwarding(DNSTest):
+    def __init__(self, *args, **kwargs):
+        super(TestDnsForwarding, self).__init__(*args, **kwargs)
+        self.subprocesses = []
+
+    def setUp(self):
+        super(TestDnsForwarding, self).setUp()
+        self.server = server_name
+        self.server_ip = server_ip
+        self.lp = lp
+        self.creds = creds
+
+    def start_toy_server(self, host, port, id):
+        python = sys.executable
+        p = subprocess.Popen([python,
+                              os.path.join(samba.source_tree_topdir(),
+                                           'python/samba/tests/'
+                                           'dns_forwarder_helpers/server.py'),
+                             host, str(port), id])
+        self.subprocesses.append(p)
+        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+        for i in xrange(30):
+            time.sleep(0.01)
+            s.connect((host, port))
+            try:
+                s.send('timeout 0', 0)
+            except socket.error, e:
+                if e.errno in (errno.ECONNREFUSED, errno.EHOSTUNREACH):
+                    continue
+            return s
+
+    def tearDown(self):
+        super(TestDnsForwarding, self).tearDown()
+        for p in self.subprocesses:
+            p.kill()
+
+    def test_comatose_forwarder(self):
+        s = self.start_toy_server('127.0.0.36', 53, 'forwarder1')
+        s.send("timeout 1000000", 0)
+
+        # make DNS query
+        name = "an-address-that-will-not-resolve"
+        p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+        questions = []
+
+        q = self.make_name_question(name, dns.DNS_QTYPE_TXT, dns.DNS_QCLASS_IN)
+        questions.append(q)
+
+        self.finish_name_packet(p, questions)
+        send_packet = ndr.ndr_pack(p)
+
+        s.send(send_packet, 0)
+        s.settimeout(1)
+        try:
+            s.recv(0xffff + 2, 0)
+            self.fail("DNS forwarder should have been inactive")
+        except socket.timeout:
+            # Expected forwarder to be dead
+            pass
+
+    def test_single_forwarder(self):
+        s = self.start_toy_server('127.0.0.36', 53, 'forwarder1')
+        ad = contact_real_server(server_ip, 53)
+        name = "dsfsfds.dsfsdfs"
+        p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+        questions = []
+
+        q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
+                                    dns.DNS_QCLASS_IN)
+        questions.append(q)
+
+        self.finish_name_packet(p, questions)
+        p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
+        send_packet = ndr.ndr_pack(p)
+
+        ad.send(send_packet, 0)
+        ad.settimeout(timeout)
+        try:
+            data = ad.recv(0xffff + 2, 0)
+            data = ndr.ndr_unpack(dns.name_packet, data)
+            self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
+            self.assertEqual('forwarder1', data.answers[0].rdata)
+        except socket.timeout:
+            self.fail("DNS server is too slow (timeout %s)" % timeout)
+
+    def test_single_forwarder_not_actually_there(self):
+        ad = contact_real_server(server_ip, 53)
+        name = "dsfsfds.dsfsdfs"
+        p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+        questions = []
+
+        q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
+                                    dns.DNS_QCLASS_IN)
+        questions.append(q)
+
+        self.finish_name_packet(p, questions)
+        p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
+        send_packet = ndr.ndr_pack(p)
+
+        ad.send(send_packet, 0)
+        ad.settimeout(timeout)
+        try:
+            data = ad.recv(0xffff + 2, 0)
+            data = ndr.ndr_unpack(dns.name_packet, data)
+            self.assert_dns_rcode_equals(data, dns.DNS_RCODE_SERVFAIL)
+        except socket.timeout:
+            self.fail("DNS server is too slow (timeout %s)" % timeout)
+
+
+    def test_single_forwarder_waiting_forever(self):
+        s = self.start_toy_server('127.0.0.36', 53, 'forwarder1')
+        s.send('timeout 10000', 0)
+        ad = contact_real_server(server_ip, 53)
+        name = "dsfsfds.dsfsdfs"
+        p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+        questions = []
+
+        q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
+                                    dns.DNS_QCLASS_IN)
+        questions.append(q)
+
+        self.finish_name_packet(p, questions)
+        p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
+        send_packet = ndr.ndr_pack(p)
+
+        ad.send(send_packet, 0)
+        ad.settimeout(timeout)
+        try:
+            data = ad.recv(0xffff + 2, 0)
+            data = ndr.ndr_unpack(dns.name_packet, data)
+            print data.__ndr_print__()
+            self.assert_dns_rcode_equals(data, dns.DNS_RCODE_SERVFAIL)
+        except socket.timeout:
+            self.fail("DNS server is too slow (timeout %s)" % timeout)
+
+    def test_double_forwarder_first_frozen(self):
+        s1 = self.start_toy_server('127.0.0.36', 53, 'forwarder1')
+        s2 = self.start_toy_server('127.0.0.37', 53, 'forwarder2')
+        s1.send('timeout 1000', 0)
+        ad = contact_real_server(server_ip, 53)
+        name = "dsfsfds.dsfsdfs"
+        p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+        questions = []
+
+        q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
+                                    dns.DNS_QCLASS_IN)
+        questions.append(q)
+
+        self.finish_name_packet(p, questions)
+        p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
+        send_packet = ndr.ndr_pack(p)
+
+        ad.send(send_packet, 0)
+        ad.settimeout(timeout)
+        try:
+            data = ad.recv(0xffff + 2, 0)
+            data = ndr.ndr_unpack(dns.name_packet, data)
+            self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
+            self.assertEqual('forwarder2', data.answers[0].rdata)
+        except socket.timeout:
+            self.fail("DNS server is too slow (timeout %s)" % timeout)
+
+    def test_double_forwarder_first_down(self):
+        s2 = self.start_toy_server('127.0.0.37', 53, 'forwarder2')
+        ad = contact_real_server(server_ip, 53)
+        name = "dsfsfds.dsfsdfs"
+        p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+        questions = []
+
+        q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
+                                    dns.DNS_QCLASS_IN)
+        questions.append(q)
+
+        self.finish_name_packet(p, questions)
+        p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
+        send_packet = ndr.ndr_pack(p)
+
+        ad.send(send_packet, 0)
+        ad.settimeout(timeout)
+        try:
+            data = ad.recv(0xffff + 2, 0)
+            data = ndr.ndr_unpack(dns.name_packet, data)
+            self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
+            self.assertEqual('forwarder2', data.answers[0].rdata)
+        except socket.timeout:
+            self.fail("DNS server is too slow (timeout %s)" % timeout)
+
+    def test_double_forwarder_both_slow(self):
+        s1 = self.start_toy_server('127.0.0.36', 53, 'forwarder1')
+        s2 = self.start_toy_server('127.0.0.37', 53, 'forwarder2')
+        s1.send('timeout 1.5', 0)
+        s2.send('timeout 1.5', 0)
+        ad = contact_real_server(server_ip, 53)
+        name = "dsfsfds.dsfsdfs"
+        p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+        questions = []
+
+        q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
+                                    dns.DNS_QCLASS_IN)
+        questions.append(q)
+
+        self.finish_name_packet(p, questions)
+        p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
+        send_packet = ndr.ndr_pack(p)
+
+        ad.send(send_packet, 0)
+        ad.settimeout(timeout)
+        try:
+            data = ad.recv(0xffff + 2, 0)
+            data = ndr.ndr_unpack(dns.name_packet, data)
+            self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
+            self.assertEqual('forwarder1', data.answers[0].rdata)
+        except socket.timeout:
+            self.fail("DNS server is too slow (timeout %s)" % timeout)
+
+    def test_cname(self):
+        s1 = self.start_toy_server('127.0.0.36', 53, 'forwarder1')
+
+        ad = contact_real_server(server_ip, 53)
+        name = "resolve.cname"
+        p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+        questions = []
+
+        q = self.make_name_question(name, dns.DNS_QTYPE_CNAME,
+                                    dns.DNS_QCLASS_IN)
+        questions.append(q)
+
+        self.finish_name_packet(p, questions)
+        p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
+        send_packet = ndr.ndr_pack(p)
+
+        ad.send(send_packet, 0)
+        ad.settimeout(timeout)
+        try:
+            data = ad.recv(0xffff + 2, 0)
+            data = ndr.ndr_unpack(dns.name_packet, data)
+            self.assertEqual('forwarder1', data.answers[0].rdata)
+            self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
+        except socket.timeout:
+            self.fail("DNS server is too slow (timeout %s)" % timeout)
+
+    def test_double_cname(self):
+        s1 = self.start_toy_server('127.0.0.36', 53, 'forwarder1')
+
+        name = 'resolve.cname.%s' % self.get_dns_domain()
+        self.make_cname_update(name, "dsfsfds.dsfsdfs")
+
+        ad = contact_real_server(server_ip, 53)
+
+        p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+        questions = []
+        q = self.make_name_question(name, dns.DNS_QTYPE_A,
+                                    dns.DNS_QCLASS_IN)
+        questions.append(q)
+
+        self.finish_name_packet(p, questions)
+        p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
+        send_packet = ndr.ndr_pack(p)
+
+        ad.send(send_packet, 0)
+        ad.settimeout(timeout)
+        try:
+            data = ad.recv(0xffff + 2, 0)
+            data = ndr.ndr_unpack(dns.name_packet, data)
+            self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
+            self.assertEqual('forwarder1', data.answers[1].rdata)
+        except socket.timeout:
+            self.fail("DNS server is too slow (timeout %s)" % timeout)
+
+    def test_cname_forwarding_with_slow_server(self):
+        s1 = self.start_toy_server('127.0.0.36', 53, 'forwarder1')
+        s2 = self.start_toy_server('127.0.0.37', 53, 'forwarder2')
+        s1.send('timeout 10000', 0)
+
+        name = 'resolve.cname.%s' % self.get_dns_domain()
+        self.make_cname_update(name, "dsfsfds.dsfsdfs")
+
+        ad = contact_real_server(server_ip, 53)
+
+        p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+        questions = []
+        q = self.make_name_question(name, dns.DNS_QTYPE_A,
+                                    dns.DNS_QCLASS_IN)
+        questions.append(q)
+
+        self.finish_name_packet(p, questions)
+        p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
+        send_packet = ndr.ndr_pack(p)
+
+        ad.send(send_packet, 0)
+        ad.settimeout(timeout)
+        try:
+            data = ad.recv(0xffff + 2, 0)
+            data = ndr.ndr_unpack(dns.name_packet, data)
+            self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
+            self.assertEqual('forwarder2', data.answers[-1].rdata)
+        except socket.timeout:
+            self.fail("DNS server is too slow (timeout %s)" % timeout)
+
+    def test_cname_forwarding_with_server_down(self):
+        s2 = self.start_toy_server('127.0.0.37', 53, 'forwarder2')
+
+        name1 = 'resolve1.cname.%s' % self.get_dns_domain()
+        name2 = 'resolve2.cname.%s' % self.get_dns_domain()
+        self.make_cname_update(name1, name2)
+        self.make_cname_update(name2, "dsfsfds.dsfsdfs")
+
+        ad = contact_real_server(server_ip, 53)
+
+        p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+        questions = []
+        q = self.make_name_question(name1, dns.DNS_QTYPE_A,
+                                    dns.DNS_QCLASS_IN)
+        questions.append(q)
+
+        self.finish_name_packet(p, questions)
+        p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
+        send_packet = ndr.ndr_pack(p)
+
+        ad.send(send_packet, 0)
+        ad.settimeout(timeout)
+        try:
+            data = ad.recv(0xffff + 2, 0)
+            data = ndr.ndr_unpack(dns.name_packet, data)
+            self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
+            self.assertEqual('forwarder2', data.answers[-1].rdata)
+        except socket.timeout:
+            self.fail("DNS server is too slow (timeout %s)" % timeout)
+
+    def test_cname_forwarding_with_lots_of_cnames(self):
+        name3 = 'resolve3.cname.%s' % self.get_dns_domain()
+        s1 = self.start_toy_server('127.0.0.36', 53, name3)
+
+        name1 = 'resolve1.cname.%s' % self.get_dns_domain()
+        name2 = 'resolve2.cname.%s' % self.get_dns_domain()
+        self.make_cname_update(name1, name2)
+        self.make_cname_update(name3, name1)
+        self.make_cname_update(name2, "dsfsfds.dsfsdfs")
+
+        ad = contact_real_server(server_ip, 53)
+
+        p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+        questions = []
+        q = self.make_name_question(name1, dns.DNS_QTYPE_A,
+                                    dns.DNS_QCLASS_IN)
+        questions.append(q)
+
+        self.finish_name_packet(p, questions)
+        p.operation |= dns.DNS_FLAG_RECURSION_DESIRED
+        send_packet = ndr.ndr_pack(p)
+
+        ad.send(send_packet, 0)
+        ad.settimeout(timeout)
+        try:
+            data = ad.recv(0xffff + 2, 0)
+            data = ndr.ndr_unpack(dns.name_packet, data)
+            # This should cause a loop in Windows
+            # (which is restricted by a 20 CNAME limit)
+            #
+            # The reason it doesn't here is because forwarded CNAME have no
+            # additional processing in the internal DNS server.
+            self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK)
+            self.assertEqual(name3, data.answers[-1].rdata)
+        except socket.timeout:
+            self.fail("DNS server is too slow (timeout %s)" % timeout)
+
+TestProgram(module=__name__, opts=subunitopts)
diff --git a/python/samba/tests/dns_forwarder_helpers/server.py b/python/samba/tests/dns_forwarder_helpers/server.py
new file mode 100644 (file)
index 0000000..e302485
--- /dev/null
@@ -0,0 +1,93 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Catalyst.Net Ltd 2016
+# Catalyst.Net's contribution was written by Douglas Bagnall
+# <douglas.bagnall@catalyst.net.nz> and Garming Sam <garming@catalyst.net.nz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Based on the EchoServer example from python docs
+import SocketServer
+import time
+import sys
+from threading import Timer
+from samba.dcerpc import dns
+import samba.ndr as ndr
+import random
+import re
+
+VERBOSE = False
+
+def debug(msg):
+    if VERBOSE:
+        sys.stdout.flush()
+        print "\033[00;36m%s\033[00m" % msg
+        sys.stdout.flush()
+
+timeout = 0
+
+
+def answer_question(data, question):
+    r = dns.res_rec()
+    r.name = question.name
+    r.rr_type = dns.DNS_QTYPE_CNAME
+    r.rr_class = dns.DNS_QCLASS_IN
+    r.ttl = 900
+    r.length = 0xffff
+    r.rdata = SERVER_ID
+    return r
+
+
+class DnsHandler(SocketServer.BaseRequestHandler):
+    def make_answer(self, data):
+        data = ndr.ndr_unpack(dns.name_packet, data)
+
+        debug('answering this question:')
+        debug(data.__ndr_print__())
+
+        answer = answer_question(data, data.questions[0])
+        if answer is not None:
+            data.answers = [answer] * 1
+            data.ancount += 1
+            debug('the answer was: ')
+            debug(data.__ndr_print__())
+
+        data.operation |= dns.DNS_FLAG_REPLY
+
+        return ndr.ndr_pack(data)
+
+    def really_handle(self, data, socket):
+        answer = self.make_answer(data)
+        socket.sendto(answer, self.client_address)
+
+    def handle(self):
+        data, socket = self.request
+        debug("%s: %s wrote:" % (SERVER_ID, self.client_address[0]))
+
+        global timeout
+        m = re.match('^timeout\s+([\d.]+)$', data.strip())
+        if m:
+            timeout = float(m.group(1))
+            debug("timing out at %s" % timeout)
+            return
+
+        t = Timer(timeout, self.really_handle, [data, socket])
+        t.start()
+
+def main():
+    global SERVER_ID
+    host, port, SERVER_ID = sys.argv[1:]
+    server = SocketServer.UDPServer((host, int(port)), DnsHandler)
+    server.serve_forever()
+
+main()
index ff5f27d08555c5177721ab504e63daa615b1efd6..7b4d4decb0863fc827dfe37bbe56e5622f7459c6 100755 (executable)
@@ -833,6 +833,8 @@ my @exported_envvars = (
        "WINBINDD_PRIV_PIPE_DIR",
        "NMBD_SOCKET_DIR",
        "LOCAL_PATH",
+       "DNS_FORWARDER1",
+       "DNS_FORWARDER2",
 
        # nss_wrapper
        "NSS_WRAPPER_PASSWD",
index 17a2bbe5f9b579e22b9d7ca6dd5799d73278a7b2..6f37aa3e46ac5b3bf992695bd91e2848e3f8c439 100644 (file)
@@ -296,6 +296,8 @@ sub get_interface($)
     $interfaces{"promotedvdc"} = 33;
     $interfaces{"rfc2307member"} = 34;
     $interfaces{"fileserver"} = 35;
+    $interfaces{"fakednsforwarder1"} = 36;
+    $interfaces{"fakednsforwarder2"} = 37;
 
     # update lib/socket_wrapper/socket_wrapper.c
     #  #define MAX_WRAPPED_INTERFACES 40
index b0ce27e6738312f4a912e3aaecd415aaf05a6033..73843a77fac091613d42eb7b4647b47499c337d9 100755 (executable)
@@ -1412,9 +1412,12 @@ sub provision_fl2000dc($$)
 sub provision_fl2003dc($$$)
 {
        my ($self, $prefix, $dcvars) = @_;
+       my $swiface1 = Samba::get_interface("fakednsforwarder1");
+       my $swiface2 = Samba::get_interface("fakednsforwarder2");
 
        print "PROVISIONING DC WITH FOREST LEVEL 2003...";
-        my $extra_conf_options = "allow dns updates = nonsecure and secure";
+       my $extra_conf_options = "allow dns updates = nonsecure and secure
+       dns forwarder = 127.0.0.$swiface1 127.0.0.$swiface2";
        my $ret = $self->provision($prefix,
                                   "domain controller",
                                   "dc6",
@@ -1438,6 +1441,8 @@ sub provision_fl2003dc($$$)
        $ret->{DC_NETBIOSNAME} = $ret->{NETBIOSNAME};
        $ret->{DC_USERNAME} = $ret->{USERNAME};
        $ret->{DC_PASSWORD} = $ret->{PASSWORD};
+       $ret->{DNS_FORWARDER1} = "127.0.0.$swiface1";
+       $ret->{DNS_FORWARDER2} = "127.0.0.$swiface2";
 
        my @samba_tool_options;
        push (@samba_tool_options, Samba::bindir_path($self, "samba-tool"));
index 45cff595ac453484381d08858ad58643a01174bd..f2bc0f06cae803053f0785028f90ef570ed832b1 100755 (executable)
@@ -358,6 +358,8 @@ for f in sorted(os.listdir(os.path.join(samba4srcdir, "../pidl/tests"))):
 # DNS tests
 plantestsuite_loadlist("samba.tests.dns", "fl2003dc:local", [python, os.path.join(srcdir(), "python/samba/tests/dns.py"), '$SERVER', '$SERVER_IP', '--machine-pass', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT'])
 
+plantestsuite_loadlist("samba.tests.dns_forwarder", "fl2003dc:local", [python, os.path.join(srcdir(), "python/samba/tests/dns_forwarder.py"), '$SERVER', '$SERVER_IP', '--machine-pass', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT'])
+
 for t in smbtorture4_testsuites("dns_internal."):
     plansmbtorture4testsuite(t, "ad_dc_ntvfs:local", '//$SERVER/whavever')