async_resolv.c revision 7aaf9138ccf7ab73883f28cfafd6e508d29ffa67
97018cf5fa25b494adffd7e9b4e87320dae6bf47Christian Maeder Async resolver
967e5f3c25249c779575864692935627004d3f9eChristian Maeder Martin Nagy <mnagy@redhat.com>
89054b2b95a3f92e78324dc852f3d34704e2ca49Christian Maeder Jakub Hrozek <jhrozek@redhat.com>
717686b54b9650402e2ebfbaadf433eab8ba5171Christian Maeder Copyright (C) Red Hat, Inc 2009
967e5f3c25249c779575864692935627004d3f9eChristian Maeder This program is free software; you can redistribute it and/or modify
967e5f3c25249c779575864692935627004d3f9eChristian Maeder it under the terms of the GNU General Public License as published by
967e5f3c25249c779575864692935627004d3f9eChristian Maeder the Free Software Foundation; either version 3 of the License, or
967e5f3c25249c779575864692935627004d3f9eChristian Maeder (at your option) any later version.
fd896e2068ad7e50aed66ac18c3720ea7ff2619fChristian Maeder This program is distributed in the hope that it will be useful,
650bafe7709533bc5f82bb9daf8fa06f431cd963Christian Maeder but WITHOUT ANY WARRANTY; without even the implied warranty of
9cb4aa4ea6685489a38f9b609f5dbe5d37f25bc7Christian Maeder MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7221c71b38c871ce66eee4537cb681d468308dfbChristian Maeder GNU General Public License for more details.
8b9fda012e5ee53b7b2320c0638896a0ff6e99f3Christian Maeder You should have received a copy of the GNU General Public License
e1839fb37a3a2ccd457464cb0dcc5efd466dbe22Christian Maeder along with this program. If not, see <http://www.gnu.org/licenses/>.
6e39bfd041946fce4982ac89834be73fd1bfb39aChristian Maeder#define ares_parse_srv_reply(abuf, alen, srv_out) \
62ecb1e7f8fd9573eea8369657de12c7bf9f4f25Christian Maeder#define ares_parse_txt_reply(abuf, alen, txt_out) \
2dfc7b04f2db681992ca04175f2beb0f127c9844Christian Maeder#endif /* HAVE_ARES_DATA */
717686b54b9650402e2ebfbaadf433eab8ba5171Christian Maeder#define DNS__16BIT(p) (((p)[0] << 8) | (p)[1])
717686b54b9650402e2ebfbaadf433eab8ba5171Christian Maeder#define DNS_HEADER_ANCOUNT(h) DNS__16BIT((h) + 6)
717686b54b9650402e2ebfbaadf433eab8ba5171Christian Maederenum host_database default_host_dbs[] = { DB_FILES, DB_DNS, DB_SENTINEL };
2dfc7b04f2db681992ca04175f2beb0f127c9844Christian Maeder /* List of file descriptors that are watched by tevent. */
4fb19f237193a3bd6778f8aee3b6dd8da5856665Christian Maeder /* Time in milliseconds before canceling a DNS request */
c66a930944d9e4d64a8f0f38c748fdad0831ff87Christian Maeder /* The timeout watcher periodically calls ares_process_fd() to check
2dfc7b04f2db681992ca04175f2beb0f127c9844Christian Maeder * if our pending requests didn't timeout. */
2dfc7b04f2db681992ca04175f2beb0f127c9844Christian Maederfd_input_available(struct tevent_context *ev, struct tevent_fd *fde,
2dfc7b04f2db681992ca04175f2beb0f127c9844Christian Maeder struct fd_watch *watch = talloc_get_type(data, struct fd_watch);
2dfc7b04f2db681992ca04175f2beb0f127c9844Christian Maeder "Invalid ares channel - this is likely a bug\n");
967e5f3c25249c779575864692935627004d3f9eChristian Maeder ares_process_fd(watch->ctx->channel, watch->fd, ARES_SOCKET_BAD);
deb7bff126ec547bd812d0c8683ad6e785a45abbChristian Maeder ares_process_fd(watch->ctx->channel, ARES_SOCKET_BAD, watch->fd);
deb7bff126ec547bd812d0c8683ad6e785a45abbChristian Maedercheck_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te,
deb7bff126ec547bd812d0c8683ad6e785a45abbChristian Maeder struct timeval current_time, void *private_data);
deb7bff126ec547bd812d0c8683ad6e785a45abbChristian Maederadd_timeout_timer(struct tevent_context *ev, struct resolv_ctx *ctx)
ee9eddfa6953868fd6fbaff0d9ff68675a13675aChristian Maeder tvp = ares_timeout(ctx->channel, NULL, &tv);
deb7bff126ec547bd812d0c8683ad6e785a45abbChristian Maeder /* Enforce a minimum of 1 second. */
4fb19f237193a3bd6778f8aee3b6dd8da5856665Christian Maeder tv = tevent_timeval_current_ofs(tvp->tv_sec, tvp->tv_usec);
2dfc7b04f2db681992ca04175f2beb0f127c9844Christian Maeder ctx->timeout_watcher = tevent_add_timer(ev, ctx, tv, check_fd_timeouts,
2dfc7b04f2db681992ca04175f2beb0f127c9844Christian Maeder DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n");
2dfc7b04f2db681992ca04175f2beb0f127c9844Christian Maedercheck_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te,
deb7bff126ec547bd812d0c8683ad6e785a45abbChristian Maeder struct timeval current_time, void *private_data)
7221c71b38c871ce66eee4537cb681d468308dfbChristian Maeder struct resolv_ctx *ctx = talloc_get_type(private_data, struct resolv_ctx);
deb7bff126ec547bd812d0c8683ad6e785a45abbChristian Maeder DEBUG(SSSDBG_TRACE_ALL, "Checking for DNS timeouts\n");
4fb19f237193a3bd6778f8aee3b6dd8da5856665Christian Maeder /* NULLify the timeout_watcher so we don't
4fb19f237193a3bd6778f8aee3b6dd8da5856665Christian Maeder * free it in the _done() function if it
83814002b4922114cbe7e9ba728472a0bf44aac5Christian Maeder * gets called. Now that we're already in
83814002b4922114cbe7e9ba728472a0bf44aac5Christian Maeder * the handler, tevent will take care of
dedabc954aa15f6ad0764472a9434dc6dafe3db2Christian Maeder * freeing it when it returns.
07b72edb610ee53b4832d132e96b0a3d8423f8ebChristian Maeder ares_process_fd(ctx->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
2dfc7b04f2db681992ca04175f2beb0f127c9844Christian Maederresolv_request_timeout(struct tevent_context *ev,
07b72edb610ee53b4832d132e96b0a3d8423f8ebChristian Maeder DEBUG(SSSDBG_MINOR_FAILURE, "The resolve request timed out\n");
static struct resolv_request *
if (!rreq) {
return NULL;
rreq);
return NULL;
return NULL;
return rreq;
static struct resolv_request *
return rreq;
int flags;
while (watch) {
while (watch) {
int ret;
return EOK;
int ret;
return ENOMEM;
goto done;
return EOK;
done:
return ret;
static errno_t
return EOK;
static errno_t
return EOK;
static struct resolv_hostent *
int len;
return NULL;
goto fail;
goto fail;
for (i = 0; i < len; i++) {
goto fail;
return ret;
fail:
return NULL;
struct resolv_hostent *
int len;
return NULL;
goto fail;
for (i = 0; i < len; i++) {
struct resolv_addr);
goto fail;
goto fail;
return ret;
fail:
return NULL;
struct resolv_hostent *
int num_ares_ttl_data)
return NULL;
if (num_ares_ttl_data > 0) {
goto fail;
for (i = 0; i < num_ares_ttl_data; i++) {
struct resolv_addr);
goto fail;
switch (family) {
case AF_INET:
case AF_INET6:
goto fail;
goto fail;
return ret;
fail:
return NULL;
struct gethostbyname_files_state {
const char *name;
int family;
int status;
static struct tevent_req *
const char *name,
int family)
struct gethostbyname_files_state);
goto done;
&hostent);
goto done;
goto done;
goto done;
done:
return req;
static errno_t
struct gethostbyname_files_state);
if (status) {
if (rhostent) {
return EOK;
struct gethostbyname_dns_state {
const char *name;
int family;
int status;
int timeouts;
int retrying;
static struct tevent_req *
int family)
return NULL;
return NULL;
return NULL;
return req;
struct tevent_req);
struct gethostbyname_dns_state);
if (!rreq) {
/* If resolv.conf changed during processing of a request we might
int naddrttls;
case AF_INET:
if (!addr) {
goto fail;
&naddrttls);
case AF_INET6:
if (!addr) {
goto fail;
&naddrttls);
goto fail;
goto fail;
return ENOENT;
fail:
return ret;
struct gethostbyname_dns_state);
if (status) {
if (timeouts) {
if (rhostent) {
return EOK;
struct gethostbyname_state {
const char *name;
int family;
int dbi;
int status;
int timeouts;
int retrying;
static errno_t
static errno_t
struct tevent_req *
return NULL;
return NULL;
goto fail;
goto fail;
return req;
goto fail;
return req;
fail:
return NULL;
int ret;
if (ret != 0) {
return ret == 0;
static errno_t
int family;
if (!rhostent) {
goto done;
goto done;
struct resolv_addr);
goto done;
sizeof(struct in6_addr));
goto done;
goto done;
done:
return ret;
switch(family_order) {
case IPV4_ONLY:
case IPV4_FIRST:
return AF_INET;
case IPV6_ONLY:
case IPV6_FIRST:
return AF_INET6;
return EOK;
return EOK;
return EOK;
return ENOENT;
static errno_t
struct gethostbyname_state);
case DB_FILES:
case DB_DNS:
return EINVAL;
return ENOMEM;
return EOK;
struct tevent_req);
struct gethostbyname_state);
case DB_FILES:
case DB_DNS:
if (status) {
if (timeouts) {
if (rhostent) {
return EOK;
unsigned int addrindex)
char *address;
return NULL;
errno = 0;
return NULL;
return address;
char *straddr;
if (!straddr) {
return NULL;
(address[0]));
return NULL;
return straddr;
struct sockaddr_storage *
return NULL;
case AF_INET:
sizeof(struct in_addr));
case AF_INET6:
sizeof(struct in6_addr));
return NULL;
return sockaddr;
if (!old_list) {
return EOK;
while (old_list) {
if (!new_list) {
return ENOMEM;
return ENOMEM;
return ENOMEM;
return EOK;
struct getsrv_state {
/* the SRV query - for example _ldap._tcp.example.com */
const char *query;
int status;
int timeouts;
int retrying;
struct tevent_req *
return NULL;
return NULL;
return NULL;
return req;
int ret;
goto fail;
goto fail;
goto fail;
fail:
if (status)
if (timeouts)
if (reply_list)
return EOK;
struct tevent_req);
struct getsrv_state);
if (!rreq) {
#ifdef BUILD_TXT
if (!old_list) {
return EOK;
while (old_list) {
if (!new_list) {
return ENOMEM;
return ENOMEM;
return ENOMEM;
return EOK;
struct gettxt_state {
const char *query;
int status;
int timeouts;
int retrying;
struct tevent_req *
return NULL;
return NULL;
return NULL;
return req;
int ret;
goto fail;
goto fail;
goto fail;
fail:
if (status)
if (timeouts)
if (reply_list)
return EOK;
struct tevent_req);
struct gettxt_state);
if (!rreq) {
if (!list) {
return NULL;
return single_step;
struct ares_srv_reply *l, *r;
if (!left)
return right;
if (!right)
return left;
r = right;
l = left;
res = l;
l = l->next;
res = r;
r = r->next;
return res_start;
return list;
return list;
int *totals;
int ret;
return EOK;
if (!totals) {
return ENOMEM;
r = *(start);
while (r != NULL) {
if (r->weight == 0) {
if (prev) {
tmp = r;
r = r->next;
prev = r;
r = r->next;
total = 0;
prev = r;
goto done;
if (prev) {
if (!new_start) {
new_start = r;
new_end = r;
new_end = r;
done:
return ret;
int ret;
int len;
return EIO;
while (pri_start) {
len++;
if (ret) {
return ret;
if (prev_end) {
return EOK;