From cf24e242f804175769f492d31a17d6f77c48ba6e Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Wed, 27 Jan 2016 11:16:29 +1300 Subject: [PATCH 1/9] dns: remove double talloc for strings Signed-off-by: Garming Sam --- librpc/ndr/ndr_dnsp.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/librpc/ndr/ndr_dnsp.c b/librpc/ndr/ndr_dnsp.c index 3cb96f9..ff77bc7 100644 --- a/librpc/ndr/ndr_dnsp.c +++ b/librpc/ndr/ndr_dnsp.c @@ -139,10 +139,6 @@ _PUBLIC_ enum ndr_err_code ndr_pull_dnsp_string(struct ndr_pull *ndr, int ndr_fl NDR_CHECK(ndr_pull_uint8(ndr, ndr_flags, &len)); - ret = talloc_strdup(ndr->current_mem_ctx, ""); - if (!ret) { - return ndr_pull_error(ndr, NDR_ERR_ALLOC, "Failed to pull dnsp_string"); - } ret = talloc_zero_array(ndr->current_mem_ctx, char, len + 1); if (!ret) { return ndr_pull_error(ndr, NDR_ERR_ALLOC, "Failed to pull dnsp_string"); -- 1.9.1 From 32b4d50ace42eec8dbcaa906f1ff00ce7d5018bb Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Mon, 11 Apr 2016 12:05:20 +1200 Subject: [PATCH 2/9] dnsserver: Remove C++ style comment Signed-off-by: Garming Sam --- source4/dns_server/dnsserver_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/dns_server/dnsserver_common.c b/source4/dns_server/dnsserver_common.c index 095f01a..9b3e630 100644 --- a/source4/dns_server/dnsserver_common.c +++ b/source4/dns_server/dnsserver_common.c @@ -473,7 +473,7 @@ NTSTATUS dns_common_zones(struct ldb_context *samdb, struct dns_server_zone *new_list = NULL; TALLOC_CTX *frame = talloc_stackframe(); - // TODO: this search does not work against windows + /* TODO: this search does not work against windows */ ret = dsdb_search(samdb, frame, &res, NULL, LDB_SCOPE_SUBTREE, attrs, DSDB_SEARCH_SEARCH_ALL_PARTITIONS, "(objectClass=dnsZone)"); if (ret != LDB_SUCCESS) { -- 1.9.1 From a685aa78befcd2398dfbf9ddf5e7cca3226f4224 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Wed, 30 Mar 2016 17:13:49 +1300 Subject: [PATCH 3/9] selftest: Remove an early return in the fl2003dc provision Signed-off-by: Garming Sam --- selftest/target/Samba4.pm | 2 -- 1 file changed, 2 deletions(-) diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index eddcfa6..6963d88 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -1441,8 +1441,6 @@ sub provision_fl2003dc($$$) return undef; } - return $ret; - unless($self->add_wins_config("$prefix/private")) { warn("Unable to add wins configuration"); return undef; -- 1.9.1 From efd47aa5413d96f890d874ef7bae2a56bfe57e9b Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Wed, 17 Feb 2016 11:30:21 +1300 Subject: [PATCH 4/9] dns: modify dns forwarder param to be multi-valued This allows a secondary DNS forwarder for a trivial failover. Requests which fail/timeout at the primary DNS forwarder will be restarted entirely with the next forwarder in the list. Signed-off-by: Garming Sam --- docs-xml/smbdotconf/domain/dnsforwarder.xml | 5 +- source4/dns_server/dns_query.c | 100 +++++++++++++++++++++++++--- source4/dns_server/dns_server.c | 6 +- 3 files changed, 94 insertions(+), 17 deletions(-) diff --git a/docs-xml/smbdotconf/domain/dnsforwarder.xml b/docs-xml/smbdotconf/domain/dnsforwarder.xml index 4147ef8..d3c8b76 100644 --- a/docs-xml/smbdotconf/domain/dnsforwarder.xml +++ b/docs-xml/smbdotconf/domain/dnsforwarder.xml @@ -1,10 +1,9 @@ - This option specifies the DNS server that DNS requests will be + This option specifies the list of DNS servers that DNS requests will be forwarded to if they can not be handled by Samba itself. diff --git a/source4/dns_server/dns_query.c b/source4/dns_server/dns_query.c index c251430..146054b 100644 --- a/source4/dns_server/dns_query.c +++ b/source4/dns_server/dns_query.c @@ -31,6 +31,7 @@ #include "dsdb/common/util.h" #include "dns_server/dns_server.h" #include "libcli/dns/libdns.h" +#include "lib/util/dlinklist.h" #include "lib/util/util_net.h" #include "lib/util/tevent_werror.h" #include "auth/auth.h" @@ -40,6 +41,11 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_DNS +struct forwarder_string { + const char *forwarder; + struct forwarder_string *prev, *next; +}; + static WERROR add_response_rr(const char *name, const struct dnsp_DnssrvRpcRecord *rec, struct dns_res_rec **answers) @@ -912,12 +918,17 @@ static WERROR handle_tkey(struct dns_server *dns, } struct dns_server_process_query_state { + struct tevent_context *ev; + struct dns_server *dns; + struct dns_name_question *question; + struct dns_res_rec *answers; uint16_t ancount; struct dns_res_rec *nsrecs; uint16_t nscount; struct dns_res_rec *additional; uint16_t arcount; + struct forwarder_string *forwarders; }; static void dns_server_process_query_got_auth(struct tevent_req *subreq); @@ -930,6 +941,8 @@ struct tevent_req *dns_server_process_query_send( { struct tevent_req *req, *subreq; struct dns_server_process_query_state *state; + const char **forwarders = NULL; + unsigned int i; req = tevent_req_create(mem_ctx, &state, struct dns_server_process_query_state); @@ -959,6 +972,18 @@ struct tevent_req *dns_server_process_query_send( return tevent_req_post(req, ev); } + state->dns = dns; + state->ev = ev; + state->question = &in->questions[0]; + + forwarders = lpcfg_dns_forwarder(dns->task->lp_ctx); + for (i = 0; forwarders != NULL && forwarders[i] != NULL; i++) { + struct forwarder_string *f = talloc_zero(state, + struct forwarder_string); + f->forwarder = forwarders[i]; + DLIST_ADD_END(state->forwarders, f); + } + if (dns_authorative_for_zone(dns, in->questions[0].name)) { req_state->flags |= DNS_FLAG_AUTHORITATIVE; @@ -978,7 +1003,7 @@ struct tevent_req *dns_server_process_query_send( } subreq = handle_authoritative_send( - state, ev, dns, lpcfg_dns_forwarder(dns->task->lp_ctx), + state, ev, dns, (forwarders == NULL ? NULL : forwarders[0]), &in->questions[0], &state->answers, &state->nsrecs); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); @@ -993,9 +1018,9 @@ struct tevent_req *dns_server_process_query_send( DEBUG(2, ("Not authoritative for '%s', forwarding\n", in->questions[0].name)); - subreq = ask_forwarder_send( - state, ev, dns, lpcfg_dns_forwarder(dns->task->lp_ctx), - &in->questions[0]); + subreq = ask_forwarder_send(state, ev, dns, + (forwarders == NULL ? NULL : forwarders[0]), + &in->questions[0]); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } @@ -1014,16 +1039,42 @@ static void dns_server_process_query_got_response(struct tevent_req *subreq) subreq, struct tevent_req); struct dns_server_process_query_state *state = tevent_req_data( req, struct dns_server_process_query_state); - WERROR err; + WERROR werr; - err = ask_forwarder_recv(subreq, state, - &state->answers, &state->ancount, - &state->nsrecs, &state->nscount, - &state->additional, &state->arcount); + werr = ask_forwarder_recv(subreq, state, + &state->answers, &state->ancount, + &state->nsrecs, &state->nscount, + &state->additional, &state->arcount); TALLOC_FREE(subreq); - if (tevent_req_werror(req, err)) { + + /* If you get an error, attempt a different forwarder */ + if (!W_ERROR_IS_OK(werr)) { + if (state->forwarders != NULL) { + DLIST_REMOVE(state->forwarders, state->forwarders); + } + + /* If you have run out of forwarders, simply finish */ + if (state->forwarders == NULL) { + tevent_req_werror(req, werr); + return; + } + + DEBUG(5, ("DNS query returned %s, trying another forwarder.\n", + win_errstr(werr))); + subreq = ask_forwarder_send(state, state->ev, state->dns, + state->forwarders->forwarder, + state->question); + + if (tevent_req_nomem(subreq, req)) { + return; + } + + tevent_req_set_callback(subreq, + dns_server_process_query_got_response, + req); return; } + tevent_req_done(req); } @@ -1037,9 +1088,36 @@ static void dns_server_process_query_got_auth(struct tevent_req *subreq) werr = handle_authoritative_recv(subreq); TALLOC_FREE(subreq); - if (tevent_req_werror(req, werr)) { + + /* If you get an error, attempt a different forwarder */ + if (!W_ERROR_IS_OK(werr)) { + if (state->forwarders != NULL) { + DLIST_REMOVE(state->forwarders, state->forwarders); + } + + /* If you have run out of forwarders, simply finish */ + if (state->forwarders == NULL) { + tevent_req_werror(req, werr); + return; + } + + DEBUG(5, ("Error: %s, trying a different forwarder.\n", + win_errstr(werr))); + subreq = handle_authoritative_send(state, state->ev, state->dns, + state->forwarders->forwarder, + state->question, &state->answers, + &state->nsrecs); + + if (tevent_req_nomem(subreq, req)) { + return; + } + + tevent_req_set_callback(subreq, + dns_server_process_query_got_auth, + req); return; } + state->ancount = talloc_array_length(state->answers); state->nscount = talloc_array_length(state->nsrecs); state->arcount = talloc_array_length(state->additional); diff --git a/source4/dns_server/dns_server.c b/source4/dns_server/dns_server.c index a2dc151..ae7ec7a 100644 --- a/source4/dns_server/dns_server.c +++ b/source4/dns_server/dns_server.c @@ -123,7 +123,7 @@ static struct tevent_req *dns_process_send(TALLOC_CTX *mem_ctx, struct dns_process_state *state; enum ndr_err_code ndr_err; WERROR ret; - const char *forwarder = lpcfg_dns_forwarder(dns->task->lp_ctx); + const char **forwarder = lpcfg_dns_forwarder(dns->task->lp_ctx); req = tevent_req_create(mem_ctx, &state, struct dns_process_state); if (req == NULL) { return NULL; @@ -169,8 +169,8 @@ static struct tevent_req *dns_process_send(TALLOC_CTX *mem_ctx, state->state.flags = state->in_packet.operation; state->state.flags |= DNS_FLAG_REPLY; - - if (forwarder && *forwarder) { + + if (forwarder && *forwarder && **forwarder) { state->state.flags |= DNS_FLAG_RECURSION_AVAIL; } -- 1.9.1 From fac024e61bb8caf8caa7c2e107ca7ae1671465f2 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Thu, 17 Mar 2016 17:13:28 +1300 Subject: [PATCH 5/9] tests/dns_forwarder: Add testing for DNS forwarding 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 Pair-programmed-with: Douglas Bagnall --- python/samba/tests/dns_forwarder.py | 530 +++++++++++++++++++++ python/samba/tests/dns_forwarder_helpers/server.py | 76 +++ selftest/selftest.pl | 2 + selftest/target/Samba.pm | 2 + selftest/target/Samba4.pm | 7 +- source4/selftest/tests.py | 2 + 6 files changed, 618 insertions(+), 1 deletion(-) create mode 100644 python/samba/tests/dns_forwarder.py create mode 100644 python/samba/tests/dns_forwarder_helpers/server.py diff --git a/python/samba/tests/dns_forwarder.py b/python/samba/tests/dns_forwarder.py new file mode 100644 index 0000000..1a5d032 --- /dev/null +++ b/python/samba/tests/dns_forwarder.py @@ -0,0 +1,530 @@ +# Unix SMB/CIFS implementation. +# Copyright (C) Kai Blin 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 . +# + +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 [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): + 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 index 0000000..b9eeff4 --- /dev/null +++ b/python/samba/tests/dns_forwarder_helpers/server.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# 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() diff --git a/selftest/selftest.pl b/selftest/selftest.pl index ff5f27d..7b4d4de 100755 --- a/selftest/selftest.pl +++ b/selftest/selftest.pl @@ -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", diff --git a/selftest/target/Samba.pm b/selftest/target/Samba.pm index 6ca1036..7573383 100644 --- a/selftest/target/Samba.pm +++ b/selftest/target/Samba.pm @@ -283,6 +283,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 diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index 6963d88..e4343f8 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -1398,9 +1398,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", @@ -1424,6 +1427,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")); diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 45cff59..f2bc0f0 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -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') -- 1.9.1 From 11fcd3385afc1cb210db314c6da29e5493305ddc Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Wed, 6 Apr 2016 15:44:58 +1200 Subject: [PATCH 6/9] tests/dns: Add additional testing of CNAME handling RFC 1034, for instance, describes that all intermediate CNAMEs should be returned. As it is, CNAME do not return all found intermediate results in the case of straightforward failure. It should be noted that in the case of forwarding success, ALL intermediate paths are returned, including the failure ones. Signed-off-by: Garming Sam --- python/samba/tests/dns.py | 91 +++++++++++++++++++++++++++++++++++++++-------- selftest/knownfail | 1 + 2 files changed, 78 insertions(+), 14 deletions(-) diff --git a/python/samba/tests/dns.py b/python/samba/tests/dns.py index 048cd03..babf898 100644 --- a/python/samba/tests/dns.py +++ b/python/samba/tests/dns.py @@ -758,33 +758,33 @@ class TestDNSUpdates(DNSTest): class TestComplexQueries(DNSTest): - - def setUp(self): - super(TestComplexQueries, self).setUp() + def make_dns_update(self, key, value, qtype): p = self.make_name_packet(dns.DNS_OPCODE_UPDATE) - updates = [] name = self.get_dns_domain() - u = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN) - updates.append(u) - self.finish_name_packet(p, updates) + self.finish_name_packet(p, [u]) - updates = [] r = dns.res_rec() - r.name = "cname_test.%s" % self.get_dns_domain() - r.rr_type = dns.DNS_QTYPE_CNAME + r.name = key + r.rr_type = qtype r.rr_class = dns.DNS_QCLASS_IN r.ttl = 900 r.length = 0xffff - r.rdata = "%s.%s" % (self.server, self.get_dns_domain()) - updates.append(r) - p.nscount = len(updates) + rdata = value + r.rdata = rdata + updates = [r] + p.nscount = 1 p.nsrecs = updates - response = self.dns_transaction_udp(p) self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + def setUp(self): + super(TestComplexQueries, self).setUp() + name = "cname_test.%s" % self.get_dns_domain() + rdata = "%s.%s" % (self.server, self.get_dns_domain()) + self.make_dns_update(name, rdata, dns.DNS_QTYPE_CNAME) + def tearDown(self): super(TestComplexQueries, self).tearDown() p = self.make_name_packet(dns.DNS_OPCODE_UPDATE) @@ -833,6 +833,69 @@ class TestComplexQueries(DNSTest): self.assertEquals(response.answers[1].rdata, self.server_ip) + def test_cname_two_chain(self): + name0 = "cnamechain0.%s" % self.get_dns_domain() + name1 = "cnamechain1.%s" % self.get_dns_domain() + name2 = "cnamechain2.%s" % self.get_dns_domain() + self.make_dns_update(name1, name2, dns.DNS_QTYPE_CNAME) + self.make_dns_update(name2, name0, dns.DNS_QTYPE_CNAME) + self.make_dns_update(name0, server_ip, dns.DNS_QTYPE_A) + + 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) + response = self.dns_transaction_udp(p) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY) + self.assertEquals(response.ancount, 3) + + self.assertEquals(response.answers[0].rr_type, dns.DNS_QTYPE_CNAME) + self.assertEquals(response.answers[0].name, name1) + self.assertEquals(response.answers[0].rdata, name2) + + self.assertEquals(response.answers[1].rr_type, dns.DNS_QTYPE_CNAME) + self.assertEquals(response.answers[1].name, name2) + self.assertEquals(response.answers[1].rdata, name0) + + self.assertEquals(response.answers[2].rr_type, dns.DNS_QTYPE_A) + self.assertEquals(response.answers[2].rdata, + self.server_ip) + + def test_cname_two_chain_not_matching_qtype(self): + name0 = "cnamechain0.%s" % self.get_dns_domain() + name1 = "cnamechain1.%s" % self.get_dns_domain() + name2 = "cnamechain2.%s" % self.get_dns_domain() + self.make_dns_update(name1, name2, dns.DNS_QTYPE_CNAME) + self.make_dns_update(name2, name0, dns.DNS_QTYPE_CNAME) + self.make_dns_update(name0, server_ip, dns.DNS_QTYPE_A) + + p = self.make_name_packet(dns.DNS_OPCODE_QUERY) + questions = [] + q = self.make_name_question(name1, dns.DNS_QTYPE_TXT, + dns.DNS_QCLASS_IN) + questions.append(q) + + self.finish_name_packet(p, questions) + response = self.dns_transaction_udp(p) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY) + + # CNAME should return all intermediate results! + # Only the A records exists, not the TXT. + self.assertEquals(response.ancount, 2) + + self.assertEquals(response.answers[0].rr_type, dns.DNS_QTYPE_CNAME) + self.assertEquals(response.answers[0].name, name1) + self.assertEquals(response.answers[0].rdata, name2) + + self.assertEquals(response.answers[1].rr_type, dns.DNS_QTYPE_CNAME) + self.assertEquals(response.answers[1].name, name2) + self.assertEquals(response.answers[1].rdata, name3) + class TestInvalidQueries(DNSTest): def test_one_a_query(self): diff --git a/selftest/knownfail b/selftest/knownfail index e17bcee..c9f4fb0 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -325,3 +325,4 @@ # we don't allow auth_level_connect anymore... # ^samba3.blackbox.rpcclient.*ncacn_np.*with.*connect.*rpcclient # we don't allow auth_level_connect anymore +^samba.tests.dns.__main__.TestComplexQueries.test_cname_two_chain_not_matching_qtype -- 1.9.1 From 7bf77dbc6683b1ddb3aad78bff191a2d79d14eac Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Mon, 11 Apr 2016 15:18:34 +1200 Subject: [PATCH 7/9] tests/dns_forwarder: remove statically defined IPs Signed-off-by: Garming Sam --- python/samba/tests/dns_forwarder.py | 50 ++++++++++++++++++++++++------------- source4/selftest/tests.py | 2 +- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/python/samba/tests/dns_forwarder.py b/python/samba/tests/dns_forwarder.py index 1a5d032..947e9c6 100644 --- a/python/samba/tests/dns_forwarder.py +++ b/python/samba/tests/dns_forwarder.py @@ -33,7 +33,7 @@ import samba.getopt as options import optparse import subprocess -parser = optparse.OptionParser("dns_forwarder.py [options]") +parser = optparse.OptionParser("dns_forwarder.py (dns forwarder)+ [options]") sambaopts = options.SambaOptions(parser) parser.add_option_group(sambaopts) @@ -55,12 +55,14 @@ creds = credopts.get_credentials(lp) timeout = opts.timeout -if len(args) < 2: +if len(args) < 3: parser.print_usage() sys.exit(1) server_name = args[0] server_ip = args[1] +dns_servers = args[2:] + creds.set_krb_forwardable(credentials.NO_KRB_FORWARDABLE) def make_txt_record(records): @@ -198,7 +200,7 @@ class TestDnsForwarding(DNSTest): p.kill() def test_comatose_forwarder(self): - s = self.start_toy_server('127.0.0.36', 53, 'forwarder1') + s = self.start_toy_server(dns_servers[0], 53, 'forwarder1') s.send("timeout 1000000", 0) # make DNS query @@ -222,7 +224,7 @@ class TestDnsForwarding(DNSTest): pass def test_single_forwarder(self): - s = self.start_toy_server('127.0.0.36', 53, 'forwarder1') + s = self.start_toy_server(dns_servers[0], 53, 'forwarder1') ad = contact_real_server(server_ip, 53) name = "dsfsfds.dsfsdfs" p = self.make_name_packet(dns.DNS_OPCODE_QUERY) @@ -271,7 +273,7 @@ class TestDnsForwarding(DNSTest): def test_single_forwarder_waiting_forever(self): - s = self.start_toy_server('127.0.0.36', 53, 'forwarder1') + s = self.start_toy_server(dns_servers[0], 53, 'forwarder1') s.send('timeout 10000', 0) ad = contact_real_server(server_ip, 53) name = "dsfsfds.dsfsdfs" @@ -291,14 +293,16 @@ class TestDnsForwarding(DNSTest): 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') + if len(dns_servers) < 2: + print "Ignoring test_double_forwarder_first_frozen" + return + s1 = self.start_toy_server(dns_servers[0], 53, 'forwarder1') + s2 = self.start_toy_server(dns_servers[1], 53, 'forwarder2') s1.send('timeout 1000', 0) ad = contact_real_server(server_ip, 53) name = "dsfsfds.dsfsdfs" @@ -324,7 +328,10 @@ class TestDnsForwarding(DNSTest): 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') + if len(dns_servers) < 2: + print "Ignoring test_double_forwarder_first_down" + return + s2 = self.start_toy_server(dns_servers[1], 53, 'forwarder2') ad = contact_real_server(server_ip, 53) name = "dsfsfds.dsfsdfs" p = self.make_name_packet(dns.DNS_OPCODE_QUERY) @@ -349,8 +356,11 @@ class TestDnsForwarding(DNSTest): 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') + if len(dns_servers) < 2: + print "Ignoring test_double_forwarder_both_slow" + return + s1 = self.start_toy_server(dns_servers[0], 53, 'forwarder1') + s2 = self.start_toy_server(dns_servers[1], 53, 'forwarder2') s1.send('timeout 1.5', 0) s2.send('timeout 1.5', 0) ad = contact_real_server(server_ip, 53) @@ -377,7 +387,7 @@ class TestDnsForwarding(DNSTest): 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') + s1 = self.start_toy_server(dns_servers[0], 53, 'forwarder1') ad = contact_real_server(server_ip, 53) name = "resolve.cname" @@ -403,7 +413,7 @@ class TestDnsForwarding(DNSTest): 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') + s1 = self.start_toy_server(dns_servers[0], 53, 'forwarder1') name = 'resolve.cname.%s' % self.get_dns_domain() self.make_cname_update(name, "dsfsfds.dsfsdfs") @@ -431,8 +441,11 @@ class TestDnsForwarding(DNSTest): 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') + if len(dns_servers) < 2: + print "Ignoring test_cname_forwarding_with_slow_server" + return + s1 = self.start_toy_server(dns_servers[0], 53, 'forwarder1') + s2 = self.start_toy_server(dns_servers[1], 53, 'forwarder2') s1.send('timeout 10000', 0) name = 'resolve.cname.%s' % self.get_dns_domain() @@ -461,7 +474,10 @@ class TestDnsForwarding(DNSTest): 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') + if len(dns_servers) < 2: + print "Ignoring test_cname_forwarding_with_server_down" + return + s2 = self.start_toy_server(dns_servers[1], 53, 'forwarder2') name1 = 'resolve1.cname.%s' % self.get_dns_domain() name2 = 'resolve2.cname.%s' % self.get_dns_domain() @@ -492,7 +508,7 @@ class TestDnsForwarding(DNSTest): 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) + s1 = self.start_toy_server(dns_servers[0], 53, name3) name1 = 'resolve1.cname.%s' % self.get_dns_domain() name2 = 'resolve2.cname.%s' % self.get_dns_domain() diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index f2bc0f0..15e7d19 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -358,7 +358,7 @@ 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']) +plantestsuite_loadlist("samba.tests.dns_forwarder", "fl2003dc:local", [python, os.path.join(srcdir(), "python/samba/tests/dns_forwarder.py"), '$SERVER', '$SERVER_IP', '$DNS_FORWARDER1', '$DNS_FORWARDER2', '--machine-pass', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) for t in smbtorture4_testsuites("dns_internal."): plansmbtorture4testsuite(t, "ad_dc_ntvfs:local", '//$SERVER/whavever') -- 1.9.1 From 0e89d4c3955679f77bc4190b80eb2d8fb92957f1 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Wed, 13 Apr 2016 13:09:41 +1200 Subject: [PATCH 8/9] tests/dns_forwarder: Add an extra test for inactive forwarders Signed-off-by: Garming Sam --- python/samba/tests/dns_forwarder.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/python/samba/tests/dns_forwarder.py b/python/samba/tests/dns_forwarder.py index 947e9c6..172de31 100644 --- a/python/samba/tests/dns_forwarder.py +++ b/python/samba/tests/dns_forwarder.py @@ -223,6 +223,33 @@ class TestDnsForwarding(DNSTest): # Expected forwarder to be dead pass + def test_no_active_forwarder(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_TXT, dns.DNS_QCLASS_IN) + questions.append(q) + + self.finish_name_packet(p, questions) + send_packet = ndr.ndr_pack(p) + + 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) + self.assertEqual(data.ancount, 0) + except socket.timeout: + self.fail("DNS server is too slow (timeout %s)" % timeout) + def test_single_forwarder(self): s = self.start_toy_server(dns_servers[0], 53, 'forwarder1') ad = contact_real_server(server_ip, 53) -- 1.9.1 From cee7b039eee215ddf6aea6a72f1569da5979582c Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Mon, 18 Apr 2016 16:31:17 +1200 Subject: [PATCH 9/9] tests/dns_forwarder: Add additional testing for no flag recursive Signed-off-by: Garming Sam --- python/samba/tests/dns_forwarder.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/python/samba/tests/dns_forwarder.py b/python/samba/tests/dns_forwarder.py index 172de31..ee58915 100644 --- a/python/samba/tests/dns_forwarder.py +++ b/python/samba/tests/dns_forwarder.py @@ -250,6 +250,33 @@ class TestDnsForwarding(DNSTest): except socket.timeout: self.fail("DNS server is too slow (timeout %s)" % timeout) + def test_no_flag_recursive_forwarder(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_TXT, dns.DNS_QCLASS_IN) + questions.append(q) + + self.finish_name_packet(p, questions) + send_packet = ndr.ndr_pack(p) + + self.finish_name_packet(p, questions) + # Leave off the recursive flag + 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_NXDOMAIN) + self.assertEqual(data.ancount, 0) + except socket.timeout: + self.fail("DNS server is too slow (timeout %s)" % timeout) + def test_single_forwarder(self): s = self.start_toy_server(dns_servers[0], 53, 'forwarder1') ad = contact_real_server(server_ip, 53) -- 1.9.1