summaryrefslogtreecommitdiff
path: root/src/lib/ares_gethostbyname.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/ares_gethostbyname.c')
-rw-r--r--src/lib/ares_gethostbyname.c466
1 files changed, 132 insertions, 334 deletions
diff --git a/src/lib/ares_gethostbyname.c b/src/lib/ares_gethostbyname.c
index 4e41898..8c71cc6 100644
--- a/src/lib/ares_gethostbyname.c
+++ b/src/lib/ares_gethostbyname.c
@@ -1,4 +1,3 @@
-
/* Copyright 1998, 2011, 2013 by the Massachusetts Institute of Technology.
*
* Permission to use, copy, modify, and distribute this
@@ -25,14 +24,8 @@
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
-#ifdef HAVE_ARPA_NAMESER_H
-# include <arpa/nameser.h>
-#else
-# include "nameser.h"
-#endif
-#ifdef HAVE_ARPA_NAMESER_COMPAT_H
-# include <arpa/nameser_compat.h>
-#endif
+
+#include "ares_nameser.h"
#ifdef HAVE_STRINGS_H
#include <strings.h>
@@ -45,30 +38,6 @@
#include "ares_nowarn.h"
#include "ares_private.h"
-#ifdef WATT32
-#undef WIN32
-#endif
-
-struct host_query {
- /* Arguments passed to ares_gethostbyname() */
- ares_channel channel;
- char *name;
- ares_host_callback callback;
- void *arg;
- int sent_family; /* this family is what was is being used */
- int want_family; /* this family is what is asked for in the API */
- const char *remaining_lookups;
- int timeouts;
-};
-
-static void next_lookup(struct host_query *hquery, int status_code);
-static void host_callback(void *arg, int status, int timeouts,
- unsigned char *abuf, int alen);
-static void end_hquery(struct host_query *hquery, int status,
- struct hostent *host);
-static int fake_hostent(const char *name, int family,
- ares_host_callback callback, void *arg);
-static int file_lookup(const char *name, int family, struct hostent **host);
static void sort_addresses(struct hostent *host,
const struct apattern *sortlist, int nsort);
static void sort6_addresses(struct hostent *host,
@@ -78,252 +47,182 @@ static int get_address_index(const struct in_addr *addr,
static int get6_address_index(const struct ares_in6_addr *addr,
const struct apattern *sortlist, int nsort);
-void ares_gethostbyname(ares_channel channel, const char *name, int family,
- ares_host_callback callback, void *arg)
+struct host_query {
+ ares_host_callback callback;
+ void *arg;
+ ares_channel channel;
+};
+
+static void ares_gethostbyname_callback(void *arg, int status, int timeouts,
+ struct ares_addrinfo *result)
{
- struct host_query *hquery;
-
- /* Right now we only know how to look up Internet addresses - and unspec
- means try both basically. */
- switch (family) {
- case AF_INET:
- case AF_INET6:
- case AF_UNSPEC:
- break;
- default:
- callback(arg, ARES_ENOTIMP, 0, NULL);
- return;
- }
+ struct hostent *hostent = NULL;
+ struct host_query *ghbn_arg = arg;
- /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
- if (ares__is_onion_domain(name))
+ if (status == ARES_SUCCESS)
{
- callback(arg, ARES_ENOTFOUND, 0, NULL);
- return;
+ status = ares__addrinfo2hostent(result, AF_UNSPEC, &hostent);
+ }
+
+ /* addrinfo2hostent will only return ENODATA if there are no addresses _and_
+ * no cname/aliases. However, gethostbyname will return ENODATA even if there
+ * is cname/alias data */
+ if (status == ARES_SUCCESS && hostent &&
+ (!hostent->h_addr_list || !hostent->h_addr_list[0]))
+ {
+ status = ARES_ENODATA;
}
- if (fake_hostent(name, family, callback, arg))
+ if (status == ARES_SUCCESS && ghbn_arg->channel->nsort && hostent)
+ {
+ if (hostent->h_addrtype == AF_INET6)
+ sort6_addresses(hostent, ghbn_arg->channel->sortlist,
+ ghbn_arg->channel->nsort);
+ if (hostent->h_addrtype == AF_INET)
+ sort_addresses(hostent, ghbn_arg->channel->sortlist,
+ ghbn_arg->channel->nsort);
+ }
+
+ ghbn_arg->callback(ghbn_arg->arg, status, timeouts, hostent);
+
+ ares_freeaddrinfo(result);
+ ares_free(ghbn_arg);
+ ares_free_hostent(hostent);
+}
+
+void ares_gethostbyname(ares_channel channel, const char *name, int family,
+ ares_host_callback callback, void *arg)
+{
+ const struct ares_addrinfo_hints hints = { ARES_AI_CANONNAME, family, 0, 0 };
+ struct host_query *ghbn_arg;
+
+ if (!callback)
return;
- /* Allocate and fill in the host query structure. */
- hquery = ares_malloc(sizeof(struct host_query));
- if (!hquery)
+ ghbn_arg = ares_malloc(sizeof(*ghbn_arg));
+ if (!ghbn_arg)
{
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
- hquery->channel = channel;
- hquery->name = ares_strdup(name);
- hquery->want_family = family;
- hquery->sent_family = -1; /* nothing is sent yet */
- if (!hquery->name) {
- ares_free(hquery);
- callback(arg, ARES_ENOMEM, 0, NULL);
- return;
- }
- hquery->callback = callback;
- hquery->arg = arg;
- hquery->remaining_lookups = channel->lookups;
- hquery->timeouts = 0;
- /* Start performing lookups according to channel->lookups. */
- next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */);
+ ghbn_arg->callback=callback;
+ ghbn_arg->arg=arg;
+ ghbn_arg->channel=channel;
+
+ ares_getaddrinfo(channel, name, NULL, &hints, ares_gethostbyname_callback,
+ ghbn_arg);
}
-static void next_lookup(struct host_query *hquery, int status_code)
+
+static void sort_addresses(struct hostent *host,
+ const struct apattern *sortlist, int nsort)
{
- const char *p;
- struct hostent *host;
- int status = status_code;
+ struct in_addr a1, a2;
+ int i1, i2, ind1, ind2;
- for (p = hquery->remaining_lookups; *p; p++)
+ /* This is a simple insertion sort, not optimized at all. i1 walks
+ * through the address list, with the loop invariant that everything
+ * to the left of i1 is sorted. In the loop body, the value at i1 is moved
+ * back through the list (via i2) until it is in sorted order.
+ */
+ for (i1 = 0; host->h_addr_list[i1]; i1++)
{
- switch (*p)
+ memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr));
+ ind1 = get_address_index(&a1, sortlist, nsort);
+ for (i2 = i1 - 1; i2 >= 0; i2--)
{
- case 'b':
- /* DNS lookup */
- hquery->remaining_lookups = p + 1;
- if ((hquery->want_family == AF_INET6) ||
- (hquery->want_family == AF_UNSPEC)) {
- /* if inet6 or unspec, start out with AAAA */
- hquery->sent_family = AF_INET6;
- ares_search(hquery->channel, hquery->name, C_IN, T_AAAA,
- host_callback, hquery);
- }
- else {
- hquery->sent_family = AF_INET;
- ares_search(hquery->channel, hquery->name, C_IN, T_A,
- host_callback, hquery);
- }
- return;
-
- case 'f':
- /* Host file lookup */
- status = file_lookup(hquery->name, hquery->want_family, &host);
-
- /* this status check below previously checked for !ARES_ENOTFOUND,
- but we should not assume that this single error code is the one
- that can occur, as that is in fact no longer the case */
- if (status == ARES_SUCCESS)
- {
- end_hquery(hquery, status, host);
- return;
- }
- status = status_code; /* Use original status code */
- break;
+ memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr));
+ ind2 = get_address_index(&a2, sortlist, nsort);
+ if (ind2 <= ind1)
+ break;
+ memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr));
}
+ memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr));
}
- end_hquery(hquery, status, NULL);
}
-static void host_callback(void *arg, int status, int timeouts,
- unsigned char *abuf, int alen)
+/* Find the first entry in sortlist which matches addr. Return nsort
+ * if none of them match.
+ */
+static int get_address_index(const struct in_addr *addr,
+ const struct apattern *sortlist,
+ int nsort)
{
- struct host_query *hquery = (struct host_query *) arg;
- ares_channel channel = hquery->channel;
- struct hostent *host = NULL;
+ int i;
- hquery->timeouts += timeouts;
- if (status == ARES_SUCCESS)
+ for (i = 0; i < nsort; i++)
{
- if (hquery->sent_family == AF_INET)
+ if (sortlist[i].family != AF_INET)
+ continue;
+ if (sortlist[i].type == PATTERN_MASK)
{
- status = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
- if (host && channel->nsort)
- sort_addresses(host, channel->sortlist, channel->nsort);
+ if ((addr->s_addr & sortlist[i].mask.addr4.s_addr)
+ == sortlist[i].addrV4.s_addr)
+ break;
}
- else if (hquery->sent_family == AF_INET6)
+ else
{
- status = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL);
- if ((status == ARES_ENODATA || status == ARES_EBADRESP ||
- (status == ARES_SUCCESS && host && host->h_addr_list[0] == NULL)) &&
- hquery->want_family == AF_UNSPEC) {
- /* The query returned something but either there were no AAAA
- records (e.g. just CNAME) or the response was malformed. Try
- looking up A instead. */
- if (host)
- ares_free_hostent(host);
- hquery->sent_family = AF_INET;
- ares_search(hquery->channel, hquery->name, C_IN, T_A,
- host_callback, hquery);
- return;
- }
- if (host && channel->nsort)
- sort6_addresses(host, channel->sortlist, channel->nsort);
+ if (!ares__bitncmp(&addr->s_addr, &sortlist[i].addrV4.s_addr,
+ sortlist[i].mask.bits))
+ break;
}
- if (status == ARES_SUCCESS && host && host->h_addr_list[0] == NULL)
- {
- /* The query returned something but had no A/AAAA record
- (even after potentially retrying AAAA with A)
- so we should treat this as an error */
- status = ARES_ENODATA;
- }
- end_hquery(hquery, status, host);
- }
- else if ((status == ARES_ENODATA || status == ARES_EBADRESP ||
- status == ARES_ETIMEOUT) && (hquery->sent_family == AF_INET6 &&
- hquery->want_family == AF_UNSPEC))
- {
- /* The AAAA query yielded no useful result. Now look up an A instead. */
- hquery->sent_family = AF_INET;
- ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback,
- hquery);
}
- else if (status == ARES_EDESTRUCTION)
- end_hquery(hquery, status, NULL);
- else
- next_lookup(hquery, status);
+ return i;
}
-static void end_hquery(struct host_query *hquery, int status,
- struct hostent *host)
+static void sort6_addresses(struct hostent *host,
+ const struct apattern *sortlist, int nsort)
{
- hquery->callback(hquery->arg, status, hquery->timeouts, host);
- if (host)
- ares_free_hostent(host);
- ares_free(hquery->name);
- ares_free(hquery);
-}
+ struct ares_in6_addr a1, a2;
+ int i1, i2, ind1, ind2;
-/* If the name looks like an IP address, fake up a host entry, end the
- * query immediately, and return true. Otherwise return false.
- */
-static int fake_hostent(const char *name, int family,
- ares_host_callback callback, void *arg)
-{
- struct hostent hostent;
- char *aliases[1] = { NULL };
- char *addrs[2];
- int result = 0;
- struct in_addr in;
- struct ares_in6_addr in6;
-
- if (family == AF_INET || family == AF_UNSPEC)
+ /* This is a simple insertion sort, not optimized at all. i1 walks
+ * through the address list, with the loop invariant that everything
+ * to the left of i1 is sorted. In the loop body, the value at i1 is moved
+ * back through the list (via i2) until it is in sorted order.
+ */
+ for (i1 = 0; host->h_addr_list[i1]; i1++)
{
- /* It only looks like an IP address if it's all numbers and dots. */
- int numdots = 0, valid = 1;
- const char *p;
- for (p = name; *p; p++)
+ memcpy(&a1, host->h_addr_list[i1], sizeof(struct ares_in6_addr));
+ ind1 = get6_address_index(&a1, sortlist, nsort);
+ for (i2 = i1 - 1; i2 >= 0; i2--)
{
- if (!ISDIGIT(*p) && *p != '.') {
- valid = 0;
+ memcpy(&a2, host->h_addr_list[i2], sizeof(struct ares_in6_addr));
+ ind2 = get6_address_index(&a2, sortlist, nsort);
+ if (ind2 <= ind1)
break;
- } else if (*p == '.') {
- numdots++;
- }
+ memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct ares_in6_addr));
}
-
- /* if we don't have 3 dots, it is illegal
- * (although inet_pton doesn't think so).
- */
- if (numdots != 3 || !valid) {
- result = 0;
- } else {
- result = (ares_inet_pton(AF_INET, name, &in) < 1 ? 0 : 1);
- }
-
- /*
- * Set address family in case of failure,
- * as we will try to convert it later afterwards
- */
- family = result ? AF_INET : AF_INET6;
+ memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr));
}
- if (family == AF_INET6)
- result = (ares_inet_pton(AF_INET6, name, &in6) < 1 ? 0 : 1);
+}
- if (!result)
- return 0;
+/* Find the first entry in sortlist which matches addr. Return nsort
+ * if none of them match.
+ */
+static int get6_address_index(const struct ares_in6_addr *addr,
+ const struct apattern *sortlist,
+ int nsort)
+{
+ int i;
- if (family == AF_INET)
- {
- hostent.h_length = (int)sizeof(struct in_addr);
- addrs[0] = (char *)&in;
- }
- else if (family == AF_INET6)
- {
- hostent.h_length = (int)sizeof(struct ares_in6_addr);
- addrs[0] = (char *)&in6;
- }
- /* Duplicate the name, to avoid a constness violation. */
- hostent.h_name = ares_strdup(name);
- if (!hostent.h_name)
+ for (i = 0; i < nsort; i++)
{
- callback(arg, ARES_ENOMEM, 0, NULL);
- return 1;
+ if (sortlist[i].family != AF_INET6)
+ continue;
+ if (!ares__bitncmp(addr, &sortlist[i].addrV6, sortlist[i].mask.bits))
+ break;
}
+ return i;
+}
- /* Fill in the rest of the host structure and terminate the query. */
- addrs[1] = NULL;
- hostent.h_aliases = aliases;
- hostent.h_addrtype = aresx_sitoss(family);
- hostent.h_addr_list = addrs;
- callback(arg, ARES_SUCCESS, 0, &hostent);
- ares_free((char *)(hostent.h_name));
- return 1;
-}
-/* This is an API method */
+static int file_lookup(const char *name, int family, struct hostent **host);
+
+/* I really have no idea why this is exposed as a public function, but since
+ * it is, we can't kill this legacy function. */
int ares_gethostbyname_file(ares_channel channel, const char *name,
int family, struct hostent **host)
{
@@ -437,104 +336,3 @@ static int file_lookup(const char *name, int family, struct hostent **host)
return status;
}
-static void sort_addresses(struct hostent *host,
- const struct apattern *sortlist, int nsort)
-{
- struct in_addr a1, a2;
- int i1, i2, ind1, ind2;
-
- /* This is a simple insertion sort, not optimized at all. i1 walks
- * through the address list, with the loop invariant that everything
- * to the left of i1 is sorted. In the loop body, the value at i1 is moved
- * back through the list (via i2) until it is in sorted order.
- */
- for (i1 = 0; host->h_addr_list[i1]; i1++)
- {
- memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr));
- ind1 = get_address_index(&a1, sortlist, nsort);
- for (i2 = i1 - 1; i2 >= 0; i2--)
- {
- memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr));
- ind2 = get_address_index(&a2, sortlist, nsort);
- if (ind2 <= ind1)
- break;
- memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr));
- }
- memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr));
- }
-}
-
-/* Find the first entry in sortlist which matches addr. Return nsort
- * if none of them match.
- */
-static int get_address_index(const struct in_addr *addr,
- const struct apattern *sortlist,
- int nsort)
-{
- int i;
-
- for (i = 0; i < nsort; i++)
- {
- if (sortlist[i].family != AF_INET)
- continue;
- if (sortlist[i].type == PATTERN_MASK)
- {
- if ((addr->s_addr & sortlist[i].mask.addr4.s_addr)
- == sortlist[i].addrV4.s_addr)
- break;
- }
- else
- {
- if (!ares__bitncmp(&addr->s_addr, &sortlist[i].addrV4.s_addr,
- sortlist[i].mask.bits))
- break;
- }
- }
- return i;
-}
-
-static void sort6_addresses(struct hostent *host,
- const struct apattern *sortlist, int nsort)
-{
- struct ares_in6_addr a1, a2;
- int i1, i2, ind1, ind2;
-
- /* This is a simple insertion sort, not optimized at all. i1 walks
- * through the address list, with the loop invariant that everything
- * to the left of i1 is sorted. In the loop body, the value at i1 is moved
- * back through the list (via i2) until it is in sorted order.
- */
- for (i1 = 0; host->h_addr_list[i1]; i1++)
- {
- memcpy(&a1, host->h_addr_list[i1], sizeof(struct ares_in6_addr));
- ind1 = get6_address_index(&a1, sortlist, nsort);
- for (i2 = i1 - 1; i2 >= 0; i2--)
- {
- memcpy(&a2, host->h_addr_list[i2], sizeof(struct ares_in6_addr));
- ind2 = get6_address_index(&a2, sortlist, nsort);
- if (ind2 <= ind1)
- break;
- memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct ares_in6_addr));
- }
- memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr));
- }
-}
-
-/* Find the first entry in sortlist which matches addr. Return nsort
- * if none of them match.
- */
-static int get6_address_index(const struct ares_in6_addr *addr,
- const struct apattern *sortlist,
- int nsort)
-{
- int i;
-
- for (i = 0; i < nsort; i++)
- {
- if (sortlist[i].family != AF_INET6)
- continue;
- if (!ares__bitncmp(addr, &sortlist[i].addrV6, sortlist[i].mask.bits))
- break;
- }
- return i;
-}