From 1c48b5a62f73234ed26bb20f0ab345ab61cda0ab Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Thu, 18 Feb 2010 07:49:04 -0500 Subject: Rename server/ directory to src/ Also update BUILD.txt --- server/resolv/async_resolv.c | 1062 ------------------------------------------ 1 file changed, 1062 deletions(-) delete mode 100644 server/resolv/async_resolv.c (limited to 'server/resolv/async_resolv.c') diff --git a/server/resolv/async_resolv.c b/server/resolv/async_resolv.c deleted file mode 100644 index 70d8d11e..00000000 --- a/server/resolv/async_resolv.c +++ /dev/null @@ -1,1062 +0,0 @@ -/* - SSSD - - Async resolver - - Authors: - Martin Nagy - Jakub Hrozek - - Copyright (C) Red Hat, Inc 2009 - - 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 . -*/ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "config.h" -#include "resolv/async_resolv.h" -#include "util/dlinklist.h" -#include "util/util.h" - -#ifndef HAVE_ARES_DATA -#define ares_parse_srv_reply(abuf, alen, srv_out) \ - _ares_parse_srv_reply(abuf, alen, srv_out) -#define ares_parse_txt_reply(abuf, alen, txt_out) \ - _ares_parse_txt_reply(abuf, alen, txt_out) -#define ares_free_data(dataptr) \ - _ares_free_data(dataptr) -#define ares_malloc_data(data) \ - _ares_malloc_data(data) -#endif /* HAVE_ARES_DATA */ - -struct fd_watch { - struct fd_watch *prev; - struct fd_watch *next; - - int fd; - struct resolv_ctx *ctx; - struct tevent_fd *fde; -}; - -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; - - /* List of file descriptors that are watched by tevent. */ - struct fd_watch *fds; - - /* Time in milliseconds before canceling a DNS request */ - int timeout; - - /* The timeout watcher periodically calls ares_process_fd() to check - * if our pending requests didn't timeout. */ - int pending_requests; - struct tevent_timer *timeout_watcher; -}; - -struct resolv_ctx *context_list; - -static int -return_code(int ares_code) -{ - switch (ares_code) { - case ARES_SUCCESS: - return EOK; - case ARES_ENOMEM: - return ENOMEM; - case ARES_EFILE: - default: - return EIO; - } -} - -const char * -resolv_strerror(int ares_code) -{ - return ares_strerror(ares_code); -} - -static int -fd_watch_destructor(struct fd_watch *f) -{ - DLIST_REMOVE(f->ctx->fds, f); - f->fd = -1; - - return 0; -} - -static void -fd_input_available(struct tevent_context *ev, struct tevent_fd *fde, - uint16_t flags, void *data) -{ - struct fd_watch *watch = talloc_get_type(data, struct fd_watch); - - if (watch->ctx->channel == NULL) { - DEBUG(1, ("Invalid ares channel - this is likely a bug\n")); - return; - } - - if (flags & TEVENT_FD_READ) { - ares_process_fd(watch->ctx->channel, watch->fd, ARES_SOCKET_BAD); - } - if (flags & TEVENT_FD_WRITE) { - ares_process_fd(watch->ctx->channel, ARES_SOCKET_BAD, watch->fd); - } -} - -static void -check_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te, - struct timeval current_time, void *private_data); - -static void -add_timeout_timer(struct tevent_context *ev, struct resolv_ctx *ctx) -{ - struct timeval tv = { 0 }; - struct timeval *tvp; - - tvp = ares_timeout(ctx->channel, NULL, &tv); - - if (tvp == NULL) { - tvp = &tv; - } - - /* Enforce a minimum of 1 second. */ - if (tvp->tv_sec < 1) { - tv = tevent_timeval_current_ofs(1, 0); - } else { - tv = tevent_timeval_current_ofs(tvp->tv_sec, tvp->tv_usec); - } - - ctx->timeout_watcher = tevent_add_timer(ev, ctx, tv, check_fd_timeouts, - ctx); - if (ctx->timeout_watcher == NULL) { - DEBUG(1, ("Out of memory\n")); - } -} - -static void -check_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te, - struct timeval current_time, void *private_data) -{ - struct resolv_ctx *ctx = talloc_get_type(private_data, struct resolv_ctx); - - DEBUG(9, ("Checking for DNS timeouts\n")); - - /* NULLify the timeout_watcher so we don't - * free it in the _done() function if it - * gets called. Now that we're already in - * the handler, tevent will take care of - * freeing it when it returns. - */ - ctx->timeout_watcher = NULL; - - ares_process_fd(ctx->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); - - if (ctx->pending_requests > 0) { - add_timeout_timer(ev, ctx); - } -} - -static void -schedule_timeout_watcher(struct tevent_context *ev, struct resolv_ctx *ctx) -{ - ctx->pending_requests++; - if (ctx->timeout_watcher) { - return; - } - - DEBUG(9, ("Scheduling DNS timeout watcher\n")); - add_timeout_timer(ev, ctx); -} - -static void -unschedule_timeout_watcher(struct resolv_ctx *ctx) -{ - if (ctx->pending_requests <= 0) { - DEBUG(1, ("Pending DNS requests mismatch\n")); - return; - } - - ctx->pending_requests--; - if (ctx->pending_requests == 0) { - DEBUG(9, ("Unscheduling DNS timeout watcher\n")); - talloc_zfree(ctx->timeout_watcher); - } -} - -static void fd_event_add(struct resolv_ctx *ctx, int s, int flags); -static void fd_event_close(struct resolv_ctx *ctx, int s); - -/* - * When ares is ready to read or write to a file descriptor, it will - * call this callback. If both read and write are 0, it means that ares - * will soon close the socket. We are mainly using this function to register - * new file descriptors with tevent. - */ -static void -fd_event(void *data, int s, int fd_read, int fd_write) -{ - struct resolv_ctx *ctx = talloc_get_type(data, struct resolv_ctx); - struct fd_watch *watch; - int flags; - - /* The socket is about to get closed. */ - if (fd_read == 0 && fd_write == 0) { - fd_event_close(ctx, s); - return; - } - - flags = fd_read ? TEVENT_FD_READ : 0; - flags |= fd_write ? TEVENT_FD_WRITE : 0; - - /* Are we already watching this file descriptor? */ - watch = ctx->fds; - while (watch) { - if (watch->fd == s) { - tevent_fd_set_flags(watch->fde, flags); - return; - } - watch = watch->next; - } - - fd_event_add(ctx, s, flags); -} - -static void -fd_event_add(struct resolv_ctx *ctx, int s, int flags) -{ - struct fd_watch *watch; - - /* The file descriptor is new, register it with tevent. */ - watch = talloc(ctx, struct fd_watch); - if (watch == NULL) { - DEBUG(1, ("Out of memory allocating fd_watch structure\n")); - return; - } - talloc_set_destructor(watch, fd_watch_destructor); - - watch->fd = s; - watch->ctx = ctx; - - watch->fde = tevent_add_fd(ctx->ev_ctx, watch, s, flags, - fd_input_available, watch); - if (watch->fde == NULL) { - DEBUG(1, ("tevent_add_fd() failed\n")); - talloc_free(watch); - return; - } - DLIST_ADD(ctx->fds, watch); -} - -static void -fd_event_close(struct resolv_ctx *ctx, int s) -{ - struct fd_watch *watch; - - /* Remove the socket from list */ - watch = ctx->fds; - while (watch) { - if (watch->fd == s) { - talloc_free(watch); - return; - } - watch = watch->next; - } -} - -static int -resolv_ctx_destructor(struct resolv_ctx *ctx) -{ - ares_channel channel; - - DLIST_REMOVE(context_list, ctx); - - if (ctx->channel == NULL) { - DEBUG(1, ("Ares channel already destroyed?\n")); - return -1; - } - - /* Set ctx->channel to NULL first, so that callbacks that get - * ARES_EDESTRUCTION won't retry. */ - channel = ctx->channel; - ctx->channel = NULL; - ares_destroy(channel); - - return 0; -} - -static int -recreate_ares_channel(struct resolv_ctx *ctx) -{ - int ret; - ares_channel new_channel; - ares_channel old_channel; - struct ares_options options; - - 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; - options.timeout = ctx->timeout * 1000; - options.tries = 1; - ret = ares_init_options(&new_channel, &options, - ARES_OPT_SOCK_STATE_CB | - ARES_OPT_TIMEOUTMS | - ARES_OPT_TRIES); - if (ret != ARES_SUCCESS) { - DEBUG(1, ("Failed to initialize ares channel: %s\n", - resolv_strerror(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, - int timeout, 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; - ctx->timeout = timeout; - - ret = recreate_ares_channel(ctx); - if (ret != EOK) { - goto done; - } - - DLIST_ADD(context_list, ctx); - talloc_set_destructor(ctx, resolv_ctx_destructor); - - *ctxp = ctx; - return EOK; - -done: - talloc_free(ctx); - 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) -{ - struct hostent *ret; - int len; - int i; - - ret = talloc_zero(mem_ctx, struct hostent); - if (ret == NULL) { - return NULL; - } - - if (src->h_name != NULL) { - ret->h_name = talloc_strdup(ret, src->h_name); - if (ret->h_name == NULL) { - goto fail; - } - } - if (src->h_aliases != NULL) { - for (len = 0; src->h_aliases[len] != NULL; len++); - ret->h_aliases = talloc_size(ret, sizeof(char *) * (len + 1)); - if (ret->h_aliases == NULL) { - goto fail; - } - for (i = 0; i < len; i++) { - ret->h_aliases[i] = talloc_strdup(ret->h_aliases, src->h_aliases[i]); - if (ret->h_aliases[i] == NULL) { - goto fail; - } - } - ret->h_aliases[len] = NULL; - } - ret->h_addrtype = src->h_addrtype; - ret->h_length = src->h_length; - if (src->h_addr_list != NULL) { - for (len = 0; src->h_addr_list[len] != NULL; len++); - ret->h_addr_list = talloc_size(ret, sizeof(char *) * (len + 1)); - if (ret->h_addr_list == NULL) { - goto fail; - } - for (i = 0; i < len; i++) { - ret->h_addr_list[i] = talloc_memdup(ret->h_addr_list, - src->h_addr_list[i], - ret->h_length); - if (ret->h_addr_list[i] == NULL) { - goto fail; - } - } - ret->h_addr_list[len] = NULL; - } - - return ret; - -fail: - talloc_free(ret); - return NULL; -} - -/******************************************************************* - * Get host by name. * - *******************************************************************/ - -struct gethostbyname_state { - struct resolv_ctx *resolv_ctx; - /* Part of the query. */ - const char *name; - int family; - /* These are returned by ares. The hostent struct will be freed - * when the user callback returns. */ - struct hostent *hostent; - int status; - int timeouts; - int retrying; -}; - -static void -ares_gethostbyname_wakeup(struct tevent_req *req); - -struct tevent_req * -resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct resolv_ctx *ctx, const char *name) -{ - struct tevent_req *req, *subreq; - struct gethostbyname_state *state; - struct timeval tv = { 0, 0 }; - - DEBUG(4, ("Trying to resolve A record of '%s'\n", name)); - - if (ctx->channel == NULL) { - DEBUG(1, ("Invalid ares channel - this is likely a bug\n")); - return NULL; - } - - req = tevent_req_create(mem_ctx, &state, struct gethostbyname_state); - if (req == NULL) - return NULL; - - state->resolv_ctx = ctx; - state->name = name; - state->family = AF_INET; - 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. - * This would not let our caller to set a callback for req. */ - subreq = tevent_wakeup_send(req, ev, tv); - if (subreq == NULL) { - DEBUG(1, ("Failed to add critical timer to run next operation!\n")); - talloc_zfree(req); - return NULL; - } - tevent_req_set_callback(subreq, ares_gethostbyname_wakeup, req); - schedule_timeout_watcher(ev, ctx); - - return req; -} - -static void -resolv_gethostbyname6_done(void *arg, int status, int timeouts, struct hostent *hostent); - -static void -resolv_gethostbyname_done(void *arg, int status, int timeouts, struct hostent *hostent) -{ - 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->resolv_ctx->channel != NULL) { - state->retrying = 1; - ares_gethostbyname(state->resolv_ctx->channel, state->name, - state->family, resolv_gethostbyname_done, req); - return; - } - - unschedule_timeout_watcher(state->resolv_ctx); - - if (hostent != NULL) { - state->hostent = resolv_copy_hostent(req, hostent); - if (state->hostent == NULL) { - tevent_req_error(req, ENOMEM); - return; - } - } else { - state->hostent = NULL; - } - state->status = status; - state->timeouts = timeouts; - - if (status != ARES_SUCCESS) { - if (status == ARES_ENOTFOUND || status == ARES_ENODATA) { - /* IPv4 failure. Try IPv6 */ - state->family = AF_INET6; - state->retrying = 0; - state->timeouts = 0; - DEBUG(4, ("Trying to resolve AAAA record of '%s'\n", - state->name)); - ares_gethostbyname(state->resolv_ctx->channel, state->name, - state->family, resolv_gethostbyname6_done, - req); - return; - } - - /* Any other error indicates a server error, - * so don't bother trying again - */ - tevent_req_error(req, return_code(status)); - } - else { - tevent_req_done(req); - } -} - -static void -resolv_gethostbyname6_done(void *arg, int status, int timeouts, struct hostent *hostent) -{ - 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_gethostbyname6_done, req); - return; - } - - if (hostent != NULL) { - state->hostent = resolv_copy_hostent(req, hostent); - if (state->hostent == NULL) { - tevent_req_error(req, ENOMEM); - return; - } - } else { - state->hostent = NULL; - } - state->status = status; - state->timeouts = timeouts; - - if (status != ARES_SUCCESS) { - tevent_req_error(req, return_code(status)); - } - else { - tevent_req_done(req); - } -} - -int -resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, - int *status, int *timeouts, - struct hostent **hostent) -{ - struct gethostbyname_state *state = tevent_req_data(req, struct gethostbyname_state); - - /* Fill in even in case of error as status contains the - * c-ares return code */ - if (status) { - *status = state->status; - } - if (timeouts) { - *timeouts = state->timeouts; - } - if (hostent) { - *hostent = talloc_steal(mem_ctx, state->hostent); - } - - TEVENT_REQ_RETURN_ON_ERROR(req); - - return EOK; -} - -static void -ares_gethostbyname_wakeup(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); - struct gethostbyname_state *state = tevent_req_data(req, - struct gethostbyname_state); - - if (!tevent_wakeup_recv(subreq)) { - return; - } - talloc_zfree(subreq); - - if (state->resolv_ctx->channel == NULL) { - DEBUG(1, ("Invalid ares channel - this is likely a bug\n")); - tevent_req_error(req, EIO); - return; - } - - ares_gethostbyname(state->resolv_ctx->channel, state->name, - state->family, resolv_gethostbyname_done, req); -} - -/* SRV and TXT parsing is not used anywhere in the code yet, so we disable it - * for now - */ -#ifdef BUILD_TXT_SRV - -/* - * A simple helper function that will take an array of struct ares_srv_reply that - * was allocated by malloc() in c-ares and copies it using talloc. The old one - * is freed and the talloc one is put into 'reply_list' instead. - */ -static int -rewrite_talloc_srv_reply(TALLOC_CTX *mem_ctx, struct ares_srv_reply **reply_list) -{ - struct ares_srv_reply *ptr = NULL; - struct ares_srv_reply *new_list = NULL; - struct ares_srv_reply *old_list = *reply_list; - - /* Nothing to do, but not an error */ - if (!old_list) { - return EOK; - } - - /* Copy the linked list */ - while (old_list) { - /* Special case for the first node */ - if (!new_list) { - new_list = talloc_zero(mem_ctx, struct ares_srv_reply); - if (new_list == NULL) { - ares_free_data(*reply_list); - return ENOMEM; - } - ptr = new_list; - } else { - ptr->next = talloc_zero(new_list, struct ares_srv_reply); - if (ptr->next == NULL) { - ares_free_data(*reply_list); - talloc_free(new_list); - return ENOMEM; - } - ptr = ptr->next; - } - - ptr->weight = old_list->weight; - ptr->priority = old_list->priority; - ptr->port = old_list->port; - ptr->host = talloc_strdup(ptr, old_list->host); - if (ptr->host == NULL) { - ares_free_data(*reply_list); - talloc_free(new_list); - return ENOMEM; - } - - old_list = old_list->next; - } - - /* Free the old one (uses malloc). */ - ares_free_data(*reply_list); - - /* And now put our own new_list in place. */ - *reply_list = new_list; - - return EOK; -} - -/******************************************************************* - * Get SRV record * - *******************************************************************/ - -struct getsrv_state { - struct resolv_ctx *resolv_ctx; - /* the SRV query - for example _ldap._tcp.example.com */ - const char *query; - - /* parsed data returned by ares */ - struct ares_srv_reply *reply_list; - int status; - int timeouts; - int retrying; -}; - -static void -ares_getsrv_wakeup(struct tevent_req *subreq); - -struct tevent_req * -resolv_getsrv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct resolv_ctx *ctx, const char *query) -{ - struct tevent_req *req, *subreq; - struct getsrv_state *state; - struct timeval tv = { 0, 0 }; - - DEBUG(4, ("Trying to resolve SRV record of '%s'\n", query)); - - if (ctx->channel == NULL) { - DEBUG(1, ("Invalid ares channel - this is likely a bug\n")); - return NULL; - } - - req = tevent_req_create(mem_ctx, &state, struct getsrv_state); - if (req == NULL) - return NULL; - - state->resolv_ctx = ctx; - state->query = query; - state->reply_list = NULL; - state->status = 0; - state->timeouts = 0; - state->retrying = 0; - - subreq = tevent_wakeup_send(req, ev, tv); - if (subreq == NULL) { - DEBUG(1, ("Failed to add critical timer to run next operation!\n")); - talloc_zfree(req); - return NULL; - } - tevent_req_set_callback(subreq, ares_getsrv_wakeup, req); - schedule_timeout_watcher(ev, ctx); - - return req; -} - -static void -resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int alen) -{ - struct tevent_req *req = talloc_get_type(arg, struct tevent_req); - struct getsrv_state *state = tevent_req_data(req, struct getsrv_state); - int ret; - struct ares_srv_reply *reply_list; - - if (state->retrying == 0 && status == ARES_EDESTRUCTION - && state->resolv_ctx->channel != NULL) { - state->retrying = 1; - ares_query(state->resolv_ctx->channel, state->query, - ns_c_in, ns_t_srv, resolv_getsrv_done, req); - return; - } - - unschedule_timeout_watcher(state->resolv_ctx); - - state->status = status; - state->timeouts = timeouts; - - if (status != ARES_SUCCESS) { - tevent_req_error(req, return_code(status)); - ret = return_code(status); - goto fail; - } - - ret = ares_parse_srv_reply(abuf, alen, &reply_list); - if (status != ARES_SUCCESS) { - DEBUG(2, ("SRV record parsing failed: %d: %s\n", ret, ares_strerror(ret))); - ret = return_code(ret); - goto fail; - } - ret = rewrite_talloc_srv_reply(req, &reply_list); - if (ret != EOK) { - goto fail; - } - state->reply_list = reply_list; - - tevent_req_done(req); - return; - -fail: - state->reply_list = NULL; - tevent_req_error(req, ret); -} - -int -resolv_getsrv_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status, - int *timeouts, struct ares_srv_reply **reply_list) -{ - struct getsrv_state *state = tevent_req_data(req, struct getsrv_state); - - if (status) - *status = state->status; - if (timeouts) - *timeouts = state->timeouts; - if (reply_list) - *reply_list = talloc_steal(mem_ctx, state->reply_list); - - TEVENT_REQ_RETURN_ON_ERROR(req); - - return EOK; -} - -static void -ares_getsrv_wakeup(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); - struct getsrv_state *state = tevent_req_data(req, - struct getsrv_state); - - if (!tevent_wakeup_recv(subreq)) { - return; - } - talloc_zfree(subreq); - - if (state->resolv_ctx->channel == NULL) { - DEBUG(1, ("Invalid ares channel - this is likely a bug\n")); - tevent_req_error(req, EIO); - return; - } - - ares_query(state->resolv_ctx->channel, state->query, - ns_c_in, ns_t_srv, resolv_getsrv_done, req); -} - -/* - * A simple helper function that will take an array of struct txt_reply that - * was allocated by malloc() in c-ares and copies it using talloc. The old one - * is freed and the talloc one is put into 'reply_list' instead. - */ -static int -rewrite_talloc_txt_reply(TALLOC_CTX *mem_ctx, struct ares_txt_reply **reply_list) -{ - struct ares_txt_reply *ptr = NULL; - struct ares_txt_reply *new_list = NULL; - struct ares_txt_reply *old_list = *reply_list; - - /* Nothing to do, but not an error */ - if (!old_list) { - return EOK; - } - - /* Copy the linked list */ - while (old_list) { - - /* Special case for the first node */ - if (!new_list) { - new_list = talloc_zero(mem_ctx, struct ares_txt_reply); - if (new_list == NULL) { - ares_free_data(*reply_list); - talloc_free(new_list); - return ENOMEM; - } - ptr = new_list; - } else { - ptr->next = talloc_zero(new_list, struct ares_txt_reply); - if (ptr->next == NULL) { - ares_free_data(*reply_list); - talloc_free(new_list); - return ENOMEM; - } - ptr = ptr->next; - } - - ptr->length = old_list->length; - ptr->txt = talloc_memdup(ptr, old_list->txt, - old_list->length); - if (ptr->txt == NULL) { - ares_free_data(*reply_list); - talloc_free(new_list); - return ENOMEM; - } - - old_list = old_list->next; - } - - ares_free_data(*reply_list); - - /* And now put our own new_list in place. */ - *reply_list = new_list; - - return EOK; -} - -/******************************************************************* - * Get TXT record * - *******************************************************************/ - -struct gettxt_state { - struct resolv_ctx *resolv_ctx; - /* the TXT query */ - const char *query; - - /* parsed data returned by ares */ - struct ares_txt_reply *reply_list; - int status; - int timeouts; - int retrying; -}; - -static void -ares_gettxt_wakeup(struct tevent_req *subreq); - -struct tevent_req * -resolv_gettxt_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct resolv_ctx *ctx, const char *query) -{ - struct tevent_req *req, *subreq; - struct gettxt_state *state; - struct timeval tv = { 0, 0 }; - - DEBUG(4, ("Trying to resolve TXT record of '%s'\n", query)); - - if (ctx->channel == NULL) { - DEBUG(1, ("Invalid ares channel - this is likely a bug\n")); - return NULL; - } - - req = tevent_req_create(mem_ctx, &state, struct gettxt_state); - if (req == NULL) - return NULL; - - state->resolv_ctx = ctx; - state->query = query; - state->reply_list = NULL; - state->status = 0; - state->timeouts = 0; - state->retrying = 0; - - subreq = tevent_wakeup_send(req, ev, tv); - if (subreq == NULL) { - DEBUG(1, ("Failed to add critical timer to run next operation!\n")); - talloc_zfree(req); - return NULL; - } - tevent_req_set_callback(subreq, ares_gettxt_wakeup, req); - schedule_timeout_watcher(ev, ctx); - - return req; -} - -static void -resolv_gettxt_done(void *arg, int status, int timeouts, unsigned char *abuf, int alen) -{ - struct tevent_req *req = talloc_get_type(arg, struct tevent_req); - struct gettxt_state *state = tevent_req_data(req, struct gettxt_state); - int ret; - struct ares_txt_reply *reply_list; - - if (state->retrying == 0 && status == ARES_EDESTRUCTION - && state->resolv_ctx->channel != NULL) { - state->retrying = 1; - ares_query(state->resolv_ctx->channel, state->query, - ns_c_in, ns_t_txt, resolv_gettxt_done, req); - return; - } - - unschedule_timeout_watcher(state->resolv_ctx); - - state->status = status; - state->timeouts = timeouts; - - if (status != ARES_SUCCESS) { - ret = return_code(status); - goto fail; - } - - ret = ares_parse_txt_reply(abuf, alen, &reply_list); - if (status != ARES_SUCCESS) { - DEBUG(2, ("TXT record parsing failed: %d: %s\n", ret, ares_strerror(ret))); - ret = return_code(ret); - goto fail; - } - ret = rewrite_talloc_txt_reply(req, &reply_list); - if (ret != EOK) { - goto fail; - } - state->reply_list = reply_list; - - tevent_req_done(req); - return; - -fail: - state->reply_list = NULL; - tevent_req_error(req, ret); -} - -int -resolv_gettxt_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status, - int *timeouts, struct ares_txt_reply **reply_list) -{ - struct gettxt_state *state = tevent_req_data(req, struct gettxt_state); - - if (status) - *status = state->status; - if (timeouts) - *timeouts = state->timeouts; - if (reply_list) - *reply_list = talloc_steal(mem_ctx, state->reply_list); - - TEVENT_REQ_RETURN_ON_ERROR(req); - - return EOK; -} - -static void -ares_gettxt_wakeup(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); - struct gettxt_state *state = tevent_req_data(req, - struct gettxt_state); - - if (!tevent_wakeup_recv(subreq)) { - return; - } - talloc_zfree(subreq); - - if (state->resolv_ctx->channel == NULL) { - DEBUG(1, ("Invalid ares channel - this is likely a bug\n")); - tevent_req_error(req, EIO); - return; - } - - ares_query(state->resolv_ctx->channel, state->query, - ns_c_in, ns_t_txt, resolv_gettxt_done, req); -} - -#endif -- cgit