123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- #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;
- 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;
- /* 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 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;
- }
|