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 @@ -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 */ - if (ipbufpfx && - (bits = ares_inet_net_pton(AF_INET, ipbufpfx, &pat.addr, sizeof(pat.addr))) > 0) + /* 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.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; };