X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=src%2Fnetwork%2Flookup_name.c;h=f268bcda6aba1ca84660799f489df435d4623946;hb=9b132e556774c744f9052581d2d8d0fab417e97c;hp=e1b583ee0c348e15403b0726bfb7b088f1146169;hpb=bb9af59bba5b72b90c38d28809c30b31933c64d5;p=musl diff --git a/src/network/lookup_name.c b/src/network/lookup_name.c index e1b583ee..f268bcda 100644 --- a/src/network/lookup_name.c +++ b/src/network/lookup_name.c @@ -1,20 +1,24 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include #include "lookup.h" #include "stdio_impl.h" #include "syscall.h" -#include "__dns.h" 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; } @@ -39,29 +43,24 @@ static int name_from_null(struct address buf[static 2], const char *name, int fa 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) { char line[512]; size_t l = strlen(name); - int cnt = 0; + int cnt = 0, badfam = 0, have_canon = 0; unsigned char _buf[1032]; FILE _f, *f = __fopen_rb_ca("/etc/hosts", &_f, _buf, sizeof _buf); - if (!f) return 0; + if (!f) switch (errno) { + case ENOENT: + case ENOTDIR: + case EACCES: + return 0; + default: + return EAI_SYSTEM; + } while (fgets(line, sizeof line, f) && cnt < MAXADDRS) { char *p, *z; @@ -73,43 +72,236 @@ static int name_from_hosts(struct address buf[static MAXADDRS], char canon[stati /* Isolate IP address to parse */ for (p=line; *p && !isspace(*p); p++); *p++ = 0; - if (name_from_numeric(buf+cnt, line, family)) + switch (name_from_numeric(buf+cnt, line, family)) { + case 1: cnt++; + break; + case 0: + continue; + default: + badfam = EAI_NODATA; + break; + } + + if (have_canon) continue; /* Extract first name as canonical name */ for (; *p && isspace(*p); p++); for (z=p; *z && !isspace(*z); z++); *z = 0; - if (is_valid_hostname(p)) memcpy(canon, p, z-p+1); + if (is_valid_hostname(p)) { + have_canon = 1; + memcpy(canon, p, z-p+1); + } } __fclose_ca(f); - return cnt; + return cnt ? cnt : badfam; } -static int name_from_dns(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family) +struct dpc_ctx { + struct address *addrs; + char *canon; + int cnt; + int rrtype; +}; + +#define RR_A 1 +#define RR_CNAME 5 +#define RR_AAAA 28 + +#define ABUF_SIZE 768 + +static int dns_parse_callback(void *c, int rr, const void *data, int len, const void *packet, int plen) { - unsigned char reply[1024] = { 0 }, *p = reply; char tmp[256]; - int i, cnt = 0; + int family; + struct dpc_ctx *ctx = c; + if (rr == RR_CNAME) { + if (__dn_expand(packet, (const unsigned char *)packet + plen, + data, tmp, sizeof tmp) > 0 && is_valid_hostname(tmp)) + strcpy(ctx->canon, tmp); + return 0; + } + if (ctx->cnt >= MAXADDRS) return 0; + if (rr != ctx->rrtype) return 0; + switch (rr) { + case RR_A: + if (len != 4) return -1; + family = AF_INET; + break; + case RR_AAAA: + if (len != 16) return -1; + family = AF_INET6; + break; + } + ctx->addrs[ctx->cnt].family = family; + ctx->addrs[ctx->cnt].scopeid = 0; + memcpy(ctx->addrs[ctx->cnt++].addr, data, len); + return 0; +} - /* Perform one or more DNS queries for host */ - int result = __dns_query(reply, name, family, 0); - if (result < 0) return result; +static int name_from_dns(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family, const struct resolvconf *conf) +{ + unsigned char qbuf[2][280], abuf[2][ABUF_SIZE]; + const unsigned char *qp[2] = { qbuf[0], qbuf[1] }; + unsigned char *ap[2] = { abuf[0], abuf[1] }; + int qlens[2], alens[2], qtypes[2]; + int i, nq = 0; + struct dpc_ctx ctx = { .addrs = buf, .canon = canon }; + static const struct { int af; int rr; } afrr[2] = { + { .af = AF_INET6, .rr = RR_A }, + { .af = AF_INET, .rr = RR_AAAA }, + }; - for (i=0; i=0; i--) { + ctx.rrtype = qtypes[i]; + __dns_parse(abuf[i], alens[i], dns_parse_callback, &ctx); + } + + if (ctx.cnt) return ctx.cnt; + return EAI_NODATA; +} + +static int name_from_dns_search(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family) +{ + char search[256]; + struct resolvconf conf; + size_t l, dots; + char *p, *z; + + if (__get_resolv_conf(&conf, search, sizeof search) < 0) return -1; + + /* Count dots, suppress search when >=ndots or name ends in + * a dot, which is an explicit request for global scope. */ + for (dots=l=0; name[l]; l++) if (name[l]=='.') dots++; + if (dots >= conf.ndots || name[l-1]=='.') *search = 0; + + /* Strip final dot for canon, fail if multiple trailing dots. */ + if (name[l-1]=='.') l--; + if (!l || name[l-1]=='.') return EAI_NONAME; + + /* This can never happen; the caller already checked length. */ + if (l >= 256) return EAI_NONAME; + + /* Name with search domain appended is setup in canon[]. This both + * provides the desired default canonical name (if the requested + * name is not a CNAME record) and serves as a buffer for passing + * the full requested name to name_from_dns. */ + memcpy(canon, name, l); + canon[l] = '.'; + + for (p=search; *p; p=z) { + for (; isspace(*p); p++); + for (z=p; *z && !isspace(*z); z++); + if (z==p) break; + if (z-p < 256 - l - 1) { + memcpy(canon+l+1, p, z-p); + canon[z-p+1+l] = 0; + int cnt = name_from_dns(buf, canon, canon, family, &conf); + if (cnt) return cnt; } - p += 512; } - __dns_get_rr(tmp, 0, 256, 1, reply, RR_CNAME, 1); - if (is_valid_hostname(tmp)) strcpy(canon, tmp); - return cnt; + + canon[l] = 0; + return name_from_dns(buf, canon, name, family, &conf); +} + +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) @@ -118,8 +310,9 @@ int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], c *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); } @@ -134,10 +327,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_search(buf, canon, name, family); } if (cnt<=0) return cnt ? cnt : EAI_NONAME; @@ -159,10 +352,85 @@ 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; ilabel; + int dprec = dpolicy->prec; + int prefixlen = 0; + int fd = socket(family, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP); + if (fd >= 0) { + if (!connect(fd, da, dalen)) { + key |= DAS_USABLE; + if (!getsockname(fd, sa, &salen)) { + if (family == AF_INET) memcpy( + sa6.sin6_addr.s6_addr+12, + &sa4.sin_addr, 4); + if (dscope == scopeof(&sa6.sin6_addr)) + key |= DAS_MATCHINGSCOPE; + if (dlabel == labelof(&sa6.sin6_addr)) + key |= DAS_MATCHINGLABEL; + prefixlen = prefixmatch(&sa6.sin6_addr, + &da6.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; }