From 9825054362a6ca3ad6ea851236ba9c1b3a703aba Mon Sep 17 00:00:00 2001 From: Martin Nagy Date: Fri, 8 Jan 2010 22:32:25 +0100 Subject: Re-create c-ares channels if /etc/resolv.conf is modified Fixes: #378 --- server/providers/data_provider_be.c | 12 ++++- server/resolv/async_resolv.c | 95 ++++++++++++++++++++++++++++++++----- server/resolv/async_resolv.h | 2 + 3 files changed, 95 insertions(+), 14 deletions(-) (limited to 'server') diff --git a/server/providers/data_provider_be.c b/server/providers/data_provider_be.c index 05f3eaffa..73a7f508c 100644 --- a/server/providers/data_provider_be.c +++ b/server/providers/data_provider_be.c @@ -51,9 +51,12 @@ #define ACCESS_DENY "deny" #define NO_PROVIDER "none" +static int data_provider_res_init(DBusMessage *message, + struct sbus_connection *conn); + struct sbus_method monitor_be_methods[] = { { MON_CLI_METHOD_PING, monitor_common_pong }, - { MON_CLI_METHOD_RES_INIT, monitor_common_res_init }, + { MON_CLI_METHOD_RES_INIT, data_provider_res_init }, { NULL, NULL } }; @@ -1190,3 +1193,10 @@ int main(int argc, const char *argv[]) return 0; } +static int data_provider_res_init(DBusMessage *message, + struct sbus_connection *conn) +{ + resolv_reread_configuration(); + + return monitor_common_res_init(message, conn); +} diff --git a/server/resolv/async_resolv.c b/server/resolv/async_resolv.c index 14e9f0c8f..c350d6c36 100644 --- a/server/resolv/async_resolv.c +++ b/server/resolv/async_resolv.c @@ -62,6 +62,11 @@ struct fd_watch { }; struct resolv_ctx { + /* Contexts are linked so we can keep track of them and re-create + * the ares channels in all of them at once if we need to. */ + struct resolv_ctx *prev; + struct resolv_ctx *next; + struct tevent_context *ev_ctx; ares_channel channel; @@ -69,6 +74,8 @@ struct resolv_ctx { struct fd_watch *fds; }; +struct resolv_ctx *context_list; + static int return_code(int ares_code) { @@ -207,6 +214,8 @@ fd_event_close(struct resolv_ctx *ctx, int s) static int resolv_ctx_destructor(struct resolv_ctx *ctx) { + DLIST_REMOVE(context_list, ctx); + if (ctx->channel == NULL) { DEBUG(1, ("Ares channel already destroyed?\n")); return -1; @@ -218,34 +227,57 @@ resolv_ctx_destructor(struct resolv_ctx *ctx) return 0; } -int -resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, - struct resolv_ctx **ctxp) +static int +recreate_ares_channel(struct resolv_ctx *ctx) { int ret; - struct resolv_ctx *ctx; + ares_channel new_channel; + ares_channel old_channel; struct ares_options options; - ctx = talloc_zero(mem_ctx, struct resolv_ctx); - if (ctx == NULL) - return ENOMEM; - - ctx->ev_ctx = ev_ctx; - + DEBUG(4, ("Initializing new c-ares channel\n")); /* FIXME: the options would contain * the nameservers to contact, the domains * to search, timeout... => get from confdb */ options.sock_state_cb = fd_event; options.sock_state_cb_data = ctx; - ret = ares_init_options(&ctx->channel, &options, ARES_OPT_SOCK_STATE_CB); + ret = ares_init_options(&new_channel, &options, ARES_OPT_SOCK_STATE_CB); if (ret != ARES_SUCCESS) { DEBUG(1, ("Failed to initialize ares channel: %s\n", resolv_strerror(ret))); - ret = return_code(ret); + return return_code(ret); + } + + old_channel = ctx->channel; + ctx->channel = new_channel; + if (old_channel != NULL) { + DEBUG(4, ("Destroying the old c-ares channel\n")); + ares_destroy(old_channel); + } + + return EOK; +} + +int +resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, + struct resolv_ctx **ctxp) +{ + int ret; + struct resolv_ctx *ctx; + + ctx = talloc_zero(mem_ctx, struct resolv_ctx); + if (ctx == NULL) + return ENOMEM; + + ctx->ev_ctx = ev_ctx; + + ret = recreate_ares_channel(ctx); + if (ret != EOK) { goto done; } + DLIST_ADD(context_list, ctx); talloc_set_destructor(ctx, resolv_ctx_destructor); *ctxp = ctx; @@ -256,6 +288,17 @@ done: return ret; } +void +resolv_reread_configuration(void) +{ + struct resolv_ctx *ctx; + + DEBUG(4, ("Recreating all c-ares channels\n")); + DLIST_FOR_EACH(ctx, context_list) { + recreate_ares_channel(ctx); + } +} + struct hostent * resolv_copy_hostent(TALLOC_CTX *mem_ctx, struct hostent *src) { @@ -328,6 +371,7 @@ struct gethostbyname_state { struct hostent *hostent; int status; int timeouts; + int retrying; }; static void @@ -358,6 +402,7 @@ resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, state->hostent = NULL; state->status = 0; state->timeouts = 0; + state->retrying = 0; /* We need to have a wrapper around ares_gethostbyname(), because * ares_gethostbyname() can in some cases call it's callback immediately. @@ -379,6 +424,13 @@ resolv_gethostbyname_done(void *arg, int status, int timeouts, struct hostent *h struct tevent_req *req = talloc_get_type(arg, struct tevent_req); struct gethostbyname_state *state = tevent_req_data(req, struct gethostbyname_state); + if (state->retrying == 0 && status == ARES_EDESTRUCTION) { + state->retrying = 1; + ares_gethostbyname(state->resolv_ctx->channel, state->name, + state->family, resolv_gethostbyname_done, req); + return; + } + if (hostent != NULL) { state->hostent = resolv_copy_hostent(req, hostent); if (state->hostent == NULL) { @@ -521,6 +573,7 @@ struct getsrv_state { struct ares_srv_reply *reply_list; int status; int timeouts; + int retrying; }; static void @@ -550,6 +603,7 @@ resolv_getsrv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, state->reply_list = NULL; state->status = 0; state->timeouts = 0; + state->retrying = 0; subreq = tevent_wakeup_send(req, ev, tv); if (subreq == NULL) { @@ -570,6 +624,13 @@ resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int int ret; struct ares_srv_reply *reply_list; + if (state->retrying == 0 && status == ARES_EDESTRUCTION) { + state->retrying = 1; + ares_query(state->resolv_ctx->channel, state->query, + ns_c_in, ns_t_srv, resolv_getsrv_done, req); + return; + } + state->status = status; state->timeouts = timeouts; @@ -712,6 +773,7 @@ struct gettxt_state { struct ares_txt_reply *reply_list; int status; int timeouts; + int retrying; }; static void @@ -741,7 +803,7 @@ resolv_gettxt_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, state->reply_list = NULL; state->status = 0; state->timeouts = 0; - + state->retrying = 0; subreq = tevent_wakeup_send(req, ev, tv); if (subreq == NULL) { @@ -762,6 +824,13 @@ resolv_gettxt_done(void *arg, int status, int timeouts, unsigned char *abuf, int int ret; struct ares_txt_reply *reply_list; + if (state->retrying == 0 && status == ARES_EDESTRUCTION) { + state->retrying = 1; + ares_query(state->resolv_ctx->channel, state->query, + ns_c_in, ns_t_txt, resolv_gettxt_done, req); + return; + } + state->status = status; state->timeouts = timeouts; diff --git a/server/resolv/async_resolv.h b/server/resolv/async_resolv.h index dab2fdfc4..e0515383b 100644 --- a/server/resolv/async_resolv.h +++ b/server/resolv/async_resolv.h @@ -49,6 +49,8 @@ struct resolv_ctx; int resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, struct resolv_ctx **ctxp); +void resolv_reread_configuration(void); + const char *resolv_strerror(int ares_code); struct hostent *resolv_copy_hostent(TALLOC_CTX *mem_ctx, -- cgit