diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c index 121b8612ac27..830e464c0125 100644 --- a/lib/libc/net/getaddrinfo.c +++ b/lib/libc/net/getaddrinfo.c @@ -102,7 +102,6 @@ __FBSDID("$FreeBSD$"); # define FAITH #endif -#define SUCCESS 0 #define ANY 0 #define YES 1 #define NO 0 @@ -179,11 +178,6 @@ static const struct explore explore[] = { { PF_INET, SOCK_STREAM, IPPROTO_SCTP, "sctp", 0x03 }, { PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, "sctp", 0x07 }, { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, - { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, - { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, - { PF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP, "sctp", 0x03 }, - { PF_UNSPEC, SOCK_SEQPACKET, IPPROTO_SCTP, "sctp", 0x07 }, - { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 }, { -1, 0, 0, NULL, 0 }, }; @@ -233,6 +227,8 @@ typedef union { } querybuf; static int str2number(const char *, int *); +static int explore_copy(const struct addrinfo *, const struct addrinfo *, + struct addrinfo **); static int explore_null(const struct addrinfo *, const char *, struct addrinfo **); static int explore_numeric(const struct addrinfo *, const char *, @@ -243,6 +239,7 @@ static int get_canonname(const struct addrinfo *, struct addrinfo *, const char *); static struct addrinfo *get_ai(const struct addrinfo *, const struct afd *, const char *); +static struct addrinfo *copy_ai(const struct addrinfo *); static int get_portmatch(const struct addrinfo *, const char *); static int get_port(struct addrinfo *, const char *, int); static const struct afd *find_afd(int); @@ -371,12 +368,23 @@ getaddrinfo(const char *hostname, const char *servname, struct addrinfo sentinel; struct addrinfo *cur; int error = 0; - struct addrinfo ai; - struct addrinfo ai0; + struct addrinfo ai, ai0, *afai; struct addrinfo *pai; + const struct afd *afd; const struct explore *ex; + struct addrinfo *afailist[sizeof(afdl)/sizeof(afdl[0])]; + struct addrinfo *afai_unspec; + int found; int numeric = 0; + /* ensure we return NULL on errors */ + *res = NULL; + + memset(&ai, 0, sizeof(ai)); + + memset(afailist, 0, sizeof(afailist)); + afai_unspec = NULL; + memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; pai = &ai; @@ -416,15 +424,18 @@ getaddrinfo(const char *hostname, const char *servname, */ if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { for (ex = explore; ex->e_af >= 0; ex++) { - if (pai->ai_family != ex->e_af) + if (!MATCH_FAMILY(pai->ai_family, ex->e_af, + WILD_AF(ex))) continue; - if (ex->e_socktype == ANY) + if (!MATCH(pai->ai_socktype, ex->e_socktype, + WILD_SOCKTYPE(ex))) continue; - if (ex->e_protocol == ANY) + if (!MATCH(pai->ai_protocol, ex->e_protocol, + WILD_PROTOCOL(ex))) continue; - if (pai->ai_socktype == ex->e_socktype && - pai->ai_protocol == ex->e_protocol) - break; + + /* matched */ + break; } if (ex->e_af < 0) @@ -460,49 +471,48 @@ getaddrinfo(const char *hostname, const char *servname, ai0 = *pai; - /* NULL hostname, or numeric hostname */ - for (ex = explore; ex->e_af >= 0; ex++) { + /* + * NULL hostname, or numeric hostname. + * If numeric representation of AF1 can be interpreted as FQDN + * representation of AF2, we need to think again about the code below. + */ + found = 0; + for (afd = afdl; afd->a_af; afd++) { *pai = ai0; - /* PF_UNSPEC entries are prepared for DNS queries only */ - if (ex->e_af == PF_UNSPEC) - continue; - - if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) - continue; - if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) - continue; - if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) + if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1)) continue; if (pai->ai_family == PF_UNSPEC) - pai->ai_family = ex->e_af; - if (pai->ai_socktype == ANY && ex->e_socktype != ANY) - pai->ai_socktype = ex->e_socktype; - if (pai->ai_protocol == ANY && ex->e_protocol != ANY) - pai->ai_protocol = ex->e_protocol; + pai->ai_family = afd->a_af; - if (hostname == NULL) - error = explore_null(pai, servname, &cur->ai_next); - else + if (hostname == NULL) { + error = explore_null(pai, servname, + &afailist[afd - afdl]); + + /* + * Errors from explore_null should be unexpected and + * be caught to avoid returning an incomplete result. + */ + if (error != 0) + goto bad; + } else { error = explore_numeric_scope(pai, hostname, servname, - &cur->ai_next); + &afailist[afd - afdl]); - if (error) - goto free; + /* + * explore_numeric_scope returns an error for address + * families that do not match that of hostname. + * Thus we should not catch the error at this moment. + */ + } - while (cur && cur->ai_next) - cur = cur->ai_next; + if (!error && afailist[afd - afdl]) + found++; } - - /* - * XXX - * If numreic representation of AF1 can be interpreted as FQDN - * representation of AF2, we need to think again about the code below. - */ - if (sentinel.ai_next) { + if (found) { numeric = 1; - goto good; + goto globcopy; } if (hostname == NULL) @@ -515,42 +525,55 @@ getaddrinfo(const char *hostname, const char *servname, /* * hostname as alphabetical name. - * we would like to prefer AF_INET6 than AF_INET, so we'll make a - * outer loop by AFs. */ + *pai = ai0; + error = explore_fqdn(pai, hostname, servname, &afai_unspec); + +globcopy: for (ex = explore; ex->e_af >= 0; ex++) { *pai = ai0; - /* require exact match for family field */ - if (pai->ai_family != ex->e_af) + if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) continue; - if (!MATCH(pai->ai_socktype, ex->e_socktype, - WILD_SOCKTYPE(ex))) { + WILD_SOCKTYPE(ex))) continue; - } if (!MATCH(pai->ai_protocol, ex->e_protocol, - WILD_PROTOCOL(ex))) { + WILD_PROTOCOL(ex))) continue; - } + if (pai->ai_family == PF_UNSPEC) + pai->ai_family = ex->e_af; if (pai->ai_socktype == ANY && ex->e_socktype != ANY) pai->ai_socktype = ex->e_socktype; if (pai->ai_protocol == ANY && ex->e_protocol != ANY) pai->ai_protocol = ex->e_protocol; - error = explore_fqdn(pai, hostname, servname, - &cur->ai_next); + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + continue; + + if (afai_unspec) + afai = afai_unspec; + else { + if ((afd = find_afd(pai->ai_family)) == NULL) + continue; + /* XXX assumes that afd points inside afdl[] */ + afai = afailist[afd - afdl]; + } + if (!afai) + continue; + + error = explore_copy(pai, afai, &cur->ai_next); + if (error != 0) + goto bad; while (cur && cur->ai_next) cur = cur->ai_next; } - /* XXX inhibit errors if we have the result */ - if (sentinel.ai_next) - error = 0; - -good: /* * ensure we return either: * - error == 0, non-NULL *res @@ -586,16 +609,22 @@ getaddrinfo(const char *hostname, const char *servname, } } *res = sentinel.ai_next; - return SUCCESS; } else error = EAI_FAIL; } -free: + bad: - if (sentinel.ai_next) - freeaddrinfo(sentinel.ai_next); - *res = NULL; - return error; + if (afai_unspec) + freeaddrinfo(afai_unspec); + for (afd = afdl; afd->a_af; afd++) { + if (afailist[afd - afdl]) + freeaddrinfo(afailist[afd - afdl]); + } + if (!*res) + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + + return (error); } static int @@ -1047,6 +1076,41 @@ gai_addr2scopetype(struct sockaddr *sa) } } +static int +explore_copy(const struct addrinfo *pai, const struct addrinfo *src0, + struct addrinfo **res) +{ + int error; + struct addrinfo sentinel, *cur; + const struct addrinfo *src; + + error = 0; + sentinel.ai_next = NULL; + cur = &sentinel; + + for (src = src0; src != NULL; src = src->ai_next) { + if (src->ai_family != pai->ai_family) + continue; + + cur->ai_next = copy_ai(src); + if (!cur->ai_next) { + error = EAI_MEMORY; + goto fail; + } + + cur->ai_next->ai_socktype = pai->ai_socktype; + cur->ai_next->ai_protocol = pai->ai_protocol; + cur = cur->ai_next; + } + + *res = sentinel.ai_next; + return 0; + +fail: + freeaddrinfo(sentinel.ai_next); + return error; +} + /* * hostname == NULL. * passive socket -> anyaddr (0.0.0.0 or ::) @@ -1075,12 +1139,6 @@ explore_null(const struct addrinfo *pai, const char *servname, } else _close(s); - /* - * if the servname does not match socktype/protocol, ignore it. - */ - if (get_portmatch(pai, servname) != 0) - return 0; - afd = find_afd(pai->ai_family); if (afd == NULL) return 0; @@ -1117,12 +1175,6 @@ explore_numeric(const struct addrinfo *pai, const char *hostname, *res = NULL; ai = NULL; - /* - * if the servname does not match socktype/protocol, ignore it. - */ - if (get_portmatch(pai, servname) != 0) - return 0; - afd = find_afd(pai->ai_family); if (afd == NULL) return 0; @@ -1189,12 +1241,6 @@ explore_numeric_scope(const struct addrinfo *pai, const char *hostname, char *cp, *hostname2 = NULL, *scope, *addr; struct sockaddr_in6 *sin6; - /* - * if the servname does not match socktype/protocol, ignore it. - */ - if (get_portmatch(pai, servname) != 0) - return 0; - afd = find_afd(pai->ai_family); if (afd == NULL) return 0; @@ -1227,6 +1273,8 @@ explore_numeric_scope(const struct addrinfo *pai, const char *hostname, sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr; if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) { free(hostname2); + freeaddrinfo(*res); + *res = NULL; return(EAI_NONAME); /* XXX: is return OK? */ } sin6->sin6_scope_id = scopeid; @@ -1235,6 +1283,10 @@ explore_numeric_scope(const struct addrinfo *pai, const char *hostname, free(hostname2); + if (error && *res) { + freeaddrinfo(*res); + *res = NULL; + } return error; #endif } @@ -1318,6 +1370,38 @@ get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) return ai; } +/* XXX need to malloc() the same way we do from other functions! */ +static struct addrinfo * +copy_ai(const struct addrinfo *pai) +{ + struct addrinfo *ai; + size_t l; + + l = sizeof(*ai) + pai->ai_addrlen; + if ((ai = (struct addrinfo *)malloc(l)) == NULL) + return NULL; + memset(ai, 0, l); + memcpy(ai, pai, sizeof(*ai)); + ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); + memcpy(ai->ai_addr, pai->ai_addr, pai->ai_addrlen); + + if (pai->ai_canonname) { + l = strlen(pai->ai_canonname) + 1; + if ((ai->ai_canonname = malloc(l)) == NULL) { + free(ai); + return NULL; + } + strlcpy(ai->ai_canonname, pai->ai_canonname, l); + } else { + /* just to make sure */ + ai->ai_canonname = NULL; + } + + ai->ai_next = NULL; + + return ai; +} + static int get_portmatch(const struct addrinfo *ai, const char *servname) {