aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ares/CHANGES2
-rw-r--r--ares/ares_gethostbyaddr.c5
-rw-r--r--ares/ares_gethostbyname.c72
-rw-r--r--ares/ares_init.c29
-rw-r--r--ares/ares_private.h10
5 files changed, 95 insertions, 23 deletions
diff --git a/ares/CHANGES b/ares/CHANGES
index 6f8ce3045..8acbf0811 100644
--- a/ares/CHANGES
+++ b/ares/CHANGES
@@ -2,6 +2,8 @@
* April 9
+- Made sortlist support IPv6 (this can probably use some testing).
+
- Made sortlist support CIDR matching for IPv4.
* April 8
diff --git a/ares/ares_gethostbyaddr.c b/ares/ares_gethostbyaddr.c
index bd63d1a95..a415e36db 100644
--- a/ares/ares_gethostbyaddr.c
+++ b/ares/ares_gethostbyaddr.c
@@ -39,11 +39,6 @@
#undef WIN32
#endif
-union ares_addr {
- struct in_addr addr4;
- struct in6_addr addr6;
-};
-
struct addr_query {
/* Arguments passed to ares_gethostbyaddr() */
ares_channel channel;
diff --git a/ares/ares_gethostbyname.c b/ares/ares_gethostbyname.c
index afd4c715e..daae5dbf6 100644
--- a/ares/ares_gethostbyname.c
+++ b/ares/ares_gethostbyname.c
@@ -12,7 +12,7 @@
* this software for any purpose. It is provided "as is"
* without express or implied warranty.
*/
-/* TODO: IPv6 sortlist */
+
#include "setup.h"
#include <sys/types.h>
@@ -60,8 +60,12 @@ static int fake_hostent(const char *name, int family, ares_host_callback callbac
static int file_lookup(const char *name, int family, struct hostent **host);
static void sort_addresses(struct hostent *host, struct apattern *sortlist,
int nsort);
+static void sort6_addresses(struct hostent *host, struct apattern *sortlist,
+ int nsort);
static int get_address_index(struct in_addr *addr, struct apattern *sortlist,
int nsort);
+static int get6_address_index(struct in6_addr *addr, struct apattern *sortlist,
+ int nsort);
void ares_gethostbyname(ares_channel channel, const char *name, int family,
ares_host_callback callback, void *arg)
@@ -154,10 +158,8 @@ static void host_callback(void *arg, int status, unsigned char *abuf, int alen)
else if (hquery->family == AF_INET6)
{
status = ares_parse_aaaa_reply(abuf, alen, &host);
-#if 0
if (host && channel->nsort)
- sort_addresses(host, channel->sortlist, channel->nsort);
-#endif
+ sort6_addresses(host, channel->sortlist, channel->nsort);
}
end_hquery(hquery, status, host);
}
@@ -331,12 +333,66 @@ static int get_address_index(struct in_addr *addr, struct apattern *sortlist,
for (i = 0; i < nsort; i++)
{
- if (sortlist[i].type = PATTERN_MASK)
- if ((addr->s_addr & sortlist[i].mask.addr.s_addr) == sortlist[i].addr.s_addr)
- break;
+ if (sortlist[i].family != AF_INET)
+ continue;
+ if (sortlist[i].type == PATTERN_MASK)
+ {
+ if ((addr->s_addr & sortlist[i].mask.addr.addr4.s_addr)
+ == sortlist[i].addr.addr4.s_addr)
+ break;
+ }
else
- if (!ares_bitncmp(&addr->s_addr, &sortlist[i].addr.s_addr, sortlist[i].mask.bits))
+ {
+ if (!ares_bitncmp(&addr->s_addr, &sortlist[i].addr.addr4.s_addr,
+ sortlist[i].mask.bits))
+ break;
+ }
+ }
+ return i;
+}
+
+static void sort6_addresses(struct hostent *host, struct apattern *sortlist,
+ int nsort)
+{
+ struct 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 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 in6_addr));
+ ind2 = get6_address_index(&a2, sortlist, nsort);
+ if (ind2 <= ind1)
+ break;
+ memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in6_addr));
+ }
+ memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in6_addr));
+ }
+}
+
+/* Find the first entry in sortlist which matches addr. Return nsort
+ * if none of them match.
+ */
+static int get6_address_index(struct in6_addr *addr, struct apattern *sortlist,
+ int nsort)
+{
+ int i;
+
+ for (i = 0; i < nsort; i++)
+ {
+ if (sortlist[i].family != AF_INET6)
+ continue;
+ if (!ares_bitncmp(&addr->s6_addr, &sortlist[i].addr.addr6.s6_addr, sortlist[i].mask.bits))
break;
}
return i;
}
+
diff --git a/ares/ares_init.c b/ares/ares_init.c
index 980e3d14e..c72033ba6 100644
--- a/ares/ares_init.c
+++ b/ares/ares_init.c
@@ -830,7 +830,7 @@ static int config_nameserver(struct server_state **servers, int *nservers,
static int config_sortlist(struct apattern **sortlist, int *nsort,
const char *str)
{
- struct apattern pat, *newsort;
+ struct apattern pat;
const char *q;
/* Add sortlist entries. */
@@ -857,26 +857,39 @@ static int config_sortlist(struct apattern **sortlist, int *nsort,
else
ipbufpfx[0] = 0;
/* Lets see if it is CIDR */
+ /* First we'll try IPv6 */
+ if ((bits = ares_inet_net_pton(AF_INET6, ipbufpfx ? ipbufpfx : ipbuf, &pat.addr.addr6,
+ sizeof(pat.addr.addr6))) > 0)
+ {
+ pat.type = PATTERN_CIDR;
+ pat.mask.bits = bits;
+ pat.family = AF_INET6;
+ if (!sortlist_alloc(sortlist, nsort, &pat))
+ return ARES_ENOMEM;
+ }
if (ipbufpfx &&
- (bits = ares_inet_net_pton(AF_INET, ipbufpfx, &pat.addr, sizeof(pat.addr))) > 0)
+ (bits = ares_inet_net_pton(AF_INET, ipbufpfx, &pat.addr.addr4,
+ sizeof(pat.addr.addr4))) > 0)
{
pat.type = PATTERN_CIDR;
pat.mask.bits = bits;
+ pat.family = AF_INET;
if (!sortlist_alloc(sortlist, nsort, &pat))
return ARES_ENOMEM;
}
/* See if it is just a regular IP */
- else if (ip_addr(ipbuf, (int)(q-str), &pat.addr) == 0)
+ else if (ip_addr(ipbuf, (int)(q-str), &pat.addr.addr4) == 0)
{
if (ipbufpfx)
{
memcpy(ipbuf, str, (int)(q-str));
ipbuf[(int)(q-str)] = 0;
- if (ip_addr(ipbuf, (int)(q - str), &pat.mask.addr) != 0)
+ if (ip_addr(ipbuf, (int)(q - str), &pat.mask.addr.addr4) != 0)
natural_mask(&pat);
}
else
natural_mask(&pat);
+ pat.family = AF_INET;
pat.type = PATTERN_MASK;
if (!sortlist_alloc(sortlist, nsort, &pat))
return ARES_ENOMEM;
@@ -1030,16 +1043,16 @@ static void natural_mask(struct apattern *pat)
/* Store a host-byte-order copy of pat in a struct in_addr. Icky,
* but portable.
*/
- addr.s_addr = ntohl(pat->addr.s_addr);
+ addr.s_addr = ntohl(pat->addr.addr4.s_addr);
/* This is out of date in the CIDR world, but some people might
* still rely on it.
*/
if (IN_CLASSA(addr.s_addr))
- pat->mask.addr.s_addr = htonl(IN_CLASSA_NET);
+ pat->mask.addr.addr4.s_addr = htonl(IN_CLASSA_NET);
else if (IN_CLASSB(addr.s_addr))
- pat->mask.addr.s_addr = htonl(IN_CLASSB_NET);
+ pat->mask.addr.addr4.s_addr = htonl(IN_CLASSB_NET);
else
- pat->mask.addr.s_addr = htonl(IN_CLASSC_NET);
+ pat->mask.addr.addr4.s_addr = htonl(IN_CLASSC_NET);
}
#endif
diff --git a/ares/ares_private.h b/ares/ares_private.h
index 723afb387..bcf2e8e91 100644
--- a/ares/ares_private.h
+++ b/ares/ares_private.h
@@ -127,13 +127,19 @@ struct query {
#define PATTERN_MASK 0x1
#define PATTERN_CIDR 0x2
+union ares_addr {
+ struct in_addr addr4;
+ struct in6_addr addr6;
+};
+
struct apattern {
- struct in_addr addr;
+ union ares_addr addr;
union
{
- struct in_addr addr;
+ union ares_addr addr;
unsigned short bits;
} mask;
+ int family;
unsigned short type;
};