| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <netdb.h> |
| #include <net/if.h> |
| #include <arpa/inet.h> |
| #include <ctype.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <pthread.h> |
| #include <errno.h> |
| #include "lookup.h" |
| #include "stdio_impl.h" |
| #include "syscall.h" |
| |
| static int is_valid_hostname(const char *host) |
| { |
| const unsigned char *s; |
| 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; |
| } |
| |
| static int name_from_null(struct address buf[static 2], const char *name, int family, int flags) |
| { |
| int cnt = 0; |
| if (name) return 0; |
| if (flags & AI_PASSIVE) { |
| if (family != AF_INET6) |
| buf[cnt++] = (struct address){ .family = AF_INET }; |
| if (family != AF_INET) |
| buf[cnt++] = (struct address){ .family = AF_INET6 }; |
| } else { |
| if (family != AF_INET6) |
| buf[cnt++] = (struct address){ .family = AF_INET, .addr = { 127,0,0,1 } }; |
| if (family != AF_INET) |
| buf[cnt++] = (struct address){ .family = AF_INET6, .addr = { [15] = 1 } }; |
| } |
| return cnt; |
| } |
| |
| static int name_from_numeric(struct address buf[static 1], const char *name, int family) |
| { |
| 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, badfam = 0; |
| unsigned char _buf[1032]; |
| FILE _f, *f = __fopen_rb_ca("/etc/hosts", &_f, _buf, sizeof _buf); |
| 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; |
| |
| if ((p=strchr(line, '#'))) *p++='\n', *p=0; |
| for(p=line+1; (p=strstr(p, name)) && |
| (!isspace(p[-1]) || !isspace(p[l])); p++); |
| if (!p) continue; |
| |
| /* Isolate IP address to parse */ |
| for (p=line; *p && !isspace(*p); p++); |
| *p++ = 0; |
| switch (name_from_numeric(buf+cnt, line, family)) { |
| case 1: |
| cnt++; |
| break; |
| case 0: |
| continue; |
| default: |
| badfam = EAI_NONAME; |
| 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); |
| } |
| __fclose_ca(f); |
| return cnt ? cnt : badfam; |
| } |
| |
| struct dpc_ctx { |
| struct address *addrs; |
| char *canon; |
| int cnt; |
| }; |
| |
| int __dns_parse(const unsigned char *, int, int (*)(void *, int, const void *, int, const void *), void *); |
| int __dn_expand(const unsigned char *, const unsigned char *, const unsigned char *, char *, int); |
| int __res_mkquery(int, const char *, int, int, const unsigned char *, int, const unsigned char*, unsigned char *, int); |
| int __res_msend_rc(int, const unsigned char *const *, const int *, unsigned char *const *, int *, int, const struct resolvconf *); |
| |
| #define RR_A 1 |
| #define RR_CNAME 5 |
| #define RR_AAAA 28 |
| |
| static int dns_parse_callback(void *c, int rr, const void *data, int len, const void *packet) |
| { |
| char tmp[256]; |
| struct dpc_ctx *ctx = c; |
| if (ctx->cnt >= MAXADDRS) return -1; |
| switch (rr) { |
| 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: |
| if (__dn_expand(packet, (const unsigned char *)packet + 512, |
| data, tmp, sizeof tmp) > 0 && is_valid_hostname(tmp)) |
| strcpy(ctx->canon, tmp); |
| break; |
| } |
| return 0; |
| } |
| |
| 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][512]; |
| const unsigned char *qp[2] = { qbuf[0], qbuf[1] }; |
| unsigned char *ap[2] = { abuf[0], abuf[1] }; |
| int qlens[2], alens[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<2; i++) { |
| if (family != afrr[i].af) { |
| qlens[nq] = __res_mkquery(0, name, 1, afrr[i].rr, |
| 0, 0, 0, qbuf[nq], sizeof *qbuf); |
| if (qlens[nq] == -1) |
| return EAI_NONAME; |
| nq++; |
| } |
| } |
| |
| if (__res_msend_rc(nq, qp, qlens, ap, alens, sizeof *abuf, conf) < 0) |
| return EAI_SYSTEM; |
| |
| for (i=0; i<nq; i++) |
| __dns_parse(abuf[i], alens[i], dns_parse_callback, &ctx); |
| |
| if (ctx.cnt) return ctx.cnt; |
| if (alens[0] < 4 || (abuf[0][3] & 15) == 2) return EAI_AGAIN; |
| if ((abuf[0][3] & 15) == 0) return EAI_NONAME; |
| if ((abuf[0][3] & 15) == 3) return 0; |
| return EAI_FAIL; |
| } |
| |
| 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; |
| } |
| } |
| |
| 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) |
| { |
| int cnt = 0, i, j; |
| |
| *canon = 0; |
| if (name) { |
| /* 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); |
| } |
| |
| /* Procedurally, a request for v6 addresses with the v4-mapped |
| * flag set is like a request for unspecified family, followed |
| * by filtering of the results. */ |
| if (flags & AI_V4MAPPED) { |
| if (family == AF_INET6) family = AF_UNSPEC; |
| else flags -= AI_V4MAPPED; |
| } |
| |
| /* Try each backend until there's at least one result. */ |
| cnt = name_from_null(buf, name, family, flags); |
| if (!cnt) cnt = name_from_numeric(buf, name, family); |
| if (!cnt && !(flags & AI_NUMERICHOST)) { |
| cnt = name_from_hosts(buf, canon, name, family); |
| if (!cnt) cnt = name_from_dns_search(buf, canon, name, family); |
| } |
| if (cnt<=0) return cnt ? cnt : EAI_NONAME; |
| |
| /* Filter/transform results for v4-mapped lookup, if requested. */ |
| if (flags & AI_V4MAPPED) { |
| if (!(flags & AI_ALL)) { |
| /* If any v6 results exist, remove v4 results. */ |
| for (i=0; i<cnt && buf[i].family != AF_INET6; i++); |
| if (i<cnt) { |
| for (j=0; i<cnt; i++) { |
| if (buf[i].family == AF_INET6) |
| buf[j++] = buf[i]; |
| } |
| cnt = i = j; |
| } |
| } |
| /* Translate any remaining v4 results to v6 */ |
| for (i=0; i<cnt; i++) { |
| 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].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; i<cnt; i++) if (buf[i].family != AF_INET) break; |
| 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 family = buf[i].family; |
| int key = 0; |
| struct sockaddr_in6 sa6 = { 0 }, da6 = { |
| .sin6_family = AF_INET6, |
| .sin6_scope_id = buf[i].scopeid, |
| .sin6_port = 65535 |
| }; |
| struct sockaddr_in sa4 = { 0 }, da4 = { |
| .sin_family = AF_INET, |
| .sin_port = 65535 |
| }; |
| void *sa, *da; |
| socklen_t salen, dalen; |
| if (family == AF_INET6) { |
| memcpy(da6.sin6_addr.s6_addr, buf[i].addr, 16); |
| da = &da6; dalen = sizeof da6; |
| sa = &sa6; salen = sizeof sa6; |
| } else { |
| memcpy(sa6.sin6_addr.s6_addr, |
| "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12); |
| memcpy(da6.sin6_addr.s6_addr+12, buf[i].addr, 4); |
| memcpy(da6.sin6_addr.s6_addr, |
| "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12); |
| memcpy(da6.sin6_addr.s6_addr+12, buf[i].addr, 4); |
| memcpy(&da4.sin_addr, buf[i].addr, 4); |
| da = &da4; dalen = sizeof da4; |
| sa = &sa4; salen = sizeof sa4; |
| } |
| const struct policy *dpolicy = policyof(&da6.sin6_addr); |
| int dscope = scopeof(&da6.sin6_addr); |
| int dlabel = dpolicy->label; |
| 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; |
| } |