s4-rpc: dnsserver: dns_name_equal() returns boolean
[samba.git] / source4 / rpc_server / dnsserver / dnsdata.c
index e1b7f356ff528fd1433de1697c8db6fcb2ae77ee..fb2547ff7ecc4c858e7c477339c3ce97d73a213b 100644 (file)
@@ -128,6 +128,10 @@ int dns_split_name_components(TALLOC_CTX *tmp_ctx, const char *name, char ***com
        char *str = NULL, *ptr, **list;
        int count = 0;
 
+       if (name == NULL) {
+               return 0;
+       }
+
        str = talloc_strdup(tmp_ctx, name);
        if (!str) {
                goto failed;
@@ -200,7 +204,7 @@ char *dns_split_node_name(TALLOC_CTX *tmp_ctx, const char *node_name, const char
                } else {
                        match = 0;
                        for (i=1; i<=zcount; i++) {
-                               if (strcmp(nlist[ncount-i], zlist[zcount-i]) != 0) {
+                               if (strcasecmp(nlist[ncount-i], zlist[zcount-i]) != 0) {
                                        break;
                                }
                                match++;
@@ -232,7 +236,7 @@ char *dns_split_node_name(TALLOC_CTX *tmp_ctx, const char *node_name, const char
 void dnsp_to_dns_copy(TALLOC_CTX *mem_ctx, struct dnsp_DnssrvRpcRecord *dnsp,
                                struct DNS_RPC_RECORD *dns)
 {
-       int len;
+       int i, len;
 
        ZERO_STRUCTP(dns);
 
@@ -319,8 +323,12 @@ void dnsp_to_dns_copy(TALLOC_CTX *mem_ctx, struct dnsp_DnssrvRpcRecord *dnsp,
                break;
 
        case DNS_TYPE_TXT:
-               dns->data.name.len = strlen(dnsp->data.txt);
-               dns->data.name.str = talloc_strdup(mem_ctx, dnsp->data.txt);
+               dns->data.txt.count = dnsp->data.txt.count;
+               dns->data.txt.str = talloc_array(mem_ctx, struct DNS_RPC_NAME, dnsp->data.txt.count);
+               for (i=0; i<dnsp->data.txt.count; i++) {
+                       dns->data.txt.str[i].str = talloc_strdup(mem_ctx, dnsp->data.txt.str[i]);
+                       dns->data.txt.str[i].len = strlen(dnsp->data.txt.str[i]);
+               }
                break;
 
        case DNS_TYPE_AAAA:
@@ -351,7 +359,7 @@ void dnsp_to_dns_copy(TALLOC_CTX *mem_ctx, struct dnsp_DnssrvRpcRecord *dnsp,
 
 struct dnsp_DnssrvRpcRecord *dns_to_dnsp_copy(TALLOC_CTX *mem_ctx, struct DNS_RPC_RECORD *dns)
 {
-       int len;
+       int i, len;
        struct dnsp_DnssrvRpcRecord *dnsp;
 
        dnsp = talloc_zero(mem_ctx, struct dnsp_DnssrvRpcRecord);
@@ -404,9 +412,9 @@ struct dnsp_DnssrvRpcRecord *dns_to_dnsp_copy(TALLOC_CTX *mem_ctx, struct DNS_RP
 
                len = dns->data.soa.NamePrimaryServer.len;
                if (dns->data.soa.NamePrimaryServer.str[len-1] == '.') {
-                       dnsp->data.soa.mname = talloc_strdup(mem_ctx, dns->data.soa.NamePrimaryServer.str);
-               } else {
                        dnsp->data.soa.mname = talloc_strndup(mem_ctx, dns->data.soa.NamePrimaryServer.str, len-1);
+               } else {
+                       dnsp->data.soa.mname = talloc_strdup(mem_ctx, dns->data.soa.NamePrimaryServer.str);
                }
 
                len = dns->data.soa.ZoneAdministratorEmail.len;
@@ -432,7 +440,11 @@ struct dnsp_DnssrvRpcRecord *dns_to_dnsp_copy(TALLOC_CTX *mem_ctx, struct DNS_RP
                break;
 
        case DNS_TYPE_TXT:
-               dnsp->data.txt = talloc_strdup(mem_ctx, dns->data.name.str);
+               dnsp->data.txt.count = dns->data.txt.count;
+               dnsp->data.txt.str = talloc_array(mem_ctx, const char *, dns->data.txt.count);
+               for (i=0; i<dns->data.txt.count; i++) {
+                       dnsp->data.txt.str[i] = talloc_strdup(mem_ctx, dns->data.txt.str[i].str);
+               }
                break;
 
        case DNS_TYPE_AAAA:
@@ -462,6 +474,192 @@ struct dnsp_DnssrvRpcRecord *dns_to_dnsp_copy(TALLOC_CTX *mem_ctx, struct DNS_RP
 }
 
 
+/* Intialize tree with given name as the root */
+static struct dns_tree *dns_tree_init(TALLOC_CTX *mem_ctx, const char *name, void *data)
+{
+       struct dns_tree *tree;
+
+       tree = talloc_zero(mem_ctx, struct dns_tree);
+       if (tree == NULL) {
+               return NULL;
+       }
+
+       tree->name = talloc_strdup(tree, name);
+       if (tree->name == NULL) {
+               talloc_free(tree);
+               return NULL;
+       }
+
+       tree->data = data;
+
+       return tree;
+}
+
+
+/* Add a child one level below */
+static struct dns_tree *dns_tree_add(struct dns_tree *tree, const char *name, void *data)
+{
+       struct dns_tree *node;
+
+       node = talloc_zero(tree, struct dns_tree);
+       if (node == NULL) {
+               return NULL;
+       }
+
+       node->name = talloc_strdup(tree, name);
+       if (node->name == NULL) {
+               talloc_free(node);
+               return NULL;
+       }
+       node->level = tree->level + 1;
+       node->num_children = 0;
+       node->children = NULL;
+       node->data = data;
+
+       if (tree->num_children == 0) {
+               tree->children = talloc_zero(tree, struct dns_tree *);
+       } else {
+               tree->children = talloc_realloc(tree, tree->children, struct dns_tree *,
+                                               tree->num_children+1);
+       }
+       if (tree->children == NULL) {
+               talloc_free(node);
+               return NULL;
+       }
+       tree->children[tree->num_children] = node;
+       tree->num_children++;
+
+       return node;
+}
+
+/* Find a node that matches the name components */
+static struct dns_tree *dns_tree_find(struct dns_tree *tree, int ncount, char **nlist, int *match_count)
+{
+       struct dns_tree *node, *next;
+       int i, j, start;
+
+       *match_count = -1;
+
+       if (strcmp(tree->name, "@") == 0) {
+               start = 0;
+       } else {
+               if (strcasecmp(tree->name, nlist[ncount-1]) != 0) {
+                       return NULL;
+               }
+               start = 1;
+               *match_count = 0;
+       }
+
+       node = tree;
+       for (i=start; i<ncount; i++) {
+               if (node->num_children == 0) {
+                       break;
+               }
+               next = NULL;
+               for (j=0; j<node->num_children; j++) {
+                       if (strcasecmp(nlist[(ncount-1)-i], node->children[j]->name) == 0) {
+                               next = node->children[j];
+                               *match_count = i;
+                               break;
+                       }
+               }
+               if (next == NULL) {
+                       break;
+               } else {
+                       node = next;
+               }
+       }
+
+       return node;
+}
+
+/* Build a 2-level tree for resulting dns names */
+struct dns_tree *dns_build_tree(TALLOC_CTX *mem_ctx, const char *name, struct ldb_result *res)
+{
+       struct dns_tree *root, *base, *tree, *node;
+       const char *ptr;
+       int rootcount, ncount;
+       char **nlist;
+       int i, level, match_count;
+
+       rootcount = dns_split_name_components(mem_ctx, name, &nlist);
+       if (rootcount <= 0) {
+               return NULL;
+       }
+
+       root = dns_tree_init(mem_ctx, nlist[rootcount-1], NULL);
+       if (root == NULL) {
+               return NULL;
+       }
+
+       tree = root;
+       for (i=rootcount-2; i>=0; i--) {
+               tree = dns_tree_add(tree, nlist[i], NULL);
+               if (tree == NULL) {
+                       goto failed;
+               }
+       }
+
+       base = tree;
+
+       /* Add all names in the result in a tree */
+       for (i=0; i<res->count; i++) {
+               ptr = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL);
+
+               if (strcmp(ptr, "@") == 0) {
+                       base->data = res->msgs[i];
+                       continue;
+               } else if (strcasecmp(ptr, name) == 0) {
+                       base->data = res->msgs[i];
+                       continue;
+               }
+
+               ncount = dns_split_name_components(root, ptr, &nlist);
+               if (ncount < 0) {
+                       goto failed;
+               }
+
+               /* Find matching node */
+               tree = dns_tree_find(root, ncount, nlist, &match_count);
+               if (tree == NULL) {
+                       goto failed;
+               }
+
+               /* If the node is on leaf, then add record data */
+               if (match_count+1 == ncount) {
+                       tree->data = res->msgs[i];
+               }
+
+               /* Add missing name components */
+               for (level=match_count+1; level<ncount; level++) {
+                       if (tree->level == rootcount+1) {
+                               break;
+                       }
+                       if (level == ncount-1) {
+                               node = dns_tree_add(tree, nlist[(ncount-1)-level], res->msgs[i]);
+                       } else {
+                               node = dns_tree_add(tree, nlist[(ncount-1)-level], NULL);
+                       }
+                       if (node == NULL) {
+                               goto failed;
+                       }
+                       tree = node;
+               }
+
+               talloc_free(nlist);
+       }
+
+       /* Mark the base record, so it can be found easily */
+       base->level = -1;
+
+       return root;
+
+failed:
+       talloc_free(root);
+       return NULL;
+}
+
+
 static void _dns_add_name(TALLOC_CTX *mem_ctx, const char *name, char ***add_names, int *add_count)
 {
        int i;
@@ -469,7 +667,7 @@ static void _dns_add_name(TALLOC_CTX *mem_ctx, const char *name, char ***add_nam
        int count = *add_count;
 
        for (i=0; i<count; i++) {
-               if (strcmp(ptr[i], name) == 0) {
+               if (strcasecmp(ptr[i], name) == 0) {
                        return;
                }
        }
@@ -529,88 +727,58 @@ WERROR dns_fill_records_array(TALLOC_CTX *mem_ctx,
                                unsigned int select_flag,
                                const char *branch_name,
                                struct ldb_message *msg,
+                               int num_children,
                                struct DNS_RPC_RECORDS_ARRAY *recs,
                                char ***add_names,
                                int *add_count)
 {
-       const char *nodename;
        struct ldb_message_element *el;
+       const char *ptr;
        int i, j;
-       bool found, node_is_rootzone;
+       bool found;
 
-       /* Check if we already have created record for the branch */
-       found = false;
-       if (branch_name == NULL) {
-               i = 0;
-               if (recs->count > 0) {
-                       found = true;
-               }
+       if (recs->count == 0) {
+               recs->rec = talloc_zero(recs, struct DNS_RPC_RECORDS);
        } else {
-               for (i=0; i<recs->count; i++) {
-                       if (strcmp(branch_name, recs->rec[i].dnsNodeName.str) == 0) {
-                               found = true;
-                               break;
-                       }
-               }
+               recs->rec = talloc_realloc(recs, recs->rec, struct DNS_RPC_RECORDS, recs->count+1);
        }
+       if (recs->rec == NULL) {
+               return WERR_NOMEM;
+       }
+       i = recs->count;
+       recs->rec[i].wLength = 0;
+       recs->rec[i].wRecordCount = 0;
+       recs->rec[i].dwChildCount = num_children;
 
-       /* If not, add empty record */
-       if (!found) {
-               if (recs->count == 0) {
-                       recs->rec = talloc_zero(recs, struct DNS_RPC_RECORDS);
-               } else {
-                       recs->rec = talloc_realloc(recs, recs->rec, struct DNS_RPC_RECORDS, recs->count+1);
-               }
-               if (recs->rec == NULL) {
-                       return WERR_NOMEM;
-               }
-               i = recs->count;
-               recs->rec[i].wLength = 0;
-               recs->rec[i].wRecordCount = 0;
-               recs->rec[i].dwChildCount = 0;
-
-               /* The base records returned with empty name */
-               /* Children records returned with names */
-               if (branch_name == NULL) {
-                       recs->rec[i].dnsNodeName.str = talloc_strdup(recs, "");
-                       recs->rec[i].dnsNodeName.len = 0;
-               } else {
-                       recs->rec[i].dnsNodeName.str = talloc_strdup(recs, branch_name);
-                       recs->rec[i].dnsNodeName.len = strlen(branch_name);
-               }
-               recs->rec[i].records = talloc_zero_array(recs, struct DNS_RPC_RECORD, 0);
-               recs->count++;
+       /* The base records returned with empty name */
+       /* Children records returned with names */
+       if (branch_name == NULL) {
+               recs->rec[i].dnsNodeName.str = talloc_strdup(recs, "");
+               recs->rec[i].dnsNodeName.len = 0;
+       } else {
+               recs->rec[i].dnsNodeName.str = talloc_strdup(recs, branch_name);
+               recs->rec[i].dnsNodeName.len = strlen(branch_name);
        }
+       recs->rec[i].records = talloc_zero_array(recs, struct DNS_RPC_RECORD, 0);
+       recs->count++;
 
        /* Allow empty records */
        if (msg == NULL) {
                return WERR_OK;
        }
 
-       nodename = ldb_msg_find_attr_as_string(msg, "name", NULL);
-
-       if (strcmp(nodename, "@") == 0) {
-               node_is_rootzone = true;
-       } else {
-               node_is_rootzone = false;
-
-               /* child record */
-               if (branch_name != NULL) {
-                       if (branch_name[strlen(branch_name)-1] != '.'
-                               && strcmp(nodename, branch_name) != 0) {
-                               recs->rec[i].dwChildCount++;
-                               return WERR_OK;
-                       }
-               }
+       /* Do not return RR records, if the node has children */
+       if (branch_name != NULL && num_children > 0) {
+               return WERR_OK;
        }
 
+       ptr = ldb_msg_find_attr_as_string(msg, "name", NULL);
        el = ldb_msg_find_element(msg, "dnsRecord");
        if (el == NULL || el->values == 0) {
-               DEBUG(0, ("dnsserver: Missing dnsRecord for %s\n", ldb_dn_get_linearized(msg->dn)));
                return WERR_OK;
        }
 
-       /* branch level record */
+       /* Add RR records */
        for (j=0; j<el->num_values; j++) {
                struct dnsp_DnssrvRpcRecord dnsp_rec;
                struct DNS_RPC_RECORD *dns_rec;
@@ -661,8 +829,12 @@ WERROR dns_fill_records_array(TALLOC_CTX *mem_ctx,
                                dnsp_to_dns_copy(recs, &dnsp_rec, dns_rec);
 
                                /* Fix record flags */
-                               if (node_is_rootzone) {
-                                       dns_rec->dwFlags |= (DNS_RPC_FLAG_ZONE_ROOT | DNS_RPC_FLAG_AUTH_ZONE_ROOT);
+                               if (strcmp(ptr, "@") == 0) {
+                                       dns_rec->dwFlags |= DNS_RPC_FLAG_ZONE_ROOT;
+
+                                       if (dnsp_rec.rank == DNS_RANK_ZONE) {
+                                               dns_rec->dwFlags |= DNS_RPC_FLAG_AUTH_ZONE_ROOT;
+                                       }
                                }
 
                                if (dns_rec->dwFlags == DNS_RANK_NS_GLUE) {
@@ -696,14 +868,14 @@ int dns_name_compare(const struct ldb_message **m1, const struct ldb_message **m
        if (name1[0] == '@') {
                return -1;
        }
-       if (search_name && strcmp(name1, search_name) == 0) {
+       if (search_name && strcasecmp(name1, search_name) == 0) {
                return -1;
        }
 
        if (name2[0] == '@') {
                return 1;
        }
-       if (search_name && strcmp(name2, search_name) == 0) {
+       if (search_name && strcasecmp(name2, search_name) == 0) {
                return 1;
        }
 
@@ -713,7 +885,7 @@ int dns_name_compare(const struct ldb_message **m1, const struct ldb_message **m
        if (ptr1 == NULL) {
                ptr1 = name1;
        } else {
-               if (search_name && strcmp(ptr1+1, search_name) == 0) {
+               if (search_name && strcasecmp(ptr1+1, search_name) == 0) {
                        ptr1--;
                        while (ptr1 != name1) {
                                ptr1--;
@@ -731,7 +903,7 @@ int dns_name_compare(const struct ldb_message **m1, const struct ldb_message **m
        if (ptr2 == NULL) {
                ptr2 = name2;
        } else {
-               if (search_name && strcmp(ptr2+1, search_name) == 0) {
+               if (search_name && strcasecmp(ptr2+1, search_name) == 0) {
                        ptr2--;
                        while (ptr2 != name2) {
                                ptr2--;
@@ -765,6 +937,9 @@ bool dns_name_equal(const char *name1, const char *name2)
 
 bool dns_record_match(struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2)
 {
+       bool status;
+       int i;
+
        if (rec1->wType != rec2->wType) {
                return false;
        }
@@ -777,14 +952,14 @@ bool dns_record_match(struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRe
                return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
 
        case DNS_TYPE_NS:
-               return dns_name_equal(rec1->data.ns, rec1->data.ns);
+               return dns_name_equal(rec1->data.ns, rec2->data.ns);
 
        case DNS_TYPE_CNAME:
-               return dns_name_equal(rec1->data.cname, rec1->data.cname);
+               return dns_name_equal(rec1->data.cname, rec2->data.cname);
 
        case DNS_TYPE_SOA:
-               return dns_name_equal(rec1->data.soa.mname, rec2->data.soa.mname) == 0 &&
-                       dns_name_equal(rec1->data.soa.rname, rec2->data.soa.rname) == 0 &&
+               return dns_name_equal(rec1->data.soa.mname, rec2->data.soa.mname) &&
+                       dns_name_equal(rec1->data.soa.rname, rec2->data.soa.rname) &&
                        rec1->data.soa.serial == rec2->data.soa.serial &&
                        rec1->data.soa.refresh == rec2->data.soa.refresh &&
                        rec1->data.soa.retry == rec2->data.soa.retry &&
@@ -792,14 +967,22 @@ bool dns_record_match(struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRe
                        rec1->data.soa.minimum == rec2->data.soa.minimum;
 
        case DNS_TYPE_PTR:
-               return strcmp(rec1->data.ptr, rec2->data.ptr) == 0;
+               return dns_name_equal(rec1->data.ptr, rec2->data.ptr);
 
        case DNS_TYPE_MX:
-               return rec1->data.mx.wPriority == rec2->data.srv.wPriority &&
-                       dns_name_equal(rec1->data.mx.nameTarget, rec2->data.srv.nameTarget);
+               return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
+                       dns_name_equal(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget);
 
        case DNS_TYPE_TXT:
-               return strcmp(rec1->data.txt, rec2->data.txt) == 0;
+               if (rec1->data.txt.count != rec2->data.txt.count) {
+                       return false;
+               }
+               status = true;
+               for (i=0; i<rec1->data.txt.count; i++) {
+                       status = status && (strcmp(rec1->data.txt.str[i],
+                                                  rec2->data.txt.str[i]) == 0);
+               }
+               return status;
 
        case DNS_TYPE_AAAA:
                return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0;