From 90f150f678347f1f73ee4280cd523021e307d861 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Fri, 10 Jun 2011 14:01:17 +0200 Subject: Honor the TTL value of SRV record lookups Add new resolv_hostent data structure and utility functions Resolve hosts by name from files into resolv_hostent Resolve hosts by name from DNS into resolv_hostent Switch resolver to using resolv_hostent and honor TTL Conflicts: src/providers/fail_over.c Provide TTL structure names for c-ares < 1.7 https://fedorahosted.org/sssd/ticket/898 In c-ares 1.7, the upstream renamed the addrttl/addr6ttl structures to ares_addrttl/ares_addr6ttl so they are in the ares_ namespace. Because they are committed to stable ABI, the contents are the same, just the name changed -- so it is safe to just #define the new name for older c-ares version in case the new one is not detected in configure time. --- src/external/libcares.m4 | 3 + src/providers/data_provider_fo.c | 9 +- src/providers/fail_over.c | 38 +- src/providers/fail_over.h | 2 +- src/providers/ipa/ipa_common.c | 4 +- src/providers/krb5/krb5_common.c | 2 +- src/providers/ldap/ldap_common.c | 4 +- src/resolv/async_resolv.c | 881 ++++++++++++++++++++++++++++++++------- src/resolv/async_resolv.h | 53 ++- src/tests/fail_over-tests.c | 6 +- src/tests/resolv-tests.c | 122 ++++-- 11 files changed, 903 insertions(+), 221 deletions(-) diff --git a/src/external/libcares.m4 b/src/external/libcares.m4 index 657deac59..5b8a35875 100644 --- a/src/external/libcares.m4 +++ b/src/external/libcares.m4 @@ -18,3 +18,6 @@ AC_CHECK_LIB([cares], ) AM_CONDITIONAL(BUILD_ARES_DATA, test x$ares_data = x1) + +dnl Check if this particular version of c-ares support the new TTL structures +AC_CHECK_TYPES([struct ares_addrttl, struct ares_addr6ttl], [], [], [#include ]) diff --git a/src/providers/data_provider_fo.c b/src/providers/data_provider_fo.c index f1e1e137c..58468c820 100644 --- a/src/providers/data_provider_fo.c +++ b/src/providers/data_provider_fo.c @@ -453,14 +453,15 @@ static void be_resolve_server_done(struct tevent_req *subreq) /* all fine we got the server */ if (debug_level >= 4) { - struct hostent *srvaddr; + struct resolv_hostent *srvaddr; char ipaddr[128]; srvaddr = fo_get_server_hostent(state->srv); - inet_ntop(srvaddr->h_addrtype, srvaddr->h_addr_list[0], + inet_ntop(srvaddr->family, srvaddr->addr_list[0]->ipaddr, ipaddr, 128); - DEBUG(4, ("Found address for server %s: [%s]\n", - fo_get_server_name(state->srv), ipaddr)); + DEBUG(4, ("Found address for server %s: [%s] TTL %d\n", + fo_get_server_name(state->srv), ipaddr, + srvaddr->addr_list[0]->ttl)); } srv_status_change = fo_get_server_hostname_last_change(state->srv); diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c index 085b1a41d..2a9631772 100644 --- a/src/providers/fail_over.c +++ b/src/providers/fail_over.c @@ -43,10 +43,6 @@ #define DEFAULT_SERVER_STATUS SERVER_NAME_NOT_RESOLVED #define DEFAULT_SRV_STATUS SRV_NEUTRAL -#ifndef HOSTNAME_RESOLVE_TIMEOUT -#define HOSTNAME_RESOLVE_TIMEOUT 7200 -#endif /* HOSTNAME_RESOLVE_TIMEOUT */ - enum srv_lookup_status { SRV_NEUTRAL, /* We didn't try this SRV lookup yet */ SRV_RESOLVED, /* This SRV lookup is resolved */ @@ -94,7 +90,7 @@ struct server_common { struct server_common *next; char *name; - struct hostent *hostent; + struct resolv_hostent *rhostent; struct resolve_service_request *request_list; int server_status; struct timeval last_status_change; @@ -328,8 +324,9 @@ get_server_status(struct fo_server *server) } } - if (STATUS_DIFF(server->common, tv) > HOSTNAME_RESOLVE_TIMEOUT) { - DEBUG(4, ("Hostname resolution expired, reseting the server " + if (server->common->rhostent && STATUS_DIFF(server->common, tv) > + server->common->rhostent->addr_list[0]->ttl) { + DEBUG(4, ("Hostname resolution expired, resetting the server " "status of '%s'\n", SERVER_NAME(server))); fo_set_server_status(server, SERVER_NAME_NOT_RESOLVED); } @@ -497,7 +494,7 @@ create_server_common(TALLOC_CTX *mem_ctx, struct fo_ctx *ctx, const char *name) common->ctx = ctx; common->prev = NULL; common->next = NULL; - common->hostent = NULL; + common->rhostent = NULL; common->request_list = NULL; common->server_status = DEFAULT_SERVER_STATUS; common->last_status_change.tv_sec = 0; @@ -846,7 +843,8 @@ fo_resolve_service_server(struct tevent_req *req) subreq = resolv_gethostbyname_send(state->server->common, state->ev, state->resolv, state->server->common->name, - state->fo_ctx->opts->family_order); + state->fo_ctx->opts->family_order, + default_host_dbs); if (subreq == NULL) { tevent_req_error(req, ENOMEM); return true; @@ -883,13 +881,13 @@ fo_resolve_service_done(struct tevent_req *subreq) struct resolve_service_request *request; int ret; - if (state->server->common->hostent != NULL) { - talloc_zfree(state->server->common->hostent); + if (state->server->common->rhostent != NULL) { + talloc_zfree(state->server->common->rhostent); } ret = resolv_gethostbyname_recv(subreq, state->server->common, &resolv_status, NULL, - &state->server->common->hostent); + &state->server->common->rhostent); talloc_zfree(subreq); if (ret != EOK) { DEBUG(1, ("Failed to resolve server '%s': %s\n", @@ -1224,7 +1222,8 @@ resolve_get_domain_send(TALLOC_CTX *mem_ctx, subreq = resolv_gethostbyname_send(state, ev, resolv, state->hostname, - foctx->opts->family_order); + foctx->opts->family_order, + default_host_dbs); if (!subreq) { talloc_zfree(req); return NULL; @@ -1240,10 +1239,10 @@ static void resolve_get_domain_done(struct tevent_req *subreq) struct tevent_req); struct resolve_get_domain_state *state = tevent_req_data(req, struct resolve_get_domain_state); - struct hostent *hostent; + struct resolv_hostent *rhostent; int ret; - ret = resolv_gethostbyname_recv(subreq, req, NULL, NULL, &hostent); + ret = resolv_gethostbyname_recv(subreq, req, NULL, NULL, &rhostent); talloc_zfree(subreq); if (ret) { DEBUG(2, ("Could not get fully qualified name for host name %s " @@ -1251,8 +1250,8 @@ static void resolve_get_domain_done(struct tevent_req *subreq) state->hostname, ret, strerror(ret))); /* We'll proceed with hostname in this case */ } else { - DEBUG(7, ("The full FQDN is: %s\n", hostent->h_name)); - state->fqdn = hostent->h_name; + DEBUG(7, ("The full FQDN is: %s\n", rhostent->name)); + state->fqdn = rhostent->name; } tevent_req_done(req); } @@ -1367,14 +1366,15 @@ const char *fo_get_server_name(struct fo_server *server) return server->common->name; } -struct hostent * +struct resolv_hostent * fo_get_server_hostent(struct fo_server *server) { if (server->common == NULL) { DEBUG(1, ("Bug: Trying to get hostent from a name-less server\n")); return NULL; } - return server->common->hostent; + + return server->common->rhostent; } time_t diff --git a/src/providers/fail_over.h b/src/providers/fail_over.h index 54141e9c8..e5d6c525f 100644 --- a/src/providers/fail_over.h +++ b/src/providers/fail_over.h @@ -169,7 +169,7 @@ int fo_get_server_port(struct fo_server *server); const char *fo_get_server_name(struct fo_server *server); -struct hostent *fo_get_server_hostent(struct fo_server *server); +struct resolv_hostent *fo_get_server_hostent(struct fo_server *server); time_t fo_get_server_hostname_last_change(struct fo_server *server); diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index 1a81bea75..647c1c187 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -557,7 +557,7 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server) { TALLOC_CTX *tmp_ctx = NULL; struct ipa_service *service; - struct hostent *srvaddr; + struct resolv_hostent *srvaddr; char *address; const char *safe_address; char *new_uri; @@ -592,7 +592,7 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server) } safe_address = sss_ldap_escape_ip_address(tmp_ctx, - srvaddr->h_addrtype, + srvaddr->family, address); if (safe_address == NULL) { DEBUG(1, ("sss_ldap_escape_ip_address failed.\n")); diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c index 06c3cbac6..6e190d0c5 100644 --- a/src/providers/krb5/krb5_common.c +++ b/src/providers/krb5/krb5_common.c @@ -367,7 +367,7 @@ done: static void krb5_resolve_callback(void *private_data, struct fo_server *server) { struct krb5_service *krb5_service; - struct hostent *srvaddr; + struct resolv_hostent *srvaddr; char *address; int ret; diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index 295469268..950c6e5aa 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -529,7 +529,7 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server) { TALLOC_CTX *tmp_ctx = NULL; struct sdap_service *service; - struct hostent *srvaddr; + struct resolv_hostent *srvaddr; char *address; const char *safe_address; const char *tmp; @@ -567,7 +567,7 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server) } safe_address = sss_ldap_escape_ip_address(tmp_ctx, - srvaddr->h_addrtype, + srvaddr->family, address); talloc_zfree(address); if (safe_address == NULL) { diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c index 13232097f..1f8056722 100644 --- a/src/resolv/async_resolv.c +++ b/src/resolv/async_resolv.c @@ -53,6 +53,19 @@ _ares_malloc_data(data) #endif /* HAVE_ARES_DATA */ +#ifndef HAVE_STRUCT_ARES_ADDRTTL +#define ares_addrttl addrttl +#endif + +#ifndef HAVE_STRUCT_ARES_ADDR6TTL +#define ares_addr6ttl addr6ttl +#endif + +#define DNS__16BIT(p) (((p)[0] << 8) | (p)[1]) +#define DNS_HEADER_ANCOUNT(h) DNS__16BIT((h) + 6) + +enum host_database default_host_dbs[] = { DB_FILES, DB_DNS, DB_SENTINEL }; + struct fd_watch { struct fd_watch *prev; struct fd_watch *next; @@ -330,7 +343,8 @@ recreate_ares_channel(struct resolv_ctx *ctx) options.sock_state_cb = fd_event; options.sock_state_cb_data = ctx; options.timeout = ctx->timeout * 1000; - options.lookups = discard_const("fb"); + /* Only affects ares_gethostbyname */ + options.lookups = discard_const("f"); options.tries = 1; ret = ares_init_options(&new_channel, &options, ARES_OPT_SOCK_STATE_CB | @@ -398,57 +412,170 @@ resolv_reread_configuration(void) } } -struct hostent * -resolv_copy_hostent(TALLOC_CTX *mem_ctx, struct hostent *src) +static errno_t +resolv_copy_in_addr(TALLOC_CTX *mem_ctx, struct resolv_addr *ret, + struct ares_addrttl *attl) +{ + ret->ipaddr = talloc_array(mem_ctx, uint8_t, sizeof(struct in_addr)); + if (!ret->ipaddr) return ENOMEM; + + memcpy(ret->ipaddr, &attl->ipaddr, sizeof(struct in_addr)); + ret->ttl = attl->ttl; + + return EOK; +} + +static errno_t +resolv_copy_in6_addr(TALLOC_CTX *mem_ctx, struct resolv_addr *ret, + struct ares_addr6ttl *a6ttl) { - struct hostent *ret; + ret->ipaddr = talloc_array(mem_ctx, uint8_t, sizeof(struct in6_addr)); + if (!ret->ipaddr) return ENOMEM; + + memcpy(ret->ipaddr, &a6ttl->ip6addr, sizeof(struct in6_addr)); + ret->ttl = a6ttl->ttl; + + return EOK; +} + +static struct resolv_hostent * +resolv_copy_hostent_common(TALLOC_CTX *mem_ctx, struct hostent *src) +{ + struct resolv_hostent *ret; int len; int i; - ret = talloc_zero(mem_ctx, struct hostent); + ret = talloc_zero(mem_ctx, struct resolv_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) { + ret->name = talloc_strdup(ret, src->h_name); + if (ret->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) { + + ret->aliases = talloc_array(ret, char *, len + 1); + if (ret->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) { + ret->aliases[i] = talloc_strdup(ret->aliases, src->h_aliases[i]); + if (ret->aliases[i] == NULL) { goto fail; } } - ret->h_aliases[len] = NULL; + ret->aliases[len] = NULL; } - ret->h_addrtype = src->h_addrtype; - ret->h_length = src->h_length; + + ret->family = src->h_addrtype; + return ret; + +fail: + talloc_free(ret); + return NULL; +} + +struct resolv_hostent * +resolv_copy_hostent(TALLOC_CTX *mem_ctx, struct hostent *src) +{ + struct resolv_hostent *ret; + int len; + int i; + + ret = resolv_copy_hostent_common(mem_ctx, src); + if (ret == NULL) { + return NULL; + } + 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) { + + ret->addr_list = talloc_array(ret, struct resolv_addr *, len + 1); + if (ret->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) { + ret->addr_list[i] = talloc_zero(ret->addr_list, + struct resolv_addr); + if (ret->addr_list[i] == NULL) { + goto fail; + } + + ret->addr_list[i]->ipaddr = talloc_memdup(ret->addr_list[i], + src->h_addr_list[i], + src->h_length); + if (ret->addr_list[i]->ipaddr == NULL) { goto fail; } + ret->addr_list[i]->ttl = RESOLV_DEFAULT_TTL; } - ret->h_addr_list[len] = NULL; + ret->addr_list[len] = NULL; } + return ret; + +fail: + talloc_free(ret); + return NULL; +} +struct resolv_hostent * +resolv_copy_hostent_ares(TALLOC_CTX *mem_ctx, struct hostent *src, + int family, void *ares_ttl_data, + int num_ares_ttl_data) +{ + struct resolv_hostent *ret; + errno_t cret; + int i; + + ret = resolv_copy_hostent_common(mem_ctx, src); + if (ret == NULL) { + return NULL; + } + + if (num_ares_ttl_data > 0) { + ret->addr_list = talloc_array(ret, struct resolv_addr *, + num_ares_ttl_data + 1); + if (ret->addr_list == NULL) { + goto fail; + } + + for (i = 0; i < num_ares_ttl_data; i++) { + ret->addr_list[i] = talloc_zero(ret->addr_list, + struct resolv_addr); + if (ret->addr_list[i] == NULL) { + goto fail; + } + + switch (family) { + case AF_INET: + cret = resolv_copy_in_addr(ret->addr_list, ret->addr_list[i], + &((struct ares_addrttl *) ares_ttl_data)[i]); + break; + case AF_INET6: + cret = resolv_copy_in6_addr(ret->addr_list, ret->addr_list[i], + &((struct ares_addr6ttl *) ares_ttl_data)[i]); + break; + default: + DEBUG(1, ("Unknown address family %d\n")); + goto fail; + } + + if (cret != EOK) { + DEBUG(1, ("Could not copy address\n")); + goto fail; + } + } + ret->addr_list[num_ares_ttl_data] = NULL; + } + + ret->family = family; return ret; fail: @@ -456,35 +583,135 @@ fail: return NULL; } -/******************************************************************* - * Get host by name. * - *******************************************************************/ +/* =================== Resolve host name in files =========================*/ +struct gethostbyname_files_state { + struct resolv_ctx *resolv_ctx; -struct gethostbyname_state { + /* Part of the query. */ + const char *name; + int family; + + /* query result */ + struct resolv_hostent *rhostent; + + /* returned by ares. */ + int status; +}; + +/* Fake up an async interface even though files would + * always be blocking */ +static struct tevent_req * +resolv_gethostbyname_files_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resolv_ctx *ctx, + const char *name, + int family) +{ + struct tevent_req *req; + struct gethostbyname_files_state *state; + struct hostent *hostent = NULL; + + req = tevent_req_create(mem_ctx, &state, + struct gethostbyname_files_state); + if (req == NULL) { + tevent_req_error(req, ENOMEM); + goto done; + } + + state->resolv_ctx = ctx; + state->name = name; + state->rhostent = NULL; + state->family = family; + + DEBUG(4, ("Trying to resolve %s record of '%s' in files\n", + state->family == AF_INET ? "A" : "AAAA", state->name)); + + state->status = ares_gethostbyname_file(state->resolv_ctx->channel, + state->name, state->family, + &hostent); + + if (state->status == ARES_SUCCESS) { + state->rhostent = resolv_copy_hostent(state, hostent); + if (state->rhostent == NULL) { + tevent_req_error(req, ENOMEM); + goto done; + } + } else if (state->status == ARES_ENOTFOUND || + state->status == ARES_ENODATA) { + /* Just say we didn't find anything and let the caller decide + * about retrying */ + tevent_req_error(req, ENOENT); + goto done; + } else { + tevent_req_error(req, return_code(state->status)); + goto done; + } + + tevent_req_done(req); +done: + if (hostent) ares_free_hostent(hostent); + tevent_req_post(req, ev); + return req; +} + +static errno_t +resolv_gethostbyname_files_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + int *status, struct resolv_hostent **rhostent) +{ + struct gethostbyname_files_state *state = tevent_req_data(req, + struct gethostbyname_files_state); + + /* Fill in even in case of error as status contains the + * c-ares return code */ + if (status) { + *status = state->status; + } + if (rhostent) { + *rhostent = talloc_steal(mem_ctx, state->rhostent); + } + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +/* ==================== Resolve host name in DNS =========================*/ +struct gethostbyname_dns_state { struct resolv_ctx *resolv_ctx; + struct tevent_context *ev; + /* Part of the query. */ const char *name; int family; - /* In which order to use IPv4, or v6 */ - enum restrict_family family_order; - /* These are returned by ares. The hostent struct will be freed - * when the user callback returns. */ - struct hostent *hostent; + + /* query result */ + struct resolv_hostent *rhostent; + + /* These are returned by ares. */ int status; int timeouts; int retrying; }; static void -ares_gethostbyname_wakeup(struct tevent_req *req); +resolv_gethostbyname_dns_wakeup(struct tevent_req *subreq); +static void +resolv_gethostbyname_dns_query(struct tevent_req *req, + struct gethostbyname_dns_state *state); +static void +resolv_gethostbyname_dns_query_done(void *arg, int status, int timeouts, + unsigned char *abuf, int alen); +static int +resolv_gethostbyname_dns_parse(struct gethostbyname_dns_state *state, int status, + int timeouts, unsigned char *abuf, int alen); -struct tevent_req * -resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - struct resolv_ctx *ctx, const char *name, - enum restrict_family family_order) +static struct tevent_req * +resolv_gethostbyname_dns_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct resolv_ctx *ctx, const char *name, + int family) { struct tevent_req *req, *subreq; - struct gethostbyname_state *state; + struct gethostbyname_dns_state *state; struct timeval tv = { 0, 0 }; if (ctx->channel == NULL) { @@ -492,25 +719,22 @@ resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, return NULL; } - req = tevent_req_create(mem_ctx, &state, struct gethostbyname_state); - if (req == NULL) + req = tevent_req_create(mem_ctx, &state, struct gethostbyname_dns_state); + if (req == NULL) { return NULL; + } state->resolv_ctx = ctx; + state->ev = ev; state->name = name; - state->hostent = NULL; + state->rhostent = NULL; state->status = 0; state->timeouts = 0; state->retrying = 0; - state->family_order = family_order; - state->family = (family_order < IPV6_ONLY) ? AF_INET : AF_INET6; + state->family = family; - DEBUG(4, ("Trying to resolve %s record of '%s'\n", - state->family == AF_INET ? "A" : "AAAA", - state->name)); - - /* We need to have a wrapper around ares_gethostbyname(), because - * ares_gethostbyname() can in some cases call it's callback immediately. + /* We need to have a wrapper around ares async calls, because + * they 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) { @@ -518,116 +742,516 @@ resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, talloc_zfree(req); return NULL; } - tevent_req_set_callback(subreq, ares_gethostbyname_wakeup, req); + tevent_req_set_callback(subreq, resolv_gethostbyname_dns_wakeup, req); schedule_timeout_watcher(ev, ctx); return req; } static void -resolv_gethostbyname_next_done(void *arg, int status, int timeouts, struct hostent *hostent); +resolv_gethostbyname_dns_wakeup(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct gethostbyname_dns_state *state = tevent_req_data(req, + struct gethostbyname_dns_state); + + if (!tevent_wakeup_recv(subreq)) { + tevent_req_error(req, EIO); + 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; + } + + resolv_gethostbyname_dns_query(req, state); +} + +static void +resolv_gethostbyname_dns_query(struct tevent_req *req, + struct gethostbyname_dns_state *state) +{ + DEBUG(4, ("Trying to resolve %s record of '%s' in DNS\n", + state->family == AF_INET ? "A" : "AAAA", state->name)); + + ares_query(state->resolv_ctx->channel, + state->name, ns_c_in, + (state->family == AF_INET) ? ns_t_a : ns_t_aaaa, + resolv_gethostbyname_dns_query_done, req); +} static void -resolv_gethostbyname_done(void *arg, int status, int timeouts, struct hostent *hostent) +resolv_gethostbyname_dns_query_done(void *arg, int status, int timeouts, + unsigned char *abuf, int alen) { struct tevent_req *req = talloc_get_type(arg, struct tevent_req); - struct gethostbyname_state *state = tevent_req_data(req, struct gethostbyname_state); + struct gethostbyname_dns_state *state = tevent_req_data(req, + struct gethostbyname_dns_state); + errno_t ret; + unschedule_timeout_watcher(state->resolv_ctx); + + state->status = status; + state->timeouts = timeouts; + + /* If resolv.conf changed during processing of a request we might + * destroy the old channel before the request has a chance to finish. + * We must resend the request in this case */ if (state->retrying == 0 && status == ARES_EDESTRUCTION - && state->resolv_ctx->channel != NULL) { + && state->resolv_ctx->channel != NULL) { state->retrying = 1; - ares_gethostbyname(state->resolv_ctx->channel, state->name, - state->family, resolv_gethostbyname_done, req); + schedule_timeout_watcher(state->ev, state->resolv_ctx); + resolv_gethostbyname_dns_query(req, state); 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; + if (status == ARES_ENOTFOUND || status == ARES_ENODATA) { + /* Just say we didn't find anything and let the caller decide + * about retrying */ + tevent_req_error(req, ENOENT); + return; } - state->status = status; - state->timeouts = timeouts; if (status != ARES_SUCCESS) { - if (status == ARES_ENOTFOUND || status == ARES_ENODATA) { - if (state->family_order == IPV4_FIRST) { - state->family = AF_INET6; - } else if (state->family_order == IPV6_FIRST) { - state->family = AF_INET; - } else { - tevent_req_error(req, return_code(status)); - return; - } - - state->retrying = 0; - state->timeouts = 0; - DEBUG(4, ("Trying to resolve %s record of '%s'\n", - state->family == AF_INET ? "A" : "AAAA", - state->name)); - schedule_timeout_watcher(state->resolv_ctx->ev_ctx, - state->resolv_ctx); - ares_gethostbyname(state->resolv_ctx->channel, state->name, - state->family, resolv_gethostbyname_next_done, - req); - return; - } - /* Any other error indicates a server error, * so don't bother trying again */ tevent_req_error(req, return_code(status)); + return; + } + + ret = resolv_gethostbyname_dns_parse(state, status, timeouts, abuf, alen); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static int +resolv_gethostbyname_dns_parse(struct gethostbyname_dns_state *state, + int status, int timeouts, + unsigned char *abuf, int alen) +{ + TALLOC_CTX *tmp_ctx; + struct hostent *hostent; + int naddrttls; + errno_t ret; + void *addr; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) return ENOMEM; + + naddrttls = DNS_HEADER_ANCOUNT(abuf); + + switch (state->family) { + case AF_INET: + DEBUG(7, ("Parsing an A reply\n")); + + addr = talloc_array(state, struct ares_addrttl, naddrttls); + if (!addr) { + ret = ENOMEM; + goto fail; + } + + status = ares_parse_a_reply(abuf, alen, &hostent, + (struct ares_addrttl *) addr, + &naddrttls); + break; + case AF_INET6: + DEBUG(7, ("Parsing an AAAA reply\n")); + + addr = talloc_array(state, struct ares_addr6ttl, naddrttls); + if (!addr) { + ret = ENOMEM; + goto fail; + } + + status = ares_parse_aaaa_reply(abuf, alen, &hostent, + (struct ares_addr6ttl *) addr, + &naddrttls); + break; + default: + DEBUG(1, ("Unknown family %d\n", state->family)); + ret = EAFNOSUPPORT; + goto fail; + } + + if (hostent != NULL) { + state->rhostent = resolv_copy_hostent_ares(state, hostent, + state->family, + addr, naddrttls); + free(hostent); + if (state->rhostent == NULL) { + ret = ENOMEM; + goto fail; + } } - else { + + talloc_free(tmp_ctx); + return return_code(status); + +fail: + talloc_free(tmp_ctx); + return ret; +} + +static int +resolv_gethostbyname_dns_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + int *status, int *timeouts, + struct resolv_hostent **rhostent) +{ + struct gethostbyname_dns_state *state = tevent_req_data(req, + struct gethostbyname_dns_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 (rhostent) { + *rhostent = talloc_steal(mem_ctx, state->rhostent); + } + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +/******************************************************************* + * Get host by name. * + *******************************************************************/ + +struct gethostbyname_state { + struct resolv_ctx *resolv_ctx; + struct tevent_context *ev; + + /* Part of the query. */ + const char *name; + int family; + + /* In which order to use IPv4, or v6 */ + enum restrict_family family_order; + + /* Known hosts databases and index to the current one */ + enum host_database *db; + int dbi; + + /* These are returned by ares. The hostent struct will be freed + * when the user callback returns. */ + struct resolv_hostent *rhostent; + int status; + int timeouts; + int retrying; +}; + +static errno_t +resolv_gethostbyname_address(TALLOC_CTX *mem_ctx, const char *address, + struct resolv_hostent **_rhostent); +static inline int +resolv_gethostbyname_family_init(enum restrict_family family_order); +static bool +resolv_is_address(const char *name); +static errno_t +resolv_gethostbyname_step(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, + enum restrict_family family_order, + enum host_database *db) +{ + struct tevent_req *req; + struct gethostbyname_state *state; + errno_t ret; + + 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->ev = ev; + state->name = name; + state->rhostent = NULL; + state->status = 0; + state->timeouts = 0; + state->retrying = 0; + state->family_order = family_order; + state->family = resolv_gethostbyname_family_init(state->family_order); + state->db = db; + state->dbi = 0; + + /* Do not attempt to resolve IP addresses */ + if (resolv_is_address(state->name)) { + ret = resolv_gethostbyname_address(state, state->name, + &state->rhostent); + if (ret != EOK) { + DEBUG(1, ("Canot create a fake hostent structure\n")); + talloc_zfree(req); + return NULL; + } + tevent_req_done(req); + tevent_req_post(req, ev); + return req; } + + ret = resolv_gethostbyname_step(req); + if (ret != EOK) { + DEBUG(1, ("Cannot start the resolving\n")); + talloc_zfree(req); + return NULL; + } + + return req; } -static void -resolv_gethostbyname_next_done(void *arg, int status, int timeouts, struct hostent *hostent) +static bool +resolv_is_address(const char *name) { - struct tevent_req *req = talloc_get_type(arg, struct tevent_req); - struct gethostbyname_state *state = tevent_req_data(req, struct gethostbyname_state); + struct addrinfo hints; + struct addrinfo *res = NULL; + int ret; - if (state->retrying == 0 && status == ARES_EDESTRUCTION) { - state->retrying = 1; - ares_gethostbyname(state->resolv_ctx->channel, state->name, - state->family, resolv_gethostbyname_next_done, req); - return; + memset((void *) &hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; /* No network lookups */ + + ret = getaddrinfo(name, NULL, &hints, &res); + freeaddrinfo(res); + if (ret != 0) { + if (ret == -2) { + DEBUG(9, ("[%s] does not look like an IP address\n", name)); + } else { + DEBUG(2, ("getaddrinfo failed [%d]: %s\n", + ret, gai_strerror(ret))); + } } - unschedule_timeout_watcher(state->resolv_ctx); + return ret == 0; +} - if (hostent != NULL) { - state->hostent = resolv_copy_hostent(req, hostent); - if (state->hostent == NULL) { - tevent_req_error(req, ENOMEM); - return; +static errno_t +resolv_gethostbyname_address(TALLOC_CTX *mem_ctx, const char *address, + struct resolv_hostent **_rhostent) +{ + struct resolv_hostent *rhostent; + TALLOC_CTX *tmp_ctx; + errno_t ret; + int family; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) return ENOMEM; + + rhostent = talloc_zero(tmp_ctx, struct resolv_hostent); + if (!rhostent) { + ret = ENOMEM; + goto done; + } + + rhostent->name = talloc_strdup(rhostent, address); + rhostent->addr_list = talloc_array(rhostent, struct resolv_addr *, 2); + + if (!rhostent->name || + !rhostent->addr_list) { + ret = ENOMEM; + goto done; + } + + rhostent->addr_list[0] = talloc_zero(rhostent->addr_list, + struct resolv_addr); + if (!rhostent->addr_list[0]) { + ret = ENOMEM; + goto done; + } + rhostent->addr_list[0]->ipaddr = talloc_array(rhostent->addr_list[0], + uint8_t, + sizeof(struct in6_addr)); + if (!rhostent->addr_list[0]->ipaddr) { + ret = ENOMEM; + goto done; + } + + family = AF_INET; + ret = inet_pton(family, address, + rhostent->addr_list[0]->ipaddr); + if (ret != 1) { + family = AF_INET6; + ret = inet_pton(family, address, + rhostent->addr_list[0]->ipaddr); + if (ret != 1) { + DEBUG(1, ("Could not parse address as neither v4 nor v6\n")); + ret = EINVAL; + goto done; } + } + + rhostent->addr_list[0]->ttl = RESOLV_DEFAULT_TTL; + rhostent->addr_list[1] = NULL; + rhostent->family = family; + rhostent->aliases = NULL; + + *_rhostent = talloc_move(mem_ctx, &rhostent); + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +static inline int +resolv_gethostbyname_family_init(enum restrict_family family_order) +{ + switch(family_order) { + case IPV4_ONLY: + case IPV4_FIRST: + return AF_INET; + case IPV6_ONLY: + case IPV6_FIRST: + return AF_INET6; + } + + DEBUG(1, ("Unknown address family order %d\n", family_order)); + return -1; +} + +static int +resolv_gethostbyname_next(struct gethostbyname_state *state) +{ + if (state->family_order == IPV4_FIRST && + state->family == AF_INET) { + state->family = AF_INET6; + return EOK; + } else if (state->family_order == IPV6_FIRST && + state->family == AF_INET6) { + state->family = AF_INET; + return EOK; } else { - state->hostent = NULL; + /* No more address families for this DB, check if + * there is another DB to try */ + DEBUG(5, ("No more address families to retry\n")); + state->dbi++; + if (state->db[state->dbi] != DB_SENTINEL) { + state->family = resolv_gethostbyname_family_init( + state->family_order); + return EOK; + } } - state->status = status; - state->timeouts = timeouts; - if (status != ARES_SUCCESS) { - tevent_req_error(req, return_code(status)); + DEBUG(4, ("No more hosts databases to retry\n")); + return ENOENT; +} + +static void +resolv_gethostbyname_done(struct tevent_req *subreq); + +static errno_t +resolv_gethostbyname_step(struct tevent_req *req) +{ + struct gethostbyname_state *state = tevent_req_data(req, + struct gethostbyname_state); + struct tevent_req *subreq; + + switch(state->db[state->dbi]) { + case DB_FILES: + DEBUG(8, ("Querying files\n")); + subreq = resolv_gethostbyname_files_send(state, state->ev, + state->resolv_ctx, + state->name, + state->family); + break; + case DB_DNS: + DEBUG(8, ("Querying DNS\n")); + subreq = resolv_gethostbyname_dns_send(state, state->ev, + state->resolv_ctx, + state->name, + state->family); + break; + default: + DEBUG(1, ("Invalid hosts database\n")); + return EINVAL; } - else { - tevent_req_done(req); + + if (subreq == NULL) { + return ENOMEM; + } + + tevent_req_set_callback(subreq, resolv_gethostbyname_done, req); + return EOK; +} + +static void +resolv_gethostbyname_done(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); + errno_t ret; + + switch(state->db[state->dbi]) { + case DB_FILES: + ret = resolv_gethostbyname_files_recv(subreq, state, + &state->status, + &state->rhostent); + /* files is synchronous, there can be no timeouts */ + state->timeouts = 0; + break; + case DB_DNS: + ret = resolv_gethostbyname_dns_recv(subreq, state, + &state->status, &state->timeouts, + &state->rhostent); + break; + default: + DEBUG(1, ("Invalid hosts database\n")); + tevent_req_error(req, EINVAL); + return; + } + + talloc_zfree(subreq); + + if (ret == ENOENT) { + ret = resolv_gethostbyname_next(state); + if (ret == EOK) { + ret = resolv_gethostbyname_step(req); + if (ret != EOK) { + tevent_req_error(req, ret); + } + return; + } + + /* No more databases and/or address families */ + tevent_req_error(req, ENOENT); + return; + } + + if (ret != EOK) { + DEBUG(2, ("querying hosts database failed [%d]: %s\n", + ret, strerror(ret))); + tevent_req_error(req, ret); + return; } + + tevent_req_done(req); } int resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, int *status, int *timeouts, - struct hostent **hostent) + struct resolv_hostent **rhostent) { struct gethostbyname_state *state = tevent_req_data(req, struct gethostbyname_state); @@ -639,8 +1263,8 @@ resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, if (timeouts) { *timeouts = state->timeouts; } - if (hostent) { - *hostent = talloc_steal(mem_ctx, state->hostent); + if (rhostent) { + *rhostent = talloc_steal(mem_ctx, state->rhostent); } TEVENT_REQ_RETURN_ON_ERROR(req); @@ -649,7 +1273,7 @@ resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, } char * -resolv_get_string_address(TALLOC_CTX *mem_ctx, struct hostent *hostent) +resolv_get_string_address(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent) { char *address; @@ -662,7 +1286,7 @@ resolv_get_string_address(TALLOC_CTX *mem_ctx, struct hostent *hostent) } errno = 0; - if (inet_ntop(hostent->h_addrtype, hostent->h_addr_list[0], + if (inet_ntop(hostent->family, hostent->addr_list[0]->ipaddr, address, 128) == NULL) { DEBUG(1, ("inet_ntop failed [%d][%s].\n", errno, strerror(errno))); talloc_free(address); @@ -672,29 +1296,6 @@ resolv_get_string_address(TALLOC_CTX *mem_ctx, struct hostent *hostent) return address; } -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); -} - /* * 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 diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h index d6cbe1494..907865f7a 100644 --- a/src/resolv/async_resolv.h +++ b/src/resolv/async_resolv.h @@ -37,6 +37,10 @@ #include "resolv/ares/ares_data.h" #endif /* HAVE_ARES_DATA */ +#ifndef RESOLV_DEFAULT_TTL +#define RESOLV_DEFAULT_TTL 7200 +#endif /* RESOLV_DEFAULT_TTL */ + /* * An opaque structure which holds context for a module using the async * resolver. Is should be used as a "local-global" variable - in sssd, @@ -53,10 +57,22 @@ void resolv_reread_configuration(void); const char *resolv_strerror(int ares_code); -struct hostent *resolv_copy_hostent(TALLOC_CTX *mem_ctx, - struct hostent *src); +struct resolv_hostent * +resolv_copy_hostent(TALLOC_CTX *mem_ctx, struct hostent *src); + +struct resolv_hostent * +resolv_copy_hostent_ares(TALLOC_CTX *mem_ctx, struct hostent *src, + int family, void *ares_ttl_data, + int num_ares_ttl_data); /** Get host by name **/ +enum host_database { + DB_FILES, + DB_DNS, + + DB_SENTINEL +}; + enum restrict_family { IPV4_ONLY, IPV4_FIRST, @@ -64,20 +80,39 @@ enum restrict_family { IPV6_FIRST }; +/* If resolv_hostent->family is AF_INET, then ipaddr points to + * struct in_addr, else if family is AF_INET6, ipaddr points to + * struct in6_addr + */ +struct resolv_addr { + uint8_t *ipaddr; + int ttl; +}; + +struct resolv_hostent { + char *name; /* official name of host */ + char **aliases; /* alias list */ + int family; /* host address type */ + + struct resolv_addr **addr_list; /* list of addresses */ +}; + +/* The default database order */ +extern enum host_database default_host_dbs[]; + struct tevent_req *resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct resolv_ctx *ctx, const char *name, - enum restrict_family family_order); + enum restrict_family family_order, + enum host_database *db); -int resolv_gethostbyname_recv(struct tevent_req *req, - TALLOC_CTX *mem_ctx, - int *status, - int *timeouts, - struct hostent **hostent); +int resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + int *status, int *timeouts, + struct resolv_hostent **rhostent); char * -resolv_get_string_address(TALLOC_CTX *mem_ctx, struct hostent *hostent); +resolv_get_string_address(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent); /** Get SRV record **/ struct tevent_req *resolv_getsrv_send(TALLOC_CTX *mem_ctx, diff --git a/src/tests/fail_over-tests.c b/src/tests/fail_over-tests.c index a96549dcc..84016dd9b 100644 --- a/src/tests/fail_over-tests.c +++ b/src/tests/fail_over-tests.c @@ -147,7 +147,7 @@ test_resolve_service_callback(struct tevent_req *req) int port; struct task *task; struct fo_server *server = NULL; - struct hostent *he; + struct resolv_hostent *he; int i; task = tevent_req_callback_data(req, struct task); @@ -172,10 +172,10 @@ test_resolve_service_callback(struct tevent_req *req) he = fo_get_server_hostent(server); fail_if(he == NULL, "%s: fo_get_server_hostent() returned NULL"); - for (i = 0; he->h_addr_list[i]; i++) { + for (i = 0; he->addr_list[i]; i++) { char buf[256]; - inet_ntop(he->h_addrtype, he->h_addr_list[i], buf, sizeof(buf)); + inet_ntop(he->family, he->addr_list[i]->ipaddr, buf, sizeof(buf)); fail_if(strcmp(buf, "127.0.0.1") != 0 && strcmp(buf, "::1") != 0); } diff --git a/src/tests/resolv-tests.c b/src/tests/resolv-tests.c index b8c535cf0..5c1a487f4 100644 --- a/src/tests/resolv-tests.c +++ b/src/tests/resolv-tests.c @@ -95,37 +95,59 @@ static int test_loop(struct resolv_test_ctx *data) START_TEST(test_copy_hostent) { void *ctx; - struct hostent *new_he; + struct resolv_hostent *rhe; char name[] = "foo.example.com"; char alias_1[] = "bar.example.com"; char alias_2[] = "baz.example.com"; char *aliases[] = { alias_1, alias_2, NULL }; - char addr_1[] = { 1, 2, 3, 4 }; - char addr_2[] = { 4, 3, 2, 1 }; - char *addr_list[] = { addr_1, addr_2, NULL }; + struct in_addr addr_1 = { 1234 }; + struct in_addr addr_2 = { 5678 }; + int ttl_1 = 12; + int ttl_2 = 34; + char *addr_list[] = { (char *) &addr_2, (char *) &addr_1, NULL }; struct hostent he = { - name, aliases, 123 /* Whatever. */, + name, aliases, AF_INET, sizeof(addr_1), addr_list }; + struct ares_addrttl attl[] = { { addr_1, ttl_1 }, { addr_2, ttl_2 } }; ctx = talloc_new(global_talloc_context); fail_if(ctx == NULL); check_leaks_push(ctx); - new_he = resolv_copy_hostent(ctx, &he); - fail_if(new_he == NULL); - fail_if(strcmp(new_he->h_name, name)); - fail_if(strcmp(new_he->h_aliases[0], alias_1)); - fail_if(strcmp(new_he->h_aliases[1], alias_2)); - fail_if(new_he->h_aliases[2] != NULL); - fail_if(new_he->h_addrtype != 123); - fail_if(new_he->h_length != sizeof(addr_1)); - fail_if(memcmp(new_he->h_addr_list[0], addr_1, sizeof(addr_1))); - fail_if(memcmp(new_he->h_addr_list[1], addr_2, sizeof(addr_1))); - fail_if(new_he->h_addr_list[2] != NULL); - - talloc_free(new_he); + + rhe = resolv_copy_hostent_ares(ctx, &he, AF_INET, &attl, 2); + + fail_if(rhe == NULL); + fail_if(strcmp(rhe->name, name)); + fail_if(strcmp(rhe->aliases[0], alias_1)); + fail_if(strcmp(rhe->aliases[1], alias_2)); + fail_if(rhe->aliases[2] != NULL); + fail_if(rhe->family != AF_INET); + fail_if(memcmp(rhe->addr_list[0]->ipaddr, &addr_1, sizeof(addr_1))); + fail_if(rhe->addr_list[0]->ttl != ttl_1); + fail_if(memcmp(rhe->addr_list[1]->ipaddr, &addr_2, sizeof(addr_2))); + fail_if(rhe->addr_list[1]->ttl != ttl_2); + fail_if(rhe->addr_list[2] != NULL); + + talloc_zfree(rhe); + + rhe = resolv_copy_hostent(ctx, &he); + fail_if(rhe == NULL); + fail_if(strcmp(rhe->name, name)); + fail_if(strcmp(rhe->aliases[0], alias_1)); + fail_if(strcmp(rhe->aliases[1], alias_2)); + fail_if(rhe->aliases[2] != NULL); + fail_if(rhe->family != AF_INET); + fail_if(memcmp(rhe->addr_list[0]->ipaddr, &addr_2, sizeof(addr_1))); + fail_if(rhe->addr_list[0]->ttl != RESOLV_DEFAULT_TTL); + fail_if(memcmp(rhe->addr_list[1]->ipaddr, &addr_1, sizeof(addr_2))); + fail_if(rhe->addr_list[1]->ttl != RESOLV_DEFAULT_TTL); + fail_if(rhe->addr_list[2] != NULL); + + talloc_free(rhe); + check_leaks_pop(ctx); } END_TEST @@ -134,7 +156,7 @@ static void test_ip_addr(struct tevent_req *req) { int recv_status; int status; - struct hostent *hostent; + struct resolv_hostent *rhostent; int i; struct resolv_test_ctx *test_ctx = tevent_req_callback_data(req, struct resolv_test_ctx); @@ -142,7 +164,7 @@ static void test_ip_addr(struct tevent_req *req) test_ctx->done = true; recv_status = resolv_gethostbyname_recv(req, test_ctx, - &status, NULL, &hostent); + &status, NULL, &rhostent); talloc_zfree(req); if (recv_status != EOK) { DEBUG(2, ("resolv_gethostbyname_recv failed: %d\n", recv_status)); @@ -152,15 +174,17 @@ static void test_ip_addr(struct tevent_req *req) DEBUG(7, ("resolv_gethostbyname_recv status: %d\n", status)); test_ctx->error = ENOENT; - for (i = 0; hostent->h_addr_list[i]; i++) { + for (i = 0; rhostent->addr_list[i]; i++) { char addr_buf[256]; - inet_ntop(hostent->h_addrtype, hostent->h_addr_list[i], addr_buf, sizeof(addr_buf)); + inet_ntop(rhostent->family, + rhostent->addr_list[i]->ipaddr, + addr_buf, sizeof(addr_buf)); if (strcmp(addr_buf, "127.0.0.1") == 0) { test_ctx->error = EOK; } } - talloc_free(hostent); + talloc_free(rhostent); } START_TEST(test_resolv_ip_addr) @@ -178,7 +202,8 @@ START_TEST(test_resolv_ip_addr) check_leaks_push(test_ctx); req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, - test_ctx->resolv, hostname, IPV4_ONLY); + test_ctx->resolv, hostname, IPV4_ONLY, + default_host_dbs); DEBUG(7, ("Sent resolv_gethostbyname\n")); if (req == NULL) { ret = ENOMEM; @@ -200,7 +225,7 @@ static void test_localhost(struct tevent_req *req) { int recv_status; int status; - struct hostent *hostent; + struct resolv_hostent *rhostent; int i; struct resolv_test_ctx *test_ctx = tevent_req_callback_data(req, struct resolv_test_ctx); @@ -208,7 +233,7 @@ static void test_localhost(struct tevent_req *req) test_ctx->done = true; recv_status = resolv_gethostbyname_recv(req, test_ctx, - &status, NULL, &hostent); + &status, NULL, &rhostent); talloc_zfree(req); if (recv_status != EOK) { DEBUG(2, ("resolv_gethostbyname_recv failed: %d\n", recv_status)); @@ -218,16 +243,17 @@ static void test_localhost(struct tevent_req *req) DEBUG(7, ("resolv_gethostbyname_recv status: %d\n", status)); test_ctx->error = ENOENT; - for (i = 0; hostent->h_addr_list[i]; i++) { + for (i = 0; rhostent->addr_list[i]; i++) { char addr_buf[256]; - inet_ntop(hostent->h_addrtype, hostent->h_addr_list[i], addr_buf, sizeof(addr_buf)); + inet_ntop(rhostent->family, rhostent->addr_list[i]->ipaddr, + addr_buf, sizeof(addr_buf)); /* test that localhost resolves to 127.0.0.1 or ::1 */ if (strcmp(addr_buf, "127.0.0.1") == 0 || strcmp(addr_buf, "::1") == 0) { test_ctx->error = EOK; } } - talloc_free(hostent); + talloc_free(rhostent); } START_TEST(test_resolv_localhost) @@ -245,7 +271,8 @@ START_TEST(test_resolv_localhost) check_leaks_push(test_ctx); req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, - test_ctx->resolv, hostname, IPV4_FIRST); + test_ctx->resolv, hostname, IPV4_FIRST, + default_host_dbs); DEBUG(7, ("Sent resolv_gethostbyname\n")); if (req == NULL) { ret = ENOMEM; @@ -267,7 +294,7 @@ static void test_negative(struct tevent_req *req) { int recv_status; int status; - struct hostent *hostent; + struct resolv_hostent *hostent; struct resolv_test_ctx *test_ctx; test_ctx = tevent_req_callback_data(req, struct resolv_test_ctx); @@ -300,7 +327,8 @@ START_TEST(test_resolv_negative) check_leaks_push(test_ctx); req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, - test_ctx->resolv, hostname, IPV4_FIRST); + test_ctx->resolv, hostname, IPV4_FIRST, + default_host_dbs); DEBUG(7, ("Sent resolv_gethostbyname\n")); if (req == NULL) { ret = ENOMEM; @@ -325,9 +353,10 @@ static void test_internet(struct tevent_req *req) int status; struct resolv_test_ctx *test_ctx; void *tmp_ctx; - struct hostent *hostent = NULL; + struct resolv_hostent *rhostent = NULL; struct ares_txt_reply *txt_replies = NULL, *txtptr; struct ares_srv_reply *srv_replies = NULL, *srvptr; + int i; test_ctx = tevent_req_callback_data(req, struct resolv_test_ctx); @@ -339,8 +368,18 @@ static void test_internet(struct tevent_req *req) switch (test_ctx->tested_function) { case TESTING_HOSTNAME: recv_status = resolv_gethostbyname_recv(req, tmp_ctx, - &status, NULL, &hostent); - test_ctx->error = (hostent->h_length == 0) ? ENOENT : EOK; + &status, NULL, &rhostent); + test_ctx->error = (rhostent->name == NULL) ? ENOENT : EOK; + if (test_ctx->error == EOK) { + char addr_buf[256]; + for (i=0; rhostent->addr_list[i]; i++) { + inet_ntop(rhostent->family, + rhostent->addr_list[i]->ipaddr, + addr_buf, sizeof(addr_buf)); + DEBUG(2, ("Found address %s with TTL %d\n", + addr_buf, rhostent->addr_list[i]->ttl)); + } + } break; case TESTING_TXT: recv_status = resolv_gettxt_recv(tmp_ctx, req, &status, NULL, @@ -368,8 +407,8 @@ static void test_internet(struct tevent_req *req) fail_if(recv_status != EOK, "The recv function failed: %d", recv_status); DEBUG(7, ("recv status: %d\n", status)); - if (hostent != NULL) { - talloc_free(hostent); + if (rhostent != NULL) { + talloc_free(rhostent); } else if (txt_replies != NULL) { talloc_free(txt_replies); } else if (srv_replies != NULL) { @@ -394,7 +433,8 @@ START_TEST(test_resolv_internet) check_leaks_push(test_ctx); req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, - test_ctx->resolv, hostname, IPV4_FIRST); + test_ctx->resolv, hostname, IPV4_FIRST, + default_host_dbs); DEBUG(7, ("Sent resolv_gethostbyname\n")); if (req == NULL) { ret = ENOMEM; @@ -498,7 +538,8 @@ START_TEST(test_resolv_free_context) } req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, - test_ctx->resolv, hostname, IPV4_FIRST); + test_ctx->resolv, hostname, IPV4_FIRST, + default_host_dbs); DEBUG(7, ("Sent resolv_gethostbyname\n")); if (req == NULL) { fail("Error calling resolv_gethostbyname_send"); @@ -645,7 +686,8 @@ START_TEST(test_resolv_free_req) check_leaks_push(test_ctx); req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, - test_ctx->resolv, hostname, IPV4_FIRST); + test_ctx->resolv, hostname, IPV4_FIRST, + default_host_dbs); DEBUG(7, ("Sent resolv_gethostbyname\n")); if (req == NULL) { fail("Error calling resolv_gethostbyname_send"); -- cgit