s4 dns: Correctly handle A questions for CNAMEs
authorKai Blin <kai@samba.org>
Fri, 1 Jun 2012 06:05:54 +0000 (08:05 +0200)
committerKai Blin <kai@samba.org>
Wed, 6 Jun 2012 13:23:55 +0000 (15:23 +0200)
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 <kai@samba.org>
Autobuild-Date: Wed Jun  6 15:23:55 CEST 2012 on sn-devel-104

source4/dns_server/dns_query.c
source4/scripting/python/samba/tests/dns.py

index 0e630582280d39113be86d7c9ef7fed0a3f0d01f..40df3a1ff0f1580484697ae4d95e2d888108797e 100644 (file)
@@ -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) {
index a032f23a50f1cda414cef42d98a580b0c66cd07a..aa6a4e602888a47765dd284e3297bb4550df554e 100644 (file)
@@ -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()