summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2011-06-10 14:01:17 +0200
committerStephen Gallagher <sgallagh@redhat.com>2011-07-13 12:35:45 -0400
commit90f150f678347f1f73ee4280cd523021e307d861 (patch)
treefe93aa07c20d98522fd0c359f50c69b7519f49e0
parent401bfa7e4f8c2ed555576576881a2bd9622c1e2e (diff)
downloadsssd-90f150f678347f1f73ee4280cd523021e307d861.tar.gz
sssd-90f150f678347f1f73ee4280cd523021e307d861.tar.xz
sssd-90f150f678347f1f73ee4280cd523021e307d861.zip
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.
-rw-r--r--src/external/libcares.m43
-rw-r--r--src/providers/data_provider_fo.c9
-rw-r--r--src/providers/fail_over.c38
-rw-r--r--src/providers/fail_over.h2
-rw-r--r--src/providers/ipa/ipa_common.c4
-rw-r--r--src/providers/krb5/krb5_common.c2
-rw-r--r--src/providers/ldap/ldap_common.c4
-rw-r--r--src/resolv/async_resolv.c881
-rw-r--r--src/resolv/async_resolv.h53
-rw-r--r--src/tests/fail_over-tests.c6
-rw-r--r--src/tests/resolv-tests.c122
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 <ares.h>])
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");