X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=src%2Fnetwork%2Flookup_name.c;h=0225a9346b8d285e16b69b2e8d0a63ec2e3ebeda;hb=6fef8cafbd0f6f185897bc87feb1ff66e2e204e1;hp=02920930392475fba29d6e10339e7a6a0f378450;hpb=d85d261ee601f7ccf0bb19c38c69e406a664fe8e;p=musl diff --git a/src/network/lookup_name.c b/src/network/lookup_name.c index 02920930..0225a934 100644 --- a/src/network/lookup_name.c +++ b/src/network/lookup_name.c @@ -1,11 +1,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include "lookup.h" #include "stdio_impl.h" #include "syscall.h" @@ -13,7 +16,7 @@ static int is_valid_hostname(const char *host) { const unsigned char *s; - if (strnlen(host, 256)-1 > 254 || mbstowcs(0, host, 0) > 255) return 0; + if (strnlen(host, 255)-1 >= 254 || mbstowcs(0, host, 0) == -1) return 0; for (s=(void *)host; *s>=0x80 || *s=='.' || *s=='-' || isalnum(*s); s++); return !*s; } @@ -36,23 +39,9 @@ static int name_from_null(struct address buf[static 2], const char *name, int fa return cnt; } -int __inet_aton(const char *, struct in_addr *); - static int name_from_numeric(struct address buf[static 1], const char *name, int family) { - struct in_addr a4; - struct in6_addr a6; - if (family != AF_INET6 && __inet_aton(name, &a4)>0) { - memcpy(&buf[0].addr, &a4, sizeof a4); - buf[0].family = AF_INET; - return 1; - } - if (family != AF_INET && inet_pton(AF_INET6, name, &a6)>0) { - memcpy(&buf[0].addr, &a6, sizeof a6); - buf[0].family = AF_INET6; - return 1; - } - return 0; + return __lookup_ipliteral(buf, name, family); } static int name_from_hosts(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family) @@ -110,11 +99,13 @@ static int dns_parse_callback(void *c, int rr, const void *data, int len, const case RR_A: if (len != 4) return -1; ctx->addrs[ctx->cnt].family = AF_INET; + ctx->addrs[ctx->cnt].scopeid = 0; memcpy(ctx->addrs[ctx->cnt++].addr, data, 4); break; case RR_AAAA: if (len != 16) return -1; ctx->addrs[ctx->cnt].family = AF_INET6; + ctx->addrs[ctx->cnt].scopeid = 0; memcpy(ctx->addrs[ctx->cnt++].addr, data, 16); break; case RR_CNAME: @@ -157,14 +148,89 @@ 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; *canon = 0; if (name) { - size_t l; - if ((l = strnlen(name, 256))-1 > 254) + /* reject empty name and check len so it fits into temp bufs */ + size_t l = strnlen(name, 255); + if (l-1 >= 254) return EAI_NONAME; memcpy(canon, name, l+1); } @@ -179,10 +245,10 @@ int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], c /* Try each backend until there's at least one result. */ cnt = name_from_null(buf, name, family, flags); - if (cnt<=0) cnt = name_from_numeric(buf, name, family); - if (cnt<=0 && !(flags & AI_NUMERICHOST)) { + if (!cnt) cnt = name_from_numeric(buf, name, family); + if (!cnt && !(flags & AI_NUMERICHOST)) { cnt = name_from_hosts(buf, canon, name, family); - if (cnt<=0) cnt = name_from_dns(buf, canon, name, family); + if (!cnt) cnt = name_from_dns(buf, canon, name, family); } if (cnt<=0) return cnt ? cnt : EAI_NONAME; @@ -204,10 +270,68 @@ int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], c if (buf[i].family != AF_INET) continue; memcpy(buf[i].addr+12, buf[i].addr, 4); memcpy(buf[i].addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12); - buf[i].scopeid = 0; buf[i].family = AF_INET6; } } + /* 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; ilabel; + 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; }