Index: fusl/src/network/lookup_name.c |
diff --git a/fusl/src/network/lookup_name.c b/fusl/src/network/lookup_name.c |
index a26ad535143e7651807c5d153fb4c1fab4ea5a73..b1f2487160eac328adddb37f1f2a672a4efadcb8 100644 |
--- a/fusl/src/network/lookup_name.c |
+++ b/fusl/src/network/lookup_name.c |
@@ -14,200 +14,261 @@ |
#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 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_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_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; |
- 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; |
- if (name_from_numeric(buf+cnt, line, family)) |
- cnt++; |
- |
- /* 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; |
+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; |
+ 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; |
+ if (name_from_numeric(buf + cnt, line, family)) |
+ cnt++; |
+ |
+ /* 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; |
} |
struct dpc_ctx { |
- struct address *addrs; |
- char *canon; |
- int cnt; |
+ 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 *); |
+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 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 }; |
- |
- if (family != AF_INET6) { |
- qlens[nq] = __res_mkquery(0, name, 1, RR_A, 0, 0, 0, |
- qbuf[nq], sizeof *qbuf); |
- nq++; |
- } |
- if (family != AF_INET) { |
- qlens[nq] = __res_mkquery(0, name, 1, RR_AAAA, 0, 0, 0, |
- qbuf[nq], sizeof *qbuf); |
- 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(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}; |
+ |
+ if (family != AF_INET6) { |
+ qlens[nq] = |
+ __res_mkquery(0, name, 1, RR_A, 0, 0, 0, qbuf[nq], sizeof *qbuf); |
+ nq++; |
+ } |
+ if (family != AF_INET) { |
+ qlens[nq] = |
+ __res_mkquery(0, name, 1, RR_AAAA, 0, 0, 0, qbuf[nq], sizeof *qbuf); |
+ 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 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; |
+ 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 }, |
+ {"\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 |
@@ -216,172 +277,181 @@ static const struct policy { |
{ "\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 }, |
+ /* 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 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 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 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; |
+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; |
+#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; 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; |
+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; 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; |
} |