| /* |
| * Copyright 1996 by Craig Metz |
| * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org> |
| * Portions from the GNU C library, |
| * Copyright (C) 2003, 2006 Free Software Foundation, Inc. |
| * |
| * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. |
| */ |
| |
| /* $USAGI: getaddrinfo.c,v 1.16 2001/10/04 09:52:03 sekiya Exp $ */ |
| |
| /* The Inner Net License, Version 2.00 |
| |
| The author(s) grant permission for redistribution and use in source and |
| binary forms, with or without modification, of the software and documentation |
| provided that the following conditions are met: |
| |
| 0. If you receive a version of the software that is specifically labelled |
| as not being for redistribution (check the version message and/or README), |
| you are not permitted to redistribute that version of the software in any |
| way or form. |
| 1. All terms of the all other applicable copyrights and licenses must be |
| followed. |
| 2. Redistributions of source code must retain the authors' copyright |
| notice(s), this list of conditions, and the following disclaimer. |
| 3. Redistributions in binary form must reproduce the authors' copyright |
| notice(s), this list of conditions, and the following disclaimer in the |
| documentation and/or other materials provided with the distribution. |
| 4. All advertising materials mentioning features or use of this software |
| must display the following acknowledgement with the name(s) of the |
| authors as specified in the copyright notice(s) substituted where |
| indicated: |
| |
| This product includes software developed by <name(s)>, The Inner |
| Net, and other contributors. |
| |
| 5. Neither the name(s) of the author(s) nor the names of its contributors |
| may be used to endorse or promote products derived from this software |
| without specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY |
| EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY |
| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| If these license terms cause you a real problem, contact the author. */ |
| |
| #define __FORCE_GLIBC |
| #include <features.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <netdb.h> |
| #ifdef __UCLIBC_HAS_TLS__ |
| #include <tls.h> |
| #endif |
| #include <resolv.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <arpa/inet.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <sys/types.h> |
| #include <sys/un.h> |
| #include <sys/utsname.h> |
| #include <net/if.h> |
| #include <ifaddrs.h> |
| |
| #define GAIH_OKIFUNSPEC 0x0100 |
| #define GAIH_EAI ~(GAIH_OKIFUNSPEC) |
| |
| #ifndef UNIX_PATH_MAX |
| #define UNIX_PATH_MAX 108 |
| #endif |
| |
| /* Useful for having small structure members/global variables */ |
| typedef int8_t socktype_t; |
| typedef int8_t family_t; |
| typedef int8_t protocol_t; |
| struct BUG_too_small { |
| char BUG_socktype_t_too_small[(0 |
| | SOCK_STREAM |
| | SOCK_DGRAM |
| | SOCK_RAW |
| ) <= 127 ? 1 : -1]; |
| char BUG_family_t_too_small[(0 |
| | AF_UNSPEC |
| | AF_INET |
| | AF_INET6 |
| ) <= 127 ? 1 : -1]; |
| char BUG_protocol_t_too_small[(0 |
| | IPPROTO_TCP |
| | IPPROTO_UDP |
| ) <= 127 ? 1 : -1]; |
| }; |
| |
| struct gaih_service { |
| const char *name; |
| int num; |
| }; |
| |
| struct gaih_servtuple { |
| struct gaih_servtuple *next; |
| int socktype; |
| int protocol; |
| int port; |
| }; |
| |
| struct gaih_addrtuple { |
| struct gaih_addrtuple *next; |
| int family; |
| char addr[16]; |
| uint32_t scopeid; |
| }; |
| |
| struct gaih_typeproto { |
| socktype_t socktype; |
| protocol_t protocol; |
| int8_t protoflag; |
| char name[4]; |
| }; |
| /* Values for `protoflag'. */ |
| #define GAI_PROTO_NOSERVICE 1 |
| #define GAI_PROTO_PROTOANY 2 |
| |
| static const struct gaih_typeproto gaih_inet_typeproto[] = { |
| { 0 , 0 , 0, "" }, |
| { SOCK_STREAM, IPPROTO_TCP, 0, "tcp" }, |
| { SOCK_DGRAM , IPPROTO_UDP, 0, "udp" }, |
| { SOCK_RAW , 0 , GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE, "raw" }, |
| { 0 , 0 , 0, "" }, |
| }; |
| |
| struct gaih { |
| int family; |
| int (*gaih)(const char *name, const struct gaih_service *service, |
| const struct addrinfo *req, struct addrinfo **pai); |
| }; |
| |
| #define SEEN_IPV4 1 |
| #define SEEN_IPV6 2 |
| |
| static unsigned __check_pf(void) |
| { |
| unsigned seen = 0; |
| |
| #if defined __UCLIBC_SUPPORT_AI_ADDRCONFIG__ |
| |
| struct ifaddrs *ifa; |
| struct ifaddrs *runp; |
| |
| /* Get the interface list via getifaddrs. */ |
| if (getifaddrs(&ifa) != 0) { |
| /* We cannot determine what interfaces are available. |
| * Be optimistic. */ |
| #if defined __UCLIBC_HAS_IPV4__ |
| seen |= SEEN_IPV4; |
| #endif |
| #if defined __UCLIBC_HAS_IPV6__ |
| seen |= SEEN_IPV6; |
| #endif |
| return seen; |
| } |
| |
| for (runp = ifa; runp != NULL; runp = runp->ifa_next) { |
| if (runp->ifa_addr == NULL) |
| continue; |
| #if defined __UCLIBC_HAS_IPV4__ |
| if (runp->ifa_addr->sa_family == PF_INET) |
| seen |= SEEN_IPV4; |
| #endif |
| #if defined __UCLIBC_HAS_IPV6__ |
| if (runp->ifa_addr->sa_family == PF_INET6) |
| seen |= SEEN_IPV6; |
| #endif |
| } |
| freeifaddrs(ifa); |
| |
| #else |
| |
| /* AI_ADDRCONFIG is disabled, assume both ipv4 and ipv6 available. */ |
| #if defined __UCLIBC_HAS_IPV4__ |
| seen |= SEEN_IPV4; |
| #endif |
| #if defined __UCLIBC_HAS_IPV6__ |
| seen |= SEEN_IPV6; |
| #endif |
| |
| #endif /* __UCLIBC_SUPPORT_AI_ADDRCONFIG__ */ |
| |
| return seen; |
| } |
| |
| static int addrconfig(sa_family_t af) |
| { |
| int s; |
| int ret; |
| int saved_errno = errno; |
| unsigned seen; |
| |
| seen = __check_pf(); |
| #if defined __UCLIBC_HAS_IPV4__ |
| if (af == AF_INET) |
| ret = seen & SEEN_IPV4; |
| else |
| #endif |
| #if defined __UCLIBC_HAS_IPV6__ |
| if (af == AF_INET6) |
| ret = seen & SEEN_IPV6; |
| else |
| #endif |
| { |
| s = socket(af, SOCK_DGRAM, 0); |
| ret = 1; /* Assume PF_UNIX. */ |
| if (s < 0) { |
| if (errno != EMFILE) |
| ret = 0; |
| } else |
| close(s); |
| } |
| __set_errno(saved_errno); |
| return ret; |
| } |
| |
| #if 0 |
| /* Using Unix sockets this way is a security risk. */ |
| static int |
| gaih_local(const char *name, const struct gaih_service *service, |
| const struct addrinfo *req, struct addrinfo **pai) |
| { |
| struct utsname utsname; |
| struct addrinfo *ai = *pai; |
| |
| if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST)) |
| return (GAIH_OKIFUNSPEC | -EAI_NONAME); |
| |
| if ((name != NULL) || (req->ai_flags & AI_CANONNAME)) |
| if (uname(&utsname) < 0) |
| return -EAI_SYSTEM; |
| |
| if (name != NULL) { |
| if (strcmp(name, "localhost") && |
| strcmp(name, "local") && |
| strcmp(name, "unix") && |
| strcmp(name, utsname.nodename)) |
| return (GAIH_OKIFUNSPEC | -EAI_NONAME); |
| } |
| |
| if (req->ai_protocol || req->ai_socktype) { |
| const struct gaih_typeproto *tp = gaih_inet_typeproto + 1; |
| |
| while (tp->name[0] |
| && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0 |
| || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype) |
| || (req->ai_protocol != 0 && !(tp->protoflag & GAI_PROTO_PROTOANY) && req->ai_protocol != tp->protocol)) |
| ) { |
| ++tp; |
| } |
| if (! tp->name[0]) { |
| if (req->ai_socktype) |
| return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE); |
| return (GAIH_OKIFUNSPEC | -EAI_SERVICE); |
| } |
| } |
| |
| *pai = ai = malloc(sizeof(struct addrinfo) + sizeof(struct sockaddr_un) |
| + ((req->ai_flags & AI_CANONNAME) |
| ? (strlen(utsname.nodename) + 1) : 0)); |
| if (ai == NULL) |
| return -EAI_MEMORY; |
| |
| ai->ai_next = NULL; |
| ai->ai_flags = req->ai_flags; |
| ai->ai_family = AF_LOCAL; |
| ai->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM; |
| ai->ai_protocol = req->ai_protocol; |
| ai->ai_addrlen = sizeof(struct sockaddr_un); |
| ai->ai_addr = (void *)ai + sizeof(struct addrinfo); |
| #if SALEN |
| ((struct sockaddr_un *)ai->ai_addr)->sun_len = sizeof(struct sockaddr_un); |
| #endif /* SALEN */ |
| |
| ((struct sockaddr_un *)ai->ai_addr)->sun_family = AF_LOCAL; |
| memset(((struct sockaddr_un *)ai->ai_addr)->sun_path, 0, UNIX_PATH_MAX); |
| |
| if (service) { |
| struct sockaddr_un *sunp = (struct sockaddr_un *)ai->ai_addr; |
| |
| if (strchr(service->name, '/') != NULL) { |
| if (strlen(service->name) >= sizeof(sunp->sun_path)) |
| return GAIH_OKIFUNSPEC | -EAI_SERVICE; |
| strcpy(sunp->sun_path, service->name); |
| } else { |
| if (strlen(P_tmpdir "/") + 1 + strlen(service->name) >= sizeof(sunp->sun_path)) |
| return (GAIH_OKIFUNSPEC | -EAI_SERVICE); |
| stpcpy(stpcpy(sunp->sun_path, P_tmpdir "/"), service->name); |
| } |
| } else { |
| /* This is a dangerous use of the interface since there is a time |
| window between the test for the file and the actual creation |
| (done by the caller) in which a file with the same name could |
| be created. */ |
| char *buf = ((struct sockaddr_un *)ai->ai_addr)->sun_path; |
| |
| if (__path_search(buf, L_tmpnam, NULL, NULL, 0) != 0 |
| || __gen_tempname(buf, __GT_NOCREATE, 0) != 0 |
| ) { |
| return -EAI_SYSTEM; |
| } |
| } |
| |
| ai->ai_canonname = NULL; |
| if (req->ai_flags & AI_CANONNAME) |
| ai->ai_canonname = strcpy((char *)(ai + 1) + sizeof(struct sockaddr_un), |
| utsname.nodename); |
| return 0; |
| } |
| #endif /* 0 */ |
| |
| static int |
| gaih_inet_serv(const char *servicename, const struct gaih_typeproto *tp, |
| const struct addrinfo *req, struct gaih_servtuple *st) |
| { |
| struct servent *s; |
| size_t tmpbuflen = 1024; |
| struct servent ts; |
| char *tmpbuf; |
| int r; |
| |
| while (1) { |
| tmpbuf = alloca(tmpbuflen); |
| r = getservbyname_r(servicename, tp->name, &ts, tmpbuf, tmpbuflen, &s); |
| if (r == 0 && s != NULL) |
| break; |
| if (r != ERANGE) |
| return (GAIH_OKIFUNSPEC | -EAI_SERVICE); |
| tmpbuflen *= 2; |
| } |
| st->next = NULL; |
| st->socktype = tp->socktype; |
| st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) ? req->ai_protocol : tp->protocol); |
| st->port = s->s_port; |
| return 0; |
| } |
| |
| /* NB: also uses h,pat,rc,no_data variables */ |
| #define gethosts(_family, _type) \ |
| { \ |
| int i, herrno; \ |
| size_t tmpbuflen; \ |
| struct hostent th; \ |
| char *tmpbuf; \ |
| \ |
| tmpbuflen = 512; \ |
| no_data = 0; \ |
| do { \ |
| tmpbuflen *= 2; \ |
| tmpbuf = alloca(tmpbuflen); \ |
| rc = gethostbyname2_r(name, _family, &th, tmpbuf, \ |
| tmpbuflen, &h, &herrno); \ |
| } while (rc == ERANGE && herrno == NETDB_INTERNAL); \ |
| if (rc != 0) { \ |
| if (herrno == NETDB_INTERNAL) { \ |
| __set_h_errno(herrno); \ |
| return -EAI_SYSTEM; \ |
| } \ |
| if (herrno == TRY_AGAIN) \ |
| no_data = EAI_AGAIN; \ |
| else \ |
| no_data = (herrno == NO_DATA); \ |
| } else if (h != NULL) { \ |
| for (i = 0; h->h_addr_list[i]; i++) { \ |
| if (*pat == NULL) { \ |
| *pat = alloca(sizeof(struct gaih_addrtuple)); \ |
| (*pat)->scopeid = 0; \ |
| } \ |
| (*pat)->next = NULL; \ |
| (*pat)->family = _family; \ |
| memcpy((*pat)->addr, h->h_addr_list[i], sizeof(_type)); \ |
| pat = &((*pat)->next); \ |
| } \ |
| } \ |
| } |
| |
| static int |
| gaih_inet(const char *name, const struct gaih_service *service, |
| const struct addrinfo *req, struct addrinfo **pai) |
| { |
| struct gaih_servtuple nullserv; |
| |
| const struct gaih_typeproto *tp; |
| struct gaih_servtuple *st; |
| struct gaih_addrtuple *at; |
| int rc; |
| int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6) |
| && (req->ai_flags & AI_V4MAPPED); |
| unsigned seen = 0; |
| if (req->ai_flags & AI_ADDRCONFIG) { |
| /* "seen" is only used when AI_ADDRCONFIG is specified. |
| Avoid unnecessary call to __check_pf() otherwise |
| since it can be costly especially when RSBAC-Net is enabled. */ |
| seen = __check_pf(); |
| } |
| |
| memset(&nullserv, 0, sizeof(nullserv)); |
| |
| tp = gaih_inet_typeproto; |
| if (req->ai_protocol || req->ai_socktype) { |
| ++tp; |
| while (tp->name[0]) { |
| if ((req->ai_socktype == 0 || req->ai_socktype == tp->socktype) |
| && (req->ai_protocol == 0 || req->ai_protocol == tp->protocol || (tp->protoflag & GAI_PROTO_PROTOANY)) |
| ) { |
| goto found; |
| } |
| ++tp; |
| } |
| if (req->ai_socktype) |
| return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE); |
| return (GAIH_OKIFUNSPEC | -EAI_SERVICE); |
| found: ; |
| } |
| |
| st = &nullserv; |
| if (service != NULL) { |
| if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) |
| return (GAIH_OKIFUNSPEC | -EAI_SERVICE); |
| |
| if (service->num < 0) { |
| if (tp->name[0]) { |
| st = alloca(sizeof(struct gaih_servtuple)); |
| rc = gaih_inet_serv(service->name, tp, req, st); |
| if (rc) |
| return rc; |
| } else { |
| struct gaih_servtuple **pst = &st; |
| for (tp++; tp->name[0]; tp++) { |
| struct gaih_servtuple *newp; |
| |
| if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) |
| continue; |
| |
| if (req->ai_socktype != 0 && req->ai_socktype != tp->socktype) |
| continue; |
| if (req->ai_protocol != 0 |
| && !(tp->protoflag & GAI_PROTO_PROTOANY) |
| && req->ai_protocol != tp->protocol) |
| continue; |
| |
| newp = alloca(sizeof(struct gaih_servtuple)); |
| rc = gaih_inet_serv(service->name, tp, req, newp); |
| if (rc) { |
| if (rc & GAIH_OKIFUNSPEC) |
| continue; |
| return rc; |
| } |
| |
| *pst = newp; |
| pst = &(newp->next); |
| } |
| if (st == &nullserv) |
| return (GAIH_OKIFUNSPEC | -EAI_SERVICE); |
| } |
| } else { |
| st = alloca(sizeof(struct gaih_servtuple)); |
| st->next = NULL; |
| st->socktype = tp->socktype; |
| st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) |
| ? req->ai_protocol : tp->protocol); |
| st->port = htons(service->num); |
| } |
| } else if (req->ai_socktype || req->ai_protocol) { |
| st = alloca(sizeof(struct gaih_servtuple)); |
| st->next = NULL; |
| st->socktype = tp->socktype; |
| st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) |
| ? req->ai_protocol : tp->protocol); |
| st->port = 0; |
| } else { |
| /* |
| * Neither socket type nor protocol is set. Return all socket types |
| * we know about. |
| */ |
| struct gaih_servtuple **lastp = &st; |
| for (++tp; tp->name[0]; ++tp) { |
| struct gaih_servtuple *newp; |
| |
| newp = alloca(sizeof(struct gaih_servtuple)); |
| newp->next = NULL; |
| newp->socktype = tp->socktype; |
| newp->protocol = tp->protocol; |
| newp->port = 0; |
| |
| *lastp = newp; |
| lastp = &newp->next; |
| } |
| } |
| |
| at = NULL; |
| if (name != NULL) { |
| at = alloca(sizeof(struct gaih_addrtuple)); |
| at->family = AF_UNSPEC; |
| at->scopeid = 0; |
| at->next = NULL; |
| |
| if (inet_pton(AF_INET, name, at->addr) > 0) { |
| if (req->ai_family != AF_UNSPEC && req->ai_family != AF_INET && !v4mapped) |
| return -EAI_FAMILY; |
| at->family = AF_INET; |
| } |
| |
| #if defined __UCLIBC_HAS_IPV6__ |
| if (at->family == AF_UNSPEC) { |
| char *namebuf = strdupa(name); |
| char *scope_delim; |
| |
| scope_delim = strchr(namebuf, SCOPE_DELIMITER); |
| if (scope_delim != NULL) |
| *scope_delim = '\0'; |
| |
| if (inet_pton(AF_INET6, namebuf, at->addr) > 0) { |
| if (req->ai_family != AF_UNSPEC && req->ai_family != AF_INET6) |
| return -EAI_FAMILY; |
| at->family = AF_INET6; |
| if (scope_delim != NULL) { |
| int try_numericscope = 0; |
| uint32_t *a32 = (uint32_t*)at->addr; |
| if (IN6_IS_ADDR_LINKLOCAL(a32) || IN6_IS_ADDR_MC_LINKLOCAL(at->addr)) { |
| at->scopeid = if_nametoindex(scope_delim + 1); |
| if (at->scopeid == 0) |
| try_numericscope = 1; |
| } else |
| try_numericscope = 1; |
| |
| if (try_numericscope != 0) { |
| char *end; |
| assert(sizeof(uint32_t) <= sizeof(unsigned long)); |
| at->scopeid = (uint32_t)strtoul(scope_delim + 1, &end, 10); |
| if (*end != '\0') |
| return (GAIH_OKIFUNSPEC | -EAI_NONAME); |
| } |
| } |
| } |
| } |
| #endif |
| |
| if (at->family == AF_UNSPEC && !(req->ai_flags & AI_NUMERICHOST)) { |
| struct hostent *h; |
| struct gaih_addrtuple **pat = &at; |
| int no_data = 0; |
| int no_inet6_data; |
| |
| /* |
| * If we are looking for both IPv4 and IPv6 address we don't want |
| * the lookup functions to automatically promote IPv4 addresses to |
| * IPv6 addresses. |
| */ |
| #if defined __UCLIBC_HAS_IPV6__ |
| if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) |
| if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV6)) |
| gethosts(AF_INET6, struct in6_addr); |
| #endif |
| no_inet6_data = no_data; |
| |
| if (req->ai_family == AF_INET |
| || (!v4mapped && req->ai_family == AF_UNSPEC) |
| || (v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL))) |
| ) { |
| if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV4)) |
| gethosts(AF_INET, struct in_addr); |
| } |
| |
| if (no_data != 0 && no_inet6_data != 0) { |
| /* If both requests timed out report this. */ |
| if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN) |
| return -EAI_AGAIN; |
| /* |
| * We made requests but they turned out no data. |
| * The name is known, though. |
| */ |
| return (GAIH_OKIFUNSPEC | -EAI_AGAIN); |
| } |
| } |
| |
| if (at->family == AF_UNSPEC) |
| return (GAIH_OKIFUNSPEC | -EAI_NONAME); |
| } else { |
| struct gaih_addrtuple *atr; |
| |
| atr = at = alloca(sizeof(struct gaih_addrtuple)); |
| memset(at, '\0', sizeof(struct gaih_addrtuple)); |
| if (req->ai_family == 0) { |
| at->next = alloca(sizeof(struct gaih_addrtuple)); |
| memset(at->next, '\0', sizeof(struct gaih_addrtuple)); |
| } |
| #if defined __UCLIBC_HAS_IPV6__ |
| if (req->ai_family == 0 || req->ai_family == AF_INET6) { |
| at->family = AF_INET6; |
| if ((req->ai_flags & AI_PASSIVE) == 0) |
| memcpy(at->addr, &in6addr_loopback, sizeof(struct in6_addr)); |
| atr = at->next; |
| } |
| #endif |
| if (req->ai_family == 0 || req->ai_family == AF_INET) { |
| atr->family = AF_INET; |
| if ((req->ai_flags & AI_PASSIVE) == 0) { |
| uint32_t *a = (uint32_t*)atr->addr; |
| *a = htonl(INADDR_LOOPBACK); |
| } |
| } |
| } |
| |
| if (pai == NULL) |
| return 0; |
| |
| { |
| const char *c = NULL; |
| struct gaih_servtuple *st2; |
| struct gaih_addrtuple *at2 = at; |
| size_t socklen, namelen; |
| sa_family_t family; |
| |
| /* |
| * buffer is the size of an unformatted IPv6 address in |
| * printable format. |
| */ |
| char buffer[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; |
| |
| while (at2 != NULL) { |
| c = inet_ntop(at2->family, at2->addr, buffer, sizeof(buffer)); |
| if (c) { |
| namelen = strlen(c) + 1; |
| } else if (req->ai_flags & AI_CANONNAME) { |
| struct hostent *h = NULL; |
| int herrno; |
| struct hostent th; |
| size_t tmpbuflen = 512; |
| char *tmpbuf; |
| |
| /* Hint says numeric, but address is not */ |
| if (req->ai_flags & AI_NUMERICHOST) |
| return -EAI_NONAME; |
| |
| do { |
| tmpbuflen *= 2; |
| tmpbuf = alloca(tmpbuflen); |
| rc = gethostbyaddr_r(at2->addr, |
| #ifdef __UCLIBC_HAS_IPV6__ |
| ((at2->family == AF_INET6) |
| ? sizeof(struct in6_addr) |
| : sizeof(struct in_addr)), |
| #else |
| sizeof(struct in_addr), |
| #endif |
| at2->family, |
| &th, tmpbuf, tmpbuflen, |
| &h, &herrno); |
| } while (rc == ERANGE && herrno == NETDB_INTERNAL); |
| |
| if (rc != 0 && herrno == NETDB_INTERNAL) { |
| __set_h_errno(herrno); |
| return -EAI_SYSTEM; |
| } |
| |
| if (h != NULL) |
| c = h->h_name; |
| |
| if (c == NULL) |
| return (GAIH_OKIFUNSPEC | -EAI_NONAME); |
| |
| namelen = strlen(c) + 1; |
| } else |
| namelen = 0; |
| |
| #if defined __UCLIBC_HAS_IPV6__ |
| if (at2->family == AF_INET6 || v4mapped) { |
| family = AF_INET6; |
| socklen = sizeof(struct sockaddr_in6); |
| } |
| #endif |
| #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__ |
| else |
| #endif |
| #if defined __UCLIBC_HAS_IPV4__ |
| { |
| family = AF_INET; |
| socklen = sizeof(struct sockaddr_in); |
| } |
| #endif |
| for (st2 = st; st2 != NULL; st2 = st2->next) { |
| if (req->ai_flags & AI_ADDRCONFIG) { |
| if (family == AF_INET && !(seen & SEEN_IPV4)) |
| break; |
| #if defined __UCLIBC_HAS_IPV6__ |
| else if (family == AF_INET6 && !(seen & SEEN_IPV6)) |
| break; |
| #endif |
| } |
| *pai = malloc(sizeof(struct addrinfo) + socklen + namelen); |
| if (*pai == NULL) |
| return -EAI_MEMORY; |
| |
| (*pai)->ai_flags = req->ai_flags; |
| (*pai)->ai_family = family; |
| (*pai)->ai_socktype = st2->socktype; |
| (*pai)->ai_protocol = st2->protocol; |
| (*pai)->ai_addrlen = socklen; |
| (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo); |
| #if defined SALEN |
| (*pai)->ai_addr->sa_len = socklen; |
| #endif |
| (*pai)->ai_addr->sa_family = family; |
| |
| #if defined __UCLIBC_HAS_IPV6__ |
| if (family == AF_INET6) { |
| struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) (*pai)->ai_addr; |
| |
| sin6p->sin6_flowinfo = 0; |
| if (at2->family == AF_INET6) { |
| memcpy(&sin6p->sin6_addr, |
| at2->addr, sizeof(struct in6_addr)); |
| } else { |
| sin6p->sin6_addr.s6_addr32[0] = 0; |
| sin6p->sin6_addr.s6_addr32[1] = 0; |
| sin6p->sin6_addr.s6_addr32[2] = htonl(0x0000ffff); |
| memcpy(&sin6p->sin6_addr.s6_addr32[3], |
| at2->addr, sizeof(sin6p->sin6_addr.s6_addr32[3])); |
| } |
| sin6p->sin6_port = st2->port; |
| sin6p->sin6_scope_id = at2->scopeid; |
| } |
| #endif |
| #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__ |
| else |
| #endif |
| #if defined __UCLIBC_HAS_IPV4__ |
| { |
| struct sockaddr_in *sinp = (struct sockaddr_in *) (*pai)->ai_addr; |
| |
| memcpy(&sinp->sin_addr, at2->addr, sizeof(struct in_addr)); |
| sinp->sin_port = st2->port; |
| memset(sinp->sin_zero, '\0', sizeof(sinp->sin_zero)); |
| } |
| #endif |
| if (c) { |
| (*pai)->ai_canonname = ((void *) (*pai) + |
| sizeof(struct addrinfo) + socklen); |
| strcpy((*pai)->ai_canonname, c); |
| } else { |
| (*pai)->ai_canonname = NULL; |
| } |
| (*pai)->ai_next = NULL; |
| pai = &((*pai)->ai_next); |
| } |
| |
| at2 = at2->next; |
| } |
| } |
| return 0; |
| } |
| |
| static const struct gaih gaih[] = { |
| #if defined __UCLIBC_HAS_IPV6__ |
| { PF_INET6, gaih_inet }, |
| #endif |
| { PF_INET, gaih_inet }, |
| #if 0 |
| { PF_LOCAL, gaih_local }, |
| #endif |
| { PF_UNSPEC, NULL } |
| }; |
| |
| void |
| freeaddrinfo(struct addrinfo *ai) |
| { |
| struct addrinfo *p; |
| |
| while (ai != NULL) { |
| p = ai; |
| ai = ai->ai_next; |
| free(p); |
| } |
| } |
| libc_hidden_def(freeaddrinfo) |
| |
| int |
| getaddrinfo(const char *name, const char *service, |
| const struct addrinfo *hints, struct addrinfo **pai) |
| { |
| int i, j, last_i; |
| struct addrinfo *p, **end; |
| const struct gaih *g, *pg; |
| struct gaih_service gaih_service, *pservice; |
| struct addrinfo default_hints; |
| |
| if (name != NULL && name[0] == '*' && name[1] == 0) |
| name = NULL; |
| |
| if (service != NULL && service[0] == '*' && service[1] == 0) |
| service = NULL; |
| |
| if (name == NULL && service == NULL) |
| return EAI_NONAME; |
| |
| if (hints == NULL) { |
| memset(&default_hints, 0, sizeof(default_hints)); |
| if (AF_UNSPEC != 0) |
| default_hints.ai_family = AF_UNSPEC; |
| hints = &default_hints; |
| } |
| |
| if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST| |
| AI_ADDRCONFIG|AI_V4MAPPED|AI_NUMERICSERV|AI_ALL)) |
| return EAI_BADFLAGS; |
| |
| if ((hints->ai_flags & AI_CANONNAME) && name == NULL) |
| return EAI_BADFLAGS; |
| |
| if (service && service[0]) { |
| char *c; |
| gaih_service.name = service; |
| gaih_service.num = strtoul(gaih_service.name, &c, 10); |
| if (*c != '\0') { |
| if (hints->ai_flags & AI_NUMERICSERV) |
| return EAI_NONAME; |
| gaih_service.num = -1; |
| } |
| pservice = &gaih_service; |
| } else |
| pservice = NULL; |
| |
| g = gaih; |
| pg = NULL; |
| p = NULL; |
| end = NULL; |
| if (pai) |
| end = &p; |
| i = 0; |
| last_i = 0; |
| j = 0; |
| while (g->gaih) { |
| if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC) { |
| if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family)) { |
| ++g; |
| continue; |
| } |
| j++; |
| if (pg == NULL || pg->gaih != g->gaih) { |
| pg = g; |
| i = g->gaih(name, pservice, hints, end); |
| if (i != 0) { |
| last_i = i; |
| if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC)) |
| continue; |
| /*if (p) - freeaddrinfo works ok on NULL too */ |
| freeaddrinfo(p); |
| return -(i & GAIH_EAI); |
| } |
| if (end) |
| while (*end) |
| end = &((*end)->ai_next); |
| } |
| } |
| ++g; |
| } |
| |
| if (j == 0) |
| return EAI_FAMILY; |
| |
| if (p) { |
| *pai = p; |
| return 0; |
| } |
| |
| if (pai == NULL && last_i == 0) |
| return 0; |
| |
| /* if (p) - never happens, see above */ |
| /* freeaddrinfo(p); */ |
| |
| return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME; |
| } |
| libc_hidden_def(getaddrinfo) |