diff options
-rw-r--r-- | source4/dns_server/dns_server.h | 6 | ||||
-rw-r--r-- | source4/dns_server/dns_update.c | 303 | ||||
-rw-r--r-- | source4/dns_server/dns_utils.c | 74 |
3 files changed, 383 insertions, 0 deletions
diff --git a/source4/dns_server/dns_server.h b/source4/dns_server/dns_server.h index 8570281baa..53d6306318 100644 --- a/source4/dns_server/dns_server.h +++ b/source4/dns_server/dns_server.h @@ -64,6 +64,12 @@ WERROR dns_lookup_records(struct dns_server *dns, 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, diff --git a/source4/dns_server/dns_update.c b/source4/dns_server/dns_update.c index 397384421c..64b6181d7e 100644 --- a/source4/dns_server/dns_update.c +++ b/source4/dns_server/dns_update.c @@ -344,6 +344,304 @@ done: 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, @@ -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 = handle_updates(dns, mem_ctx, in->questions, *prereqs, + *prereq_count, *updates, *update_count); + W_ERROR_NOT_OK_RETURN(werror); + return werror; } diff --git a/source4/dns_server/dns_utils.c b/source4/dns_server/dns_utils.c index c0e7ff7583..4649e5ffca 100644 --- a/source4/dns_server/dns_utils.c +++ b/source4/dns_server/dns_utils.c @@ -209,6 +209,80 @@ WERROR dns_lookup_records(struct dns_server *dns, 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, |