diff options
author | Eric Andersen <andersen@codepoet.org> | 2002-03-19 11:28:17 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2002-03-19 11:28:17 +0000 |
commit | 54d956c541aa6ea5a8e39d3db8bb3d4f3c9f4bb2 (patch) | |
tree | aadc83ae6ad21f6955b25ca5ab679e5c4d32304c /libc/inet/getaddrinfo.c | |
parent | 8c4fbefcd9afcd0f3a8776eb8309ee2ed91ba970 (diff) |
Bart Visscher <magick@Linux-Fan.com> has added some missing IPV6 support, and
added several additional reentrant networking functions such that iptables now
runs with IPV6 support.
Diffstat (limited to 'libc/inet/getaddrinfo.c')
-rw-r--r-- | libc/inet/getaddrinfo.c | 863 |
1 files changed, 863 insertions, 0 deletions
diff --git a/libc/inet/getaddrinfo.c b/libc/inet/getaddrinfo.c new file mode 100644 index 000000000..6a068442a --- /dev/null +++ b/libc/inet/getaddrinfo.c @@ -0,0 +1,863 @@ +/* $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. */ + +/* This software is Copyright 1996 by Craig Metz, All Rights Reserved. */ + +#define _GNU_SOURCE +#define __FORCE_GLIBC +#include <features.h> +#include <assert.h> +#include <errno.h> +#include <netdb.h> +#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> + +#define GAIH_OKIFUNSPEC 0x0100 +#define GAIH_EAI ~(GAIH_OKIFUNSPEC) + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +struct gaih_service +{ + const char *name; + int num; +}; + +struct gaih_servtuple +{ + struct gaih_servtuple *next; + int socktype; + int protocol; + int port; +}; + +static const struct gaih_servtuple nullserv; + +struct gaih_addrtuple +{ + struct gaih_addrtuple *next; + int family; + char addr[16]; + uint32_t scopeid; +}; + +struct gaih_typeproto +{ + int socktype; + int protocol; + char name[4]; + int protoflag; +}; + +/* 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, "tcp", 0 }, + { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 }, + { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE }, + { 0, 0, "", 0 } +}; + +struct gaih +{ + int family; + int (*gaih)(const char *name, const struct gaih_service *service, + const struct addrinfo *req, struct addrinfo **pai); +}; + +#if PF_UNSPEC == 0 +static const struct addrinfo default_hints; +#else +static const struct addrinfo default_hints = +{ 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL }; +#endif + + +static int addrconfig (sa_family_t af) +{ + int s; + int ret; + int saved_errno = errno; + s = socket(af, SOCK_DGRAM, 0); + if (s < 0) + ret = (errno == EMFILE) ? 1 : 0; + else + { + close(s); + ret = 1; + } + __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; + + 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); + else + return (GAIH_OKIFUNSPEC | -EAI_SERVICE); + } + } + + *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un) + + ((req->ai_flags & AI_CANONNAME) + ? (strlen(utsname.nodename) + 1): 0)); + if (*pai == NULL) + return -EAI_MEMORY; + + (*pai)->ai_next = NULL; + (*pai)->ai_flags = req->ai_flags; + (*pai)->ai_family = AF_LOCAL; + (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM; + (*pai)->ai_protocol = req->ai_protocol; + (*pai)->ai_addrlen = sizeof (struct sockaddr_un); + (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo); + +#if SALEN + ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len = + sizeof (struct sockaddr_un); +#endif /* SALEN */ + + ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL; + memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX); + + if (service) + { + struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->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 *) (*pai)->ai_addr)->sun_path; + + if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0), + 0) != 0 + || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0) + return -EAI_SYSTEM; + } + + if (req->ai_flags & AI_CANONNAME) + (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo) + + sizeof (struct sockaddr_un), + utsname.nodename); + else + (*pai)->ai_canonname = NULL; + 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; + + do + { + tmpbuf = alloca (tmpbuflen); + + r = getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen, + &s); + if (r != 0 || s == NULL) + { + if (r == ERANGE) + tmpbuflen *= 2; + else + return GAIH_OKIFUNSPEC | -EAI_SERVICE; + } + } + while (r); + + 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; +} + +#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) +{ + const struct gaih_typeproto *tp = gaih_inet_typeproto; + struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv; + struct gaih_addrtuple *at = NULL; + int rc; + int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6) && + (req->ai_flags & AI_V4MAPPED); + + if (req->ai_protocol || req->ai_socktype) + { + ++tp; + + while (tp->name[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); + else + return (GAIH_OKIFUNSPEC | -EAI_SERVICE); + } + } + + if (service != NULL) + { + if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) + return (GAIH_OKIFUNSPEC | -EAI_SERVICE); + + if (service->num < 0) + { + if (tp->name[0]) + { + st = (struct gaih_servtuple *) + alloca (sizeof (struct gaih_servtuple)); + + if ((rc = gaih_inet_serv (service->name, tp, req, st))) + 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 = (struct gaih_servtuple *) + alloca (sizeof (struct gaih_servtuple)); + + if ((rc = gaih_inet_serv (service->name, tp, req, newp))) + { + if (rc & GAIH_OKIFUNSPEC) + continue; + return rc; + } + + *pst = newp; + pst = &(newp->next); + } + if (st == (struct gaih_servtuple *) &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; + } + } + + 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) + at->family = AF_INET; + else + return -EAI_FAMILY; + } + +#if __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) + at->family = AF_INET6; + else + return -EAI_FAMILY; + + if (scope_delim != NULL) + { + int try_numericscope = 0; + if (IN6_IS_ADDR_LINKLOCAL (at->addr) + || 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) == 0) + { + 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 __UCLIBC_HAS_IPV6__ + if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) + 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)))) + 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 __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 *) atr->addr = 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) + { + if (req->ai_flags & AI_CANONNAME) + { + struct hostent *h = NULL; + + int herrno; + struct hostent th; + size_t tmpbuflen = 512; + char *tmpbuf; + + do + { + tmpbuflen *= 2; + tmpbuf = alloca (tmpbuflen); + + if (tmpbuf == NULL) + return -EAI_MEMORY; + + rc = gethostbyaddr_r (at2->addr, + ((at2->family == AF_INET6) + ? sizeof(struct in6_addr) + : sizeof(struct in_addr)), + at2->family, &th, tmpbuf, tmpbuflen, + &h, &herrno); + + } + while (rc == errno && herrno == NETDB_INTERNAL); + + if (rc != 0 && herrno == NETDB_INTERNAL) + { + __set_h_errno (herrno); + return -EAI_SYSTEM; + } + + if (h == NULL) + c = inet_ntop (at2->family, at2->addr, buffer, sizeof(buffer)); + else + c = h->h_name; + + if (c == NULL) + return GAIH_OKIFUNSPEC | -EAI_NONAME; + + namelen = strlen (c) + 1; + } + else + namelen = 0; + +#if __UCLIBC_HAS_IPV6__ + if (at2->family == AF_INET6 || v4mapped) + { + family = AF_INET6; + socklen = sizeof (struct sockaddr_in6); + } + else +#endif + { + family = AF_INET; + socklen = sizeof (struct sockaddr_in); + } + + for (st2 = st; st2 != NULL; st2 = st2->next) + { + *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 SALEN + (*pai)->ai_addr->sa_len = socklen; +#endif /* SALEN */ + (*pai)->ai_addr->sa_family = family; + +#if __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; + } + else +#endif + { + 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)); + } + + 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 struct gaih gaih[] = +{ +#if __UCLIBC_HAS_IPV6__ + { PF_INET6, gaih_inet }, +#endif + { PF_INET, gaih_inet }, +#if 0 + { PF_LOCAL, gaih_local }, +#endif + { PF_UNSPEC, NULL } +}; + +int +getaddrinfo (const char *name, const char *service, + const struct addrinfo *hints, struct addrinfo **pai) +{ + int i = 0, j = 0, last_i = 0; + struct addrinfo *p = NULL, **end; + struct gaih *g = gaih, *pg = NULL; + struct gaih_service gaih_service, *pservice; + + 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) + hints = &default_hints; + + if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST| + AI_ADDRCONFIG|AI_V4MAPPED|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) + gaih_service.num = -1; + else + /* + * Can't specify a numerical socket unless a protocol + * family was given. + */ + if (hints->ai_socktype == 0 && hints->ai_protocol == 0) + return EAI_SERVICE; + pservice = &gaih_service; + } + else + pservice = NULL; + + if (pai) + end = &p; + else + end = NULL; + + while (g->gaih) + { + if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC) + { + if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family)) + 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 (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) + freeaddrinfo (p); + + return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME; +} + +void +freeaddrinfo (struct addrinfo *ai) +{ + struct addrinfo *p; + + while (ai != NULL) + { + p = ai; + ai = ai->ai_next; + free (p); + } +} |