From f3df2988ba6928cde0bd89da321bbe74fd76f53f Mon Sep 17 00:00:00 2001 From: Kai Blin Date: Fri, 1 Jun 2012 08:05:54 +0200 Subject: s4 dns: Correctly handle A questions for CNAMEs When an A/AAAA lookup is made for a name that actually is a CNAME record, we need to return the CNAME record, and then do the A/AAAA lookup for the name the CNAME points at. This still fails for CNAMEs pointing at records for domains we need to ask our forwarders for. Autobuild-User: Kai Blin Autobuild-Date: Wed Jun 6 15:23:55 CEST 2012 on sn-devel-104 --- source4/dns_server/dns_query.c | 61 +++++++++++++++++++++-- source4/scripting/python/samba/tests/dns.py | 76 +++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 5 deletions(-) (limited to 'source4') diff --git a/source4/dns_server/dns_query.c b/source4/dns_server/dns_query.c index 0e63058228..40df3a1ff0 100644 --- a/source4/dns_server/dns_query.c +++ b/source4/dns_server/dns_query.c @@ -231,11 +231,11 @@ static WERROR handle_question(struct dns_server *dns, const struct dns_name_question *question, struct dns_res_rec **answers, uint16_t *ancount) { - struct dns_res_rec *ans; + struct dns_res_rec *ans = *answers; WERROR werror; unsigned int ri; struct dnsp_DnssrvRpcRecord *recs; - uint16_t rec_count, ai = 0; + uint16_t rec_count, ai = *ancount; struct ldb_dn *dn = NULL; werror = dns_name2dn(dns, mem_ctx, question->name, &dn); @@ -244,16 +244,67 @@ static WERROR handle_question(struct dns_server *dns, werror = dns_lookup_records(dns, mem_ctx, dn, &recs, &rec_count); W_ERROR_NOT_OK_RETURN(werror); - ans = talloc_zero_array(mem_ctx, struct dns_res_rec, rec_count); - W_ERROR_HAVE_NO_MEMORY(ans); + ans = talloc_realloc(mem_ctx, ans, struct dns_res_rec, rec_count + ai); + if (ans == NULL) { + return WERR_NOMEM; + } for (ri = 0; ri < rec_count; ri++) { + if ((recs[ri].wType == DNS_TYPE_CNAME) && + ((question->question_type == DNS_QTYPE_A) || + (question->question_type == DNS_QTYPE_AAAA))) { + struct dns_name_question *new_q = + talloc(mem_ctx, struct dns_name_question); + + if (new_q == NULL) { + return WERR_NOMEM; + } + + /* We reply with one more record, so grow the array */ + ans = talloc_realloc(mem_ctx, ans, struct dns_res_rec, + rec_count + 1); + if (ans == NULL) { + TALLOC_FREE(new_q); + return WERR_NOMEM; + } + + /* First put in the CNAME record */ + werror = create_response_rr(question, &recs[ri], &ans, &ai); + if (!W_ERROR_IS_OK(werror)) { + return werror; + } + + /* And then look up the name it points at.. */ + + /* First build up the new question */ + new_q->question_type = question->question_type; + new_q->question_class = question->question_class; + if (new_q->question_type == DNS_QTYPE_A) { + new_q->name = talloc_strdup(new_q, recs[ri].data.ipv4); + } else if (new_q->question_type == DNS_QTYPE_AAAA) { + new_q->name = talloc_strdup(new_q, recs[ri].data.ipv6); + } + if (new_q->name == NULL) { + TALLOC_FREE(new_q); + return WERR_NOMEM; + } + /* and then call the lookup again */ + werror = handle_question(dns, mem_ctx, new_q, &ans, &ai); + if (!W_ERROR_IS_OK(werror)) { + return werror; + } + + + continue; + } if ((question->question_type != DNS_QTYPE_ALL) && (recs[ri].wType != question->question_type)) { continue; } werror = create_response_rr(question, &recs[ri], &ans, &ai); - W_ERROR_NOT_OK_RETURN(werror); + if (!W_ERROR_IS_OK(werror)) { + return werror; + } } if (ai == 0) { diff --git a/source4/scripting/python/samba/tests/dns.py b/source4/scripting/python/samba/tests/dns.py index a032f23a50..aa6a4e6028 100644 --- a/source4/scripting/python/samba/tests/dns.py +++ b/source4/scripting/python/samba/tests/dns.py @@ -469,6 +469,82 @@ class TestDNSUpdates(DNSTest): self.assert_dns_rcode_equals(response, dns.DNS_RCODE_NXDOMAIN) +class TestComplexQueries(DNSTest): + def setUp(self): + super(TestComplexQueries, self).setUp() + 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) + + updates = [] + r = dns.res_rec() + r.name = "cname_test.%s" % self.get_dns_domain() + r.rr_type = dns.DNS_QTYPE_CNAME + r.rr_class = dns.DNS_QCLASS_IN + r.ttl = 900 + r.length = 0xffff + r.rdata = "%s.%s" % (os.getenv('DC_SERVER'), self.get_dns_domain()) + updates.append(r) + p.nscount = len(updates) + p.nsrecs = updates + + response = self.dns_transaction_udp(p) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + + def tearDown(self): + super(TestComplexQueries, self).tearDown() + 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) + + updates = [] + r = dns.res_rec() + r.name = "cname_test.%s" % self.get_dns_domain() + r.rr_type = dns.DNS_QTYPE_CNAME + r.rr_class = dns.DNS_QCLASS_NONE + r.ttl = 0 + r.length = 0xffff + r.rdata = "%s.%s" % (os.getenv('DC_SERVER'), self.get_dns_domain()) + updates.append(r) + p.nscount = len(updates) + p.nsrecs = updates + + response = self.dns_transaction_udp(p) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + + def test_one_a_query(self): + "create a query packet containing one query record" + p = self.make_name_packet(dns.DNS_OPCODE_QUERY) + questions = [] + + name = "cname_test.%s" % self.get_dns_domain() + q = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN) + print "asking for ", q.name + 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, 2) + self.assertEquals(response.answers[0].rr_type, dns.DNS_QTYPE_CNAME) + self.assertEquals(response.answers[0].rdata, "%s.%s" % + (os.getenv('DC_SERVER'), self.get_dns_domain())) + self.assertEquals(response.answers[1].rr_type, dns.DNS_QTYPE_A) + self.assertEquals(response.answers[1].rdata, + os.getenv('DC_SERVER_IP')) + + if __name__ == "__main__": import unittest unittest.main() -- cgit