|
|
|
@ -7,6 +7,8 @@ |
|
|
|
#include <stdlib.h> |
|
|
|
#include <string.h> |
|
|
|
#include <fcntl.h> |
|
|
|
#include <unistd.h> |
|
|
|
#include <pthread.h> |
|
|
|
#include "lookup.h" |
|
|
|
#include "stdio_impl.h" |
|
|
|
#include "syscall.h" |
|
|
|
@ -146,6 +148,80 @@ static int name_from_dns(struct address buf[static MAXADDRS], char canon[static |
|
|
|
return EAI_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
static const struct policy { |
|
|
|
unsigned char addr[16]; |
|
|
|
unsigned char len, mask; |
|
|
|
unsigned char prec, label; |
|
|
|
} defpolicy[] = { |
|
|
|
{ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1", 15, 0xff, 50, 0 }, |
|
|
|
{ "\0\0\0\0\0\0\0\0\0\0\xff\xff", 11, 0xff, 35, 4 }, |
|
|
|
{ "\x20\2", 1, 0xff, 30, 2 }, |
|
|
|
{ "\x20\1", 3, 0xff, 5, 5 }, |
|
|
|
{ "\xfc", 0, 0xfe, 3, 13 }, |
|
|
|
#if 0 |
|
|
|
/* These are deprecated and/or returned to the address
|
|
|
|
* pool, so despite the RFC, treating them as special |
|
|
|
* is probably wrong. */ |
|
|
|
{ "", 11, 0xff, 1, 3 }, |
|
|
|
{ "\xfe\xc0", 1, 0xc0, 1, 11 }, |
|
|
|
{ "\x3f\xfe", 1, 0xff, 1, 12 }, |
|
|
|
#endif |
|
|
|
/* Last rule must match all addresses to stop loop. */ |
|
|
|
{ "", 0, 0, 40, 1 }, |
|
|
|
}; |
|
|
|
|
|
|
|
static const struct policy *policyof(const struct in6_addr *a) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
for (i=0; ; i++) { |
|
|
|
if (memcmp(a->s6_addr, defpolicy[i].addr, defpolicy[i].len)) |
|
|
|
continue; |
|
|
|
if ((a->s6_addr[defpolicy[i].len] & defpolicy[i].mask) |
|
|
|
!= defpolicy[i].addr[defpolicy[i].len]) |
|
|
|
continue; |
|
|
|
return defpolicy+i; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static int labelof(const struct in6_addr *a) |
|
|
|
{ |
|
|
|
return policyof(a)->label; |
|
|
|
} |
|
|
|
|
|
|
|
static int scopeof(const struct in6_addr *a) |
|
|
|
{ |
|
|
|
if (IN6_IS_ADDR_MULTICAST(a)) return a->s6_addr[1] & 15; |
|
|
|
if (IN6_IS_ADDR_LINKLOCAL(a)) return 2; |
|
|
|
if (IN6_IS_ADDR_LOOPBACK(a)) return 2; |
|
|
|
if (IN6_IS_ADDR_SITELOCAL(a)) return 5; |
|
|
|
return 14; |
|
|
|
} |
|
|
|
|
|
|
|
static int prefixmatch(const struct in6_addr *s, const struct in6_addr *d) |
|
|
|
{ |
|
|
|
/* FIXME: The common prefix length should be limited to no greater
|
|
|
|
* than the nominal length of the prefix portion of the source |
|
|
|
* address. However the definition of the source prefix length is |
|
|
|
* not clear and thus this limiting is not yet implemented. */ |
|
|
|
unsigned i; |
|
|
|
for (i=0; i<128 && !((s->s6_addr[i/8]^d->s6_addr[i/8])&(128>>(i%8))); i++); |
|
|
|
return i; |
|
|
|
} |
|
|
|
|
|
|
|
#define DAS_USABLE 0x40000000 |
|
|
|
#define DAS_MATCHINGSCOPE 0x20000000 |
|
|
|
#define DAS_MATCHINGLABEL 0x10000000 |
|
|
|
#define DAS_PREC_SHIFT 20 |
|
|
|
#define DAS_SCOPE_SHIFT 16 |
|
|
|
#define DAS_PREFIX_SHIFT 8 |
|
|
|
#define DAS_ORDER_SHIFT 0 |
|
|
|
|
|
|
|
static int addrcmp(const void *_a, const void *_b) |
|
|
|
{ |
|
|
|
const struct address *a = _a, *b = _b; |
|
|
|
return b->sortkey - a->sortkey; |
|
|
|
} |
|
|
|
|
|
|
|
int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family, int flags) |
|
|
|
{ |
|
|
|
int cnt = 0, i, j; |
|
|
|
@ -198,5 +274,64 @@ int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], c |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* No further processing is needed if there are fewer than 2
|
|
|
|
* results or if there are only IPv4 results. */ |
|
|
|
if (cnt<2 || family==AF_INET) return cnt; |
|
|
|
for (i=0; buf[i].family == AF_INET; i++) |
|
|
|
if (i==cnt) return cnt; |
|
|
|
|
|
|
|
int cs; |
|
|
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); |
|
|
|
|
|
|
|
/* The following implements a subset of RFC 3484/6724 destination
|
|
|
|
* address selection by generating a single 31-bit sort key for |
|
|
|
* each address. Rules 3, 4, and 7 are omitted for having |
|
|
|
* excessive runtime and code size cost and dubious benefit. |
|
|
|
* So far the label/precedence table cannot be customized. */ |
|
|
|
for (i=0; i<cnt; i++) { |
|
|
|
int key = 0; |
|
|
|
struct sockaddr_in6 sa, da = { |
|
|
|
.sin6_family = AF_INET6, |
|
|
|
.sin6_scope_id = buf[i].scopeid, |
|
|
|
.sin6_port = 65535 |
|
|
|
}; |
|
|
|
if (buf[i].family == AF_INET6) { |
|
|
|
memcpy(da.sin6_addr.s6_addr, buf[i].addr, 16); |
|
|
|
} else { |
|
|
|
memcpy(da.sin6_addr.s6_addr, |
|
|
|
"\0\0\0\0\0\0\0\0\0\0\xff\xff", 12); |
|
|
|
memcpy(da.sin6_addr.s6_addr+12, buf[i].addr, 4); |
|
|
|
} |
|
|
|
const struct policy *dpolicy = policyof(&da.sin6_addr); |
|
|
|
int dscope = scopeof(&da.sin6_addr); |
|
|
|
int dlabel = dpolicy->label; |
|
|
|
int dprec = dpolicy->prec; |
|
|
|
int prefixlen = 0; |
|
|
|
int fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP); |
|
|
|
if (fd >= 0) { |
|
|
|
if (!connect(fd, (void *)&da, sizeof da)) { |
|
|
|
key |= DAS_USABLE; |
|
|
|
if (!getsockname(fd, (void *)&sa, |
|
|
|
&(socklen_t){sizeof sa})) { |
|
|
|
if (dscope == scopeof(&sa.sin6_addr)) |
|
|
|
key |= DAS_MATCHINGSCOPE; |
|
|
|
if (dlabel == labelof(&sa.sin6_addr)) |
|
|
|
key |= DAS_MATCHINGLABEL; |
|
|
|
prefixlen = prefixmatch(&sa.sin6_addr, |
|
|
|
&da.sin6_addr); |
|
|
|
} |
|
|
|
} |
|
|
|
close(fd); |
|
|
|
} |
|
|
|
key |= dprec << DAS_PREC_SHIFT; |
|
|
|
key |= (15-dscope) << DAS_SCOPE_SHIFT; |
|
|
|
key |= prefixlen << DAS_PREFIX_SHIFT; |
|
|
|
key |= (MAXADDRS-i) << DAS_ORDER_SHIFT; |
|
|
|
buf[i].sortkey = key; |
|
|
|
} |
|
|
|
qsort(buf, cnt, sizeof *buf, addrcmp); |
|
|
|
|
|
|
|
pthread_setcancelstate(cs, 0); |
|
|
|
|
|
|
|
return cnt; |
|
|
|
} |
|
|
|
|