summaryrefslogtreecommitdiffstats
path: root/server/resolv/async_resolv.c
diff options
context:
space:
mode:
Diffstat (limited to 'server/resolv/async_resolv.c')
-rw-r--r--server/resolv/async_resolv.c1062
1 files changed, 0 insertions, 1062 deletions
diff --git a/server/resolv/async_resolv.c b/server/resolv/async_resolv.c
deleted file mode 100644
index 70d8d11e4..000000000
--- a/server/resolv/async_resolv.c
+++ /dev/null
@@ -1,1062 +0,0 @@
-/*
- SSSD
-
- Async resolver
-
- Authors:
- Martin Nagy <mnagy@redhat.com>
- Jakub Hrozek <jhrozek@redhat.com>
-
- 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 <http://www.gnu.org/licenses/>.
-*/
-
-#include <sys/select.h>
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-
-#include <ares.h>
-#include <talloc.h>
-#include <tevent.h>
-
-#include <errno.h>
-#include <netdb.h>
-#include <stddef.h>
-#include <string.h>
-#include <unistd.h>
-
-#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