From 4c2381898351468dc33eb97ec32dea436f31200b Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Thu, 11 Dec 2014 12:19:08 +0100 Subject: [PATCH] s4-dns: Reload zones from dsdb when zones are added or deleted through RPC Setup a RPC management call on the internal DNS server triggered by the RPC server when zones are added or deleted by ZoneCreate or DeleteZoneFromDs calls. Signed-off-by: Samuel Cabrero --- source4/dns_server/dns_server.c | 133 +++++++++++++++++------- source4/librpc/idl/irpc.idl | 11 ++ source4/rpc_server/dnsserver/dcerpc_dnsserver.c | 57 ++++++++++ 3 files changed, 163 insertions(+), 38 deletions(-) diff --git a/source4/dns_server/dns_server.c b/source4/dns_server/dns_server.c index f1a4c4c..f46efd3 100644 --- a/source4/dns_server/dns_server.c +++ b/source4/dns_server/dns_server.c @@ -45,6 +45,8 @@ #include "lib/util/tevent_werror.h" #include "auth/auth.h" #include "auth/credentials/credentials.h" +#include "librpc/gen_ndr/ndr_irpc.h" +#include "lib/messaging/irpc.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_DNS @@ -761,16 +763,89 @@ static struct dns_server_tkey_store *tkey_store_init(TALLOC_CTX *mem_ctx, return buffer; } +static NTSTATUS dns_server_reload_zones(struct dns_server *dns) +{ + int ret; + static const char * const attrs[] = { "name", NULL}; + struct ldb_result *res; + int i; + struct dns_server_zone *new_list = NULL; + struct dns_server_zone *old_list = NULL; + struct dns_server_zone *z; + + // TODO: this search does not work against windows + ret = dsdb_search(dns->samdb, dns, &res, NULL, LDB_SCOPE_SUBTREE, + attrs, DSDB_SEARCH_SEARCH_ALL_PARTITIONS, "(objectClass=dnsZone)"); + if (ret != LDB_SUCCESS) { + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + TYPESAFE_QSORT(res->msgs, res->count, dns_server_sort_zones); + + for (i=0; i < res->count; i++) { + struct dns_server_zone *z; + + z = talloc_zero(dns, struct dns_server_zone); + if (z == NULL) { + return NT_STATUS_NO_MEMORY; + } + + z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL); + z->dn = talloc_move(z, &res->msgs[i]->dn); + /* + * Ignore the RootDNSServers zone and zones that we don't support yet + * RootDNSServers should never be returned (Windows DNS server don't) + * ..TrustAnchors should never be returned as is, (Windows returns + * TrustAnchors) and for the moment we don't support DNSSEC so we'd better + * not return this zone. + */ + if ((strcmp(z->name, "RootDNSServers") == 0) || + (strcmp(z->name, "..TrustAnchors") == 0)) + { + DEBUG(10, ("Ignoring zone %s\n", z->name)); + talloc_free(z); + continue; + } + DLIST_ADD_END(new_list, z, NULL); + } + + old_list = dns->zones; + dns->zones = new_list; + while ((z = DLIST_TAIL(old_list)) != NULL) { + DLIST_REMOVE(old_list, z); + talloc_free(z); + } + + return NT_STATUS_OK; +} + +/** + * Called when the internal DNS server should reload the zones from DB, for + * example, when zones are added or deleted through RPC. + */ +static NTSTATUS dns_reload_zones(struct irpc_message *msg, + struct dns_server_reload_zones *r) +{ + struct dns_server *dns; + + dns = talloc_get_type(msg->private_data, struct dns_server); + if (dns == NULL) { + r->out.result = NT_STATUS_INTERNAL_ERROR; + return NT_STATUS_INTERNAL_ERROR; + } + + r->out.result = dns_server_reload_zones(dns); + + return NT_STATUS_OK; +} + static void dns_task_init(struct task_server *task) { struct dns_server *dns; NTSTATUS status; struct interface *ifaces = NULL; int ret; - struct ldb_result *res; - static const char * const attrs[] = { "name", NULL}; static const char * const attrs_none[] = { NULL}; - unsigned int i; struct ldb_message *dns_acc; char *hostname_lower; char *dns_spn; @@ -866,48 +941,30 @@ static void dns_task_init(struct task_server *task) return; } - // TODO: this search does not work against windows - ret = dsdb_search(dns->samdb, dns, &res, NULL, LDB_SCOPE_SUBTREE, - attrs, DSDB_SEARCH_SEARCH_ALL_PARTITIONS, "(objectClass=dnsZone)"); - if (ret != LDB_SUCCESS) { - task_server_terminate(task, - "dns: failed to look up root DNS zones", - true); + status = dns_server_reload_zones(dns); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(task, "dns: failed to load DNS zones", true); return; } - TYPESAFE_QSORT(res->msgs, res->count, dns_server_sort_zones); - - for (i=0; i < res->count; i++) { - struct dns_server_zone *z; - - z = talloc_zero(dns, struct dns_server_zone); - if (z == NULL) { - task_server_terminate(task, "dns failed to allocate memory", true); - } + status = dns_startup_interfaces(dns, ifaces); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(task, "dns failed to setup interfaces", true); + return; + } - z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL); - z->dn = talloc_move(z, &res->msgs[i]->dn); - /* - * Ignore the RootDNSServers zone and zones that we don't support yet - * RootDNSServers should never be returned (Windows DNS server don't) - * ..TrustAnchors should never be returned as is, (Windows returns - * TrustAnchors) and for the moment we don't support DNSSEC so we'd better - * not return this zone. - */ - if ((strcmp(z->name, "RootDNSServers") == 0) || - (strcmp(z->name, "..TrustAnchors") == 0)) - { - DEBUG(10, ("Ignoring zone %s\n", z->name)); - talloc_free(z); - continue; - } - DLIST_ADD_END(dns->zones, z, NULL); + /* Setup the IRPC interface and register handlers */ + status = irpc_add_name(task->msg_ctx, "dns"); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(task, "dns: failed to register IRPC name", true); + return; } - status = dns_startup_interfaces(dns, ifaces); + status = IRPC_REGISTER(task->msg_ctx, irpc, DNS_SERVER_RELOAD_ZONES, + dns_reload_zones, dns); if (!NT_STATUS_IS_OK(status)) { - task_server_terminate(task, "dns failed to setup interfaces", true); + task_server_terminate(task, "dns: failed to setup reload " + "handler", true); return; } } diff --git a/source4/librpc/idl/irpc.idl b/source4/librpc/idl/irpc.idl index 6a55eef..ce4ae25 100644 --- a/source4/librpc/idl/irpc.idl +++ b/source4/librpc/idl/irpc.idl @@ -207,4 +207,15 @@ import "misc.idl", "security.idl", "nbt.idl", "netlogon.idl", "server_id.idl"; [in] uint32 dns_ttl, [in,out,ref] NL_DNS_NAME_INFO_ARRAY *dns_names ); + + /****************************************************** + * Management calls for the dns server + ******************************************************/ + /** + * Force internal DNS server to reload the zones. + * + * Called when zones are added or deleted through RPC + * server. + */ + NTSTATUS dns_server_reload_zones(); } diff --git a/source4/rpc_server/dnsserver/dcerpc_dnsserver.c b/source4/rpc_server/dnsserver/dcerpc_dnsserver.c index be31500..0ce62b1 100644 --- a/source4/rpc_server/dnsserver/dcerpc_dnsserver.c +++ b/source4/rpc_server/dnsserver/dcerpc_dnsserver.c @@ -27,6 +27,9 @@ #include "librpc/gen_ndr/ndr_dnsserver.h" #include "dnsserver.h" #include "lib/ldb/include/ldb_private.h" +#include "librpc/gen_ndr/ndr_irpc.h" +#include "lib/messaging/irpc.h" +#include "librpc/gen_ndr/ndr_irpc_c.h" struct dnsserver_state { struct loadparm_context *lp_ctx; @@ -35,10 +38,59 @@ struct dnsserver_state { struct dnsserver_zone *zones; int zones_count; struct dnsserver_serverinfo *serverinfo; + struct imessaging_context *msg_ctx; + struct tevent_context *event_ctx; }; /* Utility functions */ +struct dnsserver_notify_dns_server_state { + struct dns_server_reload_zones r; +}; + +static void dnsserver_notify_dns_server_done(struct tevent_req *subreq); + +/** + * Sends a notification to the dns server to reload zones from database + */ +static void dnsserver_notify_dns_server(struct dnsserver_state *dsstate) +{ + struct dnsserver_notify_dns_server_state *state; + struct dcerpc_binding_handle *irpc_handle; + struct tevent_req *subreq; + + state = talloc_zero(dsstate, struct dnsserver_notify_dns_server_state); + if (state == NULL) { + return; + } + + irpc_handle = irpc_binding_handle_by_name(state, dsstate->msg_ctx, + "dns", &ndr_table_irpc); + if (irpc_handle == NULL) { + /* DNS server not running */ + talloc_free(state); + return; + } + + subreq = dcerpc_dns_server_reload_zones_r_send(state, dsstate->event_ctx, + irpc_handle, &state->r); + if (subreq == NULL) { + talloc_free(state); + return; + } + tevent_req_set_callback(subreq, dnsserver_notify_dns_server_done, state); +} + +static void dnsserver_notify_dns_server_done(struct tevent_req *subreq) +{ + struct dnsserver_notify_dns_server_state *state; + + state = tevent_req_callback_data(subreq, struct dnsserver_notify_dns_server_state); + + dcerpc_dns_server_reload_zones_r_recv(subreq, state); + talloc_free(subreq); + talloc_free(state); +} static void dnsserver_reload_zones(struct dnsserver_state *dsstate) { @@ -90,6 +142,9 @@ static void dnsserver_reload_zones(struct dnsserver_state *dsstate) } dsstate->zones = new_list; + + /* Send a notification to the dns server to reload zones */ + dnsserver_notify_dns_server(dsstate); } @@ -110,6 +165,8 @@ static struct dnsserver_state *dnsserver_connect(struct dcesrv_call_state *dce_c } dsstate->lp_ctx = dce_call->conn->dce_ctx->lp_ctx; + dsstate->msg_ctx = dce_call->conn->msg_ctx; + dsstate->event_ctx = dce_call->conn->event_ctx; /* FIXME: create correct auth_session_info for connecting user */ dsstate->samdb = samdb_connect(dsstate, dce_call->event_ctx, dsstate->lp_ctx, -- 1.9.1