diff options
Diffstat (limited to 'source4/rpc_server/dnsserver/dnsdb.c')
-rw-r--r-- | source4/rpc_server/dnsserver/dnsdb.c | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/source4/rpc_server/dnsserver/dnsdb.c b/source4/rpc_server/dnsserver/dnsdb.c new file mode 100644 index 0000000000..ac881905ab --- /dev/null +++ b/source4/rpc_server/dnsserver/dnsdb.c @@ -0,0 +1,418 @@ +/* + Unix SMB/CIFS implementation. + + DNS Server + + Copyright (C) Amitay Isaacs 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "dnsserver.h" +#include "lib/util/dlinklist.h" +#include "librpc/gen_ndr/ndr_dnsp.h" + +struct dnsserver_zone *dnsserver_db_enumerate_zones(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + bool is_forest) +{ + TALLOC_CTX *tmp_ctx; + const char *domain_prefix = "DC=DomainDnsZones"; + const char *forest_prefix = "DC=ForestDnsZones"; + const char *prefix; + const char * const attrs[] = {"name", NULL}; + struct ldb_dn *dn_base, *partition_dn, *dn; + struct ldb_result *res; + struct dnsserver_zone *zones, *z; + int i, ret; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return NULL; + } + + if (is_forest) { + dn_base = ldb_get_root_basedn(samdb); + } else { + dn_base = ldb_get_default_basedn(samdb); + } + + partition_dn = ldb_dn_copy(tmp_ctx, dn_base); + if (partition_dn == NULL) { + goto failed; + } + + if (is_forest) { + prefix = forest_prefix; + } else { + prefix = domain_prefix; + } + + if (!ldb_dn_add_child_fmt(partition_dn, "%s", prefix)) { + goto failed; + } + + dn = ldb_dn_copy(tmp_ctx, partition_dn); + if (dn == NULL) { + goto failed; + } + if (!ldb_dn_add_child_fmt(dn, "CN=MicrosoftDNS")) { + goto failed; + } + + ret = ldb_search(samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE, + attrs, "(&(objectClass=dnsZone)(!(name=RootDNSServers)))"); + if (ret != LDB_SUCCESS) { + DEBUG(0, ("dnsserver: Failed to find DNS Zones in %s\n", + ldb_dn_get_linearized(dn))); + goto failed; + } + + zones = NULL; + for(i=0; i<res->count; i++) { + z = talloc_zero(mem_ctx, struct dnsserver_zone); + if (z == NULL) { + goto failed; + } + + z->name = talloc_strdup(z, + ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL)); + DEBUG(2, ("dnsserver: Found DNS zone %s\n", z->name)); + z->zone_dn = talloc_steal(z, res->msgs[i]->dn); + z->partition_dn = talloc_steal(z, partition_dn); + + DLIST_ADD_END(zones, z, NULL); + } + + return zones; + +failed: + talloc_free(tmp_ctx); + return NULL; +} + + +static WERROR dnsserver_db_do_add_rec(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct ldb_dn *dn, + struct dnsp_DnssrvRpcRecord *rec) +{ + struct ldb_message *msg; + struct ldb_val v; + int ret; + enum ndr_err_code ndr_err; + + msg = ldb_msg_new(mem_ctx); + W_ERROR_HAVE_NO_MEMORY(msg); + + msg->dn = dn; + ret = ldb_msg_add_string(msg, "objectClass", "dnsNode"); + if (ret != LDB_SUCCESS) { + return WERR_NOMEM; + } + + if (rec) { + ndr_err = ndr_push_struct_blob(&v, mem_ctx, rec, + (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_GENERAL_FAILURE; + } + + ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL); + if (ret != LDB_SUCCESS) { + return WERR_NOMEM; + } + } + + ret = ldb_add(samdb, msg); + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + return WERR_OK; +} + + +WERROR dnsserver_db_add_empty_node(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_zone *z, + const char *name) +{ + const char * const attrs[] = { "name", NULL }; + struct ldb_result *res; + struct ldb_dn *dn; + int ret; + + ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_BASE, attrs, + "(&(objectClass=dnsNode)(name=%s))", name); + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + if (res->count > 0) { + talloc_free(res); + return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS; + } + + dn = ldb_dn_copy(mem_ctx, z->zone_dn); + W_ERROR_HAVE_NO_MEMORY(dn); + + if (!ldb_dn_add_child_fmt(dn, "DC=%s", name)) { + return WERR_NOMEM; + } + + return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, NULL); +} + + +WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_zone *z, + const char *name, + struct DNS_RPC_RECORD *add_record) +{ + const char * const attrs[] = { "dnsRecord", NULL }; + struct ldb_result *res; + struct dnsp_DnssrvRpcRecord *rec; + struct ldb_message_element *el; + struct ldb_dn *dn; + enum ndr_err_code ndr_err; + NTTIME t; + int ret, i; + + rec = dns_to_dnsp_copy(mem_ctx, add_record); + W_ERROR_HAVE_NO_MEMORY(rec); + + unix_to_nt_time(&t, time(NULL)); + t /= 10*1000*1000; + + rec->dwSerial = 0; /* FIXME: put soa serial value */ + rec->dwTimeStamp = t; + + ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(name=%s))", name); + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + if (res->count == 0) { + dn = dnsserver_name_to_dn(mem_ctx, z, name); + W_ERROR_HAVE_NO_MEMORY(dn); + + return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, rec); + } + + el = ldb_msg_find_element(res->msgs[0], "dnsRecord"); + if (el == NULL) { + ret = ldb_msg_add_empty(res->msgs[0], "dnsRecord", 0, &el); + if (ret != LDB_SUCCESS) { + return WERR_NOMEM; + } + } + + for (i=0; i<el->num_values; i++) { + struct dnsp_DnssrvRpcRecord rec2; + + ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_GENERAL_FAILURE; + } + + if (dns_record_match(rec, &rec2)) { + break; + } + } + if (i < el->num_values) { + return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS; + } + if (i == el->num_values) { + /* adding a new value */ + el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1); + W_ERROR_HAVE_NO_MEMORY(el->values); + el->num_values++; + } + + ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, rec, + (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_GENERAL_FAILURE; + } + + el->flags = LDB_FLAG_MOD_REPLACE; + ret = ldb_modify(samdb, res->msgs[0]); + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + return WERR_OK; +} + +WERROR dnsserver_db_update_record(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_zone *z, + const char *name, + struct DNS_RPC_RECORD *add_record, + struct DNS_RPC_RECORD *del_record) +{ + const char * const attrs[] = { "dnsRecord", NULL }; + struct ldb_result *res; + struct dnsp_DnssrvRpcRecord *arec, *drec; + struct ldb_message_element *el; + enum ndr_err_code ndr_err; + NTTIME t; + int ret, i; + + arec = dns_to_dnsp_copy(mem_ctx, add_record); + W_ERROR_HAVE_NO_MEMORY(arec); + + drec = dns_to_dnsp_copy(mem_ctx, del_record); + W_ERROR_HAVE_NO_MEMORY(drec); + + unix_to_nt_time(&t, time(NULL)); + t /= 10*1000*1000; + + arec->dwSerial = 0; /* FIXME: put soa serial value */ + arec->dwTimeStamp = t; + + ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(name=%s))", name); + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + if (res->count == 0) { + return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST; + } + + el = ldb_msg_find_element(res->msgs[0], "dnsRecord"); + if (el == NULL || el->num_values == 0) { + return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST; + } + + for (i=0; i<el->num_values; i++) { + struct dnsp_DnssrvRpcRecord rec2; + + ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_GENERAL_FAILURE; + } + + if (dns_record_match(arec, &rec2)) { + break; + } + } + if (i < el->num_values) { + return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS; + } + + + for (i=0; i<el->num_values; i++) { + struct dnsp_DnssrvRpcRecord rec2; + + ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_GENERAL_FAILURE; + } + + if (dns_record_match(drec, &rec2)) { + break; + } + } + if (i == el->num_values) { + return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST; + } + + ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, arec, + (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_GENERAL_FAILURE; + } + + el->flags = LDB_FLAG_MOD_REPLACE; + ret = ldb_modify(samdb, res->msgs[0]); + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + return WERR_OK; +} + +WERROR dnsserver_db_delete_record(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_zone *z, + const char *name, + struct DNS_RPC_RECORD *del_record) +{ + const char * const attrs[] = { "dnsRecord", NULL }; + struct ldb_result *res; + struct dnsp_DnssrvRpcRecord *rec; + struct ldb_message_element *el; + enum ndr_err_code ndr_err; + int ret, i; + + rec = dns_to_dnsp_copy(mem_ctx, del_record); + W_ERROR_HAVE_NO_MEMORY(rec); + + ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(name=%s))", name); + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + if (res->count == 0) { + return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST; + } + + el = ldb_msg_find_element(res->msgs[0], "dnsRecord"); + if (el == NULL || el->num_values == 0) { + return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST; + } + + for (i=0; i<el->num_values; i++) { + struct dnsp_DnssrvRpcRecord rec2; + + ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_GENERAL_FAILURE; + } + + if (dns_record_match(rec, &rec2)) { + break; + } + } + if (i == el->num_values) { + return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST; + } + if (i < el->num_values-1) { + memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i)); + } + el->num_values--; + + if (el->num_values == 0) { + ret = ldb_delete(samdb, res->msgs[0]->dn); + } else { + el->flags = LDB_FLAG_MOD_REPLACE; + ret = ldb_modify(samdb, res->msgs[0]); + } + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + return WERR_OK; +} |