s4 dns: Actually handle the update request
authorKai Blin <kai@samba.org>
Fri, 16 Dec 2011 13:21:52 +0000 (14:21 +0100)
committerKai Blin <kai@samba.org>
Sat, 17 Dec 2011 01:46:08 +0000 (02:46 +0100)
source4/dns_server/dns_server.h
source4/dns_server/dns_update.c
source4/dns_server/dns_utils.c

index 8570281baab9174a03a49912d65968ce26f68488..53d63063180c6b9956cdbc24ae0efeda27b27085 100644 (file)
@@ -64,6 +64,12 @@ WERROR dns_lookup_records(struct dns_server *dns,
                          struct ldb_dn *dn,
                          struct dnsp_DnssrvRpcRecord **records,
                          uint16_t *rec_count);
                          struct ldb_dn *dn,
                          struct dnsp_DnssrvRpcRecord **records,
                          uint16_t *rec_count);
+WERROR dns_replace_records(struct dns_server *dns,
+                          TALLOC_CTX *mem_ctx,
+                          struct ldb_dn *dn,
+                          bool needs_add,
+                          const struct dnsp_DnssrvRpcRecord *records,
+                          uint16_t rec_count);
 WERROR dns_name2dn(struct dns_server *dns,
                   TALLOC_CTX *mem_ctx,
                   const char *name,
 WERROR dns_name2dn(struct dns_server *dns,
                   TALLOC_CTX *mem_ctx,
                   const char *name,
index 397384421c4c0ce2e638524b7ac9f257a4a8548c..64b6181d7e349d881cbf65de626dd88283209ded 100644 (file)
@@ -344,6 +344,304 @@ done:
        return WERR_OK;
 }
 
        return WERR_OK;
 }
 
+
+static WERROR handle_one_update(struct dns_server *dns,
+                               TALLOC_CTX *mem_ctx,
+                               const struct dns_name_question *zone,
+                               const struct dns_res_rec *update)
+{
+       struct dnsp_DnssrvRpcRecord *recs = NULL;
+       uint16_t rcount = 0;
+       struct ldb_dn *dn;
+       uint16_t i;
+       WERROR werror;
+       bool needs_add = false;
+
+       DEBUG(1, ("Looking at record: \n"));
+       NDR_PRINT_DEBUG(dns_res_rec, discard_const(update));
+
+       switch (update->rr_type) {
+       case DNS_QTYPE_A:
+               break;
+       case DNS_QTYPE_NS:
+               break;
+       case DNS_QTYPE_CNAME:
+               break;
+       case DNS_QTYPE_SOA:
+               break;
+       case DNS_QTYPE_PTR:
+               break;
+       case DNS_QTYPE_MX:
+               break;
+       case DNS_QTYPE_AAAA:
+               break;
+       case DNS_QTYPE_SRV:
+               break;
+       case DNS_QTYPE_TXT:
+               break;
+       default:
+               return DNS_ERR(NOT_IMPLEMENTED);
+       }
+
+       werror = dns_name2dn(dns, mem_ctx, update->name, &dn);
+       W_ERROR_NOT_OK_RETURN(werror);
+
+       werror = dns_lookup_records(dns, mem_ctx, dn, &recs, &rcount);
+       if (W_ERROR_EQUAL(werror, DNS_ERR(NAME_ERROR))) {
+               recs = NULL;
+               rcount = 0;
+               needs_add = true;
+               werror = WERR_OK;
+       }
+       W_ERROR_NOT_OK_RETURN(werror);
+
+       if (update->rr_class == zone->question_class) {
+               if (update->rr_type == DNS_QTYPE_CNAME) {
+                       /*
+                        * If there is a record in the directory
+                        * that's not a CNAME, ignore update
+                        */
+                       for (i = 0; i < rcount; i++) {
+                               if (recs[i].wType != DNS_TYPE_CNAME) {
+                                       DEBUG(0, ("Skipping update\n"));
+                                       return WERR_OK;
+                               }
+                               break;
+                       }
+
+                       /*
+                        * There should be no entries besides one CNAME record
+                        * per name, so replace everything with the new CNAME
+                        */
+
+                       rcount = 1;
+                       recs = talloc_realloc(mem_ctx, recs,
+                                       struct dnsp_DnssrvRpcRecord, rcount);
+                       W_ERROR_HAVE_NO_MEMORY(recs);
+
+                       werror = dns_rr_to_dnsp(recs, update, &recs[0]);
+                       W_ERROR_NOT_OK_RETURN(werror);
+
+                       werror = dns_replace_records(dns, mem_ctx, dn,
+                                                    needs_add, recs, rcount);
+                       W_ERROR_NOT_OK_RETURN(werror);
+
+                       return WERR_OK;
+               } else {
+                       /*
+                        * If there is a CNAME record for this name,
+                        * ignore update
+                        */
+                       for (i = 0; i < rcount; i++) {
+                               if (recs[i].wType == DNS_TYPE_CNAME) {
+                                       DEBUG(0, ("Skipping update\n"));
+                                       return WERR_OK;
+                               }
+                       }
+               }
+               if (update->rr_type == DNS_QTYPE_SOA) {
+                       bool found = false;
+
+                       /*
+                        * If the zone has no SOA record?? or update's
+                        * serial number is smaller than existing SOA's,
+                        * ignore update
+                        */
+                       for (i = 0; i < rcount; i++) {
+                               if (recs[i].wType == DNS_TYPE_SOA) {
+                                       uint16_t n, o;
+
+                                       n = update->rdata.soa_record.serial;
+                                       o = recs[i].data.soa.serial;
+                                       /*
+                                        * TODO: Implement RFC 1982 comparison
+                                        * logic for RFC2136
+                                        */
+                                       if (n <= o) {
+                                               DEBUG(0, ("Skipping update\n"));
+                                               return WERR_OK;
+                                       }
+                                       found = true;
+                                       break;
+                               }
+                       }
+                       if (!found) {
+                               DEBUG(0, ("Skipping update\n"));
+                               return WERR_OK;
+                       }
+
+                       werror = dns_rr_to_dnsp(mem_ctx, update, &recs[i]);
+                       W_ERROR_NOT_OK_RETURN(werror);
+
+                       for (i++; i < rcount; i++) {
+                               if (recs[i].wType != DNS_TYPE_SOA) {
+                                       continue;
+                               }
+
+                               ZERO_STRUCT(recs[i]);
+                       }
+
+                       werror = dns_replace_records(dns, mem_ctx, dn,
+                                                    needs_add, recs, rcount);
+                       W_ERROR_NOT_OK_RETURN(werror);
+
+                       return WERR_OK;
+               }
+
+               recs = talloc_realloc(mem_ctx, recs,
+                               struct dnsp_DnssrvRpcRecord, rcount+1);
+               W_ERROR_HAVE_NO_MEMORY(recs);
+
+               werror = dns_rr_to_dnsp(recs, update, &recs[rcount]);
+               W_ERROR_NOT_OK_RETURN(werror);
+
+               for (i = 0; i < rcount; i++) {
+                       if (!dns_records_match(&recs[i], &recs[rcount])) {
+                               continue;
+                       }
+
+                       recs[i] = recs[rcount];
+
+                       werror = dns_replace_records(dns, mem_ctx, dn,
+                                                    needs_add, recs, rcount);
+                       W_ERROR_NOT_OK_RETURN(werror);
+
+                       return WERR_OK;
+               }
+
+               werror = dns_replace_records(dns, mem_ctx, dn,
+                                            needs_add, recs, rcount+1);
+               W_ERROR_NOT_OK_RETURN(werror);
+
+               return WERR_OK;
+       } else if (update->rr_class == DNS_QCLASS_ANY) {
+               if (update->rr_type == DNS_QTYPE_ALL) {
+                       if (dns_name_equal(update->name, zone->name)) {
+                               for (i = 0; i < rcount; i++) {
+
+                                       if (recs[i].wType == DNS_TYPE_SOA) {
+                                               continue;
+                                       }
+
+                                       if (recs[i].wType == DNS_TYPE_NS) {
+                                               continue;
+                                       }
+
+                                       ZERO_STRUCT(recs[i]);
+                               }
+
+                       } else {
+                               for (i = 0; i < rcount; i++) {
+                                       ZERO_STRUCT(recs[i]);
+                               }
+                       }
+
+               } else if (dns_name_equal(update->name, zone->name)) {
+
+                       if (update->rr_type == DNS_QTYPE_SOA) {
+                               return WERR_OK;
+                       }
+
+                       if (update->rr_type == DNS_QTYPE_NS) {
+                               return WERR_OK;
+                       }
+               }
+               for (i = 0; i < rcount; i++) {
+                       if (recs[i].wType == update->rr_type) {
+                               ZERO_STRUCT(recs[i]);
+                       }
+               }
+
+               werror = dns_replace_records(dns, mem_ctx, dn,
+                                            needs_add, recs, rcount);
+               W_ERROR_NOT_OK_RETURN(werror);
+
+               return WERR_OK;
+       } else if (update->rr_class == DNS_QCLASS_NONE) {
+               struct dnsp_DnssrvRpcRecord *del_rec;
+
+               if (update->rr_type == DNS_QTYPE_SOA) {
+                       return WERR_OK;
+               }
+               if (update->rr_type == DNS_QTYPE_NS) {
+                       bool found = false;
+                       struct dnsp_DnssrvRpcRecord *ns_rec = talloc(mem_ctx,
+                                               struct dnsp_DnssrvRpcRecord);
+                       W_ERROR_HAVE_NO_MEMORY(ns_rec);
+
+
+                       werror = dns_rr_to_dnsp(ns_rec, update, ns_rec);
+                       W_ERROR_NOT_OK_RETURN(werror);
+
+                       for (i = 0; i < rcount; i++) {
+                               if (dns_records_match(ns_rec, &recs[i])) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+                       if (found) {
+                               return WERR_OK;
+                       }
+               }
+
+               del_rec = talloc(mem_ctx, struct dnsp_DnssrvRpcRecord);
+               W_ERROR_HAVE_NO_MEMORY(del_rec);
+
+               werror = dns_rr_to_dnsp(del_rec, update, del_rec);
+               W_ERROR_NOT_OK_RETURN(werror);
+
+               for (i = 0; i < rcount; i++) {
+                       if (dns_records_match(del_rec, &recs[i])) {
+                               ZERO_STRUCT(recs[i]);
+                       }
+               }
+       }
+
+       return WERR_OK;
+}
+
+static WERROR handle_updates(struct dns_server *dns,
+                            TALLOC_CTX *mem_ctx,
+                            const struct dns_name_question *zone,
+                            const struct dns_res_rec *prereqs, uint16_t pcount,
+                            struct dns_res_rec *updates, uint16_t upd_count)
+{
+       struct ldb_dn *zone_dn = NULL;
+       WERROR werror = WERR_OK;
+       int ret;
+       uint16_t ri;
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+       werror = dns_name2dn(dns, tmp_ctx, zone->name, &zone_dn);
+       W_ERROR_NOT_OK_RETURN(werror);
+
+       ret = ldb_transaction_start(dns->samdb);
+       if (ret != LDB_SUCCESS) {
+               return DNS_ERR(SERVER_FAILURE);
+       }
+
+       werror = check_prerequisites(dns, tmp_ctx, zone, prereqs, pcount);
+       W_ERROR_NOT_OK_GOTO(werror, failed);
+
+       DEBUG(0, ("update count is %u\n", upd_count));
+
+       for (ri = 0; ri < upd_count; ri++) {
+               werror = handle_one_update(dns, tmp_ctx, zone,
+                                          &updates[ri]);
+               W_ERROR_NOT_OK_GOTO(werror, failed);
+       }
+
+       ldb_transaction_commit(dns->samdb);
+       TALLOC_FREE(tmp_ctx);
+       return WERR_OK;
+
+failed:
+       ldb_transaction_cancel(dns->samdb);
+       TALLOC_FREE(tmp_ctx);
+       return werror;
+
+}
+
 WERROR dns_server_process_update(struct dns_server *dns,
                                 TALLOC_CTX *mem_ctx,
                                 struct dns_name_packet *in,
 WERROR dns_server_process_update(struct dns_server *dns,
                                 TALLOC_CTX *mem_ctx,
                                 struct dns_name_packet *in,
@@ -410,5 +708,10 @@ WERROR dns_server_process_update(struct dns_server *dns,
        werror = update_prescan(in->questions, *updates, *update_count);
        W_ERROR_NOT_OK_RETURN(werror);
 
        werror = update_prescan(in->questions, *updates, *update_count);
        W_ERROR_NOT_OK_RETURN(werror);
 
+
+       werror = handle_updates(dns, mem_ctx, in->questions, *prereqs,
+                               *prereq_count, *updates, *update_count);
+       W_ERROR_NOT_OK_RETURN(werror);
+
        return werror;
 }
        return werror;
 }
index c0e7ff758377a4d5108ed93c8f346ba08c0392a5..4649e5ffcac7b72dcb5254f6c7eaf766d5f0e189 100644 (file)
@@ -209,6 +209,80 @@ WERROR dns_lookup_records(struct dns_server *dns,
        return WERR_OK;
 }
 
        return WERR_OK;
 }
 
+WERROR dns_replace_records(struct dns_server *dns,
+                          TALLOC_CTX *mem_ctx,
+                          struct ldb_dn *dn,
+                          bool needs_add,
+                          const struct dnsp_DnssrvRpcRecord *records,
+                          uint16_t rec_count)
+{
+       struct ldb_message_element *el;
+       uint16_t i;
+       int ret;
+       struct ldb_message *msg = NULL;
+
+       msg = ldb_msg_new(mem_ctx);
+       W_ERROR_HAVE_NO_MEMORY(msg);
+
+       msg->dn = dn;
+
+       ret = ldb_msg_add_empty(msg, "dnsRecord", LDB_FLAG_MOD_REPLACE, &el);
+       if (ret != LDB_SUCCESS) {
+               return DNS_ERR(SERVER_FAILURE);
+       }
+
+       el->values = talloc_zero_array(el, struct ldb_val, rec_count);
+       if (rec_count > 0) {
+               W_ERROR_HAVE_NO_MEMORY(el->values);
+       }
+
+       for (i = 0; i < rec_count; i++) {
+               static const struct dnsp_DnssrvRpcRecord zero;
+               struct ldb_val *v = &el->values[el->num_values];
+               enum ndr_err_code ndr_err;
+
+               if (memcmp(&records[i], &zero, sizeof(zero)) == 0) {
+                       continue;
+               }
+               ndr_err = ndr_push_struct_blob(v, el->values, &records[i],
+                               (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       DEBUG(0, ("Failed to grab dnsp_DnssrvRpcRecord\n"));
+                       return DNS_ERR(SERVER_FAILURE);
+               }
+               el->num_values++;
+       }
+
+
+       if (el->num_values == 0) {
+               if (needs_add) {
+                       return WERR_OK;
+               }
+               /* TODO: Delete object? */
+       }
+
+       if (needs_add) {
+               ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
+               if (ret != LDB_SUCCESS) {
+                       return DNS_ERR(SERVER_FAILURE);
+               }
+
+               ret = ldb_add(dns->samdb, msg);
+               if (ret != LDB_SUCCESS) {
+                       return DNS_ERR(SERVER_FAILURE);
+               }
+
+               return WERR_OK;
+       }
+
+       ret = ldb_modify(dns->samdb, msg);
+       if (ret != LDB_SUCCESS) {
+               return DNS_ERR(SERVER_FAILURE);
+       }
+
+       return WERR_OK;
+}
+
 WERROR dns_name2dn(struct dns_server *dns,
                   TALLOC_CTX *mem_ctx,
                   const char *name,
 WERROR dns_name2dn(struct dns_server *dns,
                   TALLOC_CTX *mem_ctx,
                   const char *name,