zte's code,first commit
Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/build/uClibc/libc/inet/getaddrinfo.c b/ap/build/uClibc/libc/inet/getaddrinfo.c
new file mode 100644
index 0000000..88bd745
--- /dev/null
+++ b/ap/build/uClibc/libc/inet/getaddrinfo.c
@@ -0,0 +1,890 @@
+/*
+ * 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)