OSDN Git Service

mkostemp: fix implementation
[uclinux-h8/uClibc.git] / libc / inet / getaddrinfo.c
index a701667..168adb1 100644 (file)
@@ -1,6 +1,8 @@
 /*
- * Copyright 1996 by Craig Metz 
+ * 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.
  */
@@ -49,11 +51,12 @@ 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>
@@ -66,29 +69,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <sys/un.h>
 #include <sys/utsname.h>
 #include <net/if.h>
-
-libc_hidden_proto(memcpy)
-libc_hidden_proto(memset)
-/* libc_hidden_proto(strcmp) */
-/* libc_hidden_proto(stpcpy) */
-libc_hidden_proto(strchr)
-libc_hidden_proto(strcpy)
-libc_hidden_proto(strlen)
-libc_hidden_proto(socket)
-libc_hidden_proto(close)
-libc_hidden_proto(getservbyname_r)
-libc_hidden_proto(gethostbyname_r)
-libc_hidden_proto(gethostbyname2_r)
-libc_hidden_proto(gethostbyaddr_r)
-libc_hidden_proto(inet_pton)
-libc_hidden_proto(inet_ntop)
-libc_hidden_proto(strtoul)
-libc_hidden_proto(if_nametoindex)
-libc_hidden_proto(__h_errno_location)
-/* libc_hidden_proto(uname) */
-#ifdef __UCLIBC_HAS_IPV6__
-libc_hidden_proto(in6addr_loopback)
-#endif
+#include <ifaddrs.h>
 
 #define GAIH_OKIFUNSPEC 0x0100
 #define GAIH_EAI        ~(GAIH_OKIFUNSPEC)
@@ -97,802 +78,811 @@ libc_hidden_proto(in6addr_loopback)
 #define UNIX_PATH_MAX  108
 #endif
 
-struct gaih_service
-{
-    const char *name;
-    int num;
+/* 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_servtuple
-{
-    struct gaih_servtuple *next;
-    int socktype;
-    int protocol;
-    int port;
+struct gaih_service {
+       const char *name;
+       int num;
 };
 
-static const struct gaih_servtuple nullserv;
-
-struct gaih_addrtuple
-{
-    struct gaih_addrtuple *next;
-    int family;
-    char addr[16];
-    uint32_t scopeid;
+struct gaih_servtuple {
+       struct gaih_servtuple *next;
+       int socktype;
+       int protocol;
+       int port;
 };
 
-struct gaih_typeproto
-{
-    int socktype;
-    int protocol;
-    char name[4];
-    int protoflag;
+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
+#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, ""    },
+};
 
-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);
 };
 
-struct gaih
+#define SEEN_IPV4 1
+#define SEEN_IPV6 2
+
+static unsigned __check_pf(void)
 {
-    int family;
-    int (*gaih)(const char *name, const struct gaih_service *service,
-               const struct addrinfo *req, struct addrinfo **pai);
-};
+       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);
 
-#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 };
+
+       /* 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)
+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;
+       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)
+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);
+       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);
        }
-    }
-
-    *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 (req->ai_protocol || req->ai_socktype) {
+               const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
 
-    if (service)
-    {
-       struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
+               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);
+               }
+       }
 
-       if (strchr (service->name, '/') != NULL)
-       {
-           if (strlen (service->name) >= sizeof (sunp->sun_path))
-               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 0 /* SALEN */
+       ((struct sockaddr_un *)ai->ai_addr)->sun_len = sizeof(struct sockaddr_un);
+#endif /* SALEN */
 
-           strcpy (sunp->sun_path, service->name);
-       }
-       else
-       {
-           if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
-               sizeof (sunp->sun_path))
-               return GAIH_OKIFUNSPEC | -EAI_SERVICE;
+       ((struct sockaddr_un *)ai->ai_addr)->sun_family = AF_LOCAL;
+       memset(((struct sockaddr_un *)ai->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
 
-           stpcpy (stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
+       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) != 0
+               ) {
+                       return -EAI_SYSTEM;
+               }
        }
-    }
-    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;
+
+       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,
+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)
+       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;
-           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;
+       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);                                  \
-       }                                                               \
-    }                                                                  \
+/* 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)
+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);
+       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();
        }
-    }
-
-    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);
+       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 (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);
+               if (req->ai_socktype)
+                       return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
+               return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+ found: ;
        }
-    }
-    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;
+       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;
+               }
        }
-    }
-
-    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;
-       }
+       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 __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;
-                   }
+#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) == 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);
+               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))))
-               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));
+                       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 (req->ai_family == 0)
-       {
-           at->next = alloca (sizeof (struct gaih_addrtuple));
-           memset (at->next, '\0', sizeof (struct gaih_addrtuple));
-       }
+               if (at->family == AF_UNSPEC)
+                       return (GAIH_OKIFUNSPEC | -EAI_NONAME);
+       } else {
+               struct gaih_addrtuple *atr;
 
-#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;
-       }
+               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 *) atr->addr = htonl (INADDR_LOOPBACK);
+               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"];
+       if (pai == NULL)
+               return 0;
 
-       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);
+               const char *c = NULL;
+               struct gaih_servtuple *st2;
+               struct gaih_addrtuple *at2 = at;
+               size_t socklen, namelen;
+               sa_family_t family;
 
-               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
+               /*
+                * 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
-           {
-               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
+                                               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
-               {
-                   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);
+#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 0 /* 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;
                }
-               else
-                   (*pai)->ai_canonname = NULL;
-
-               (*pai)->ai_next = NULL;
-               pai = &((*pai)->ai_next);
-           }
-
-           at2 = at2->next;
        }
-    }
-    return 0;
+       return 0;
 }
 
-static struct gaih gaih[] =
-{
-#if __UCLIBC_HAS_IPV6__
-    { PF_INET6, gaih_inet },
+static const struct gaih gaih[] = {
+#if defined __UCLIBC_HAS_IPV6__
+       { PF_INET6, gaih_inet },
 #endif
-    { PF_INET, gaih_inet },
+       { PF_INET, gaih_inet },
 #if 0
-    { PF_LOCAL, gaih_local },
+       { PF_LOCAL, gaih_local },
 #endif
-    { PF_UNSPEC, NULL }
+       { PF_UNSPEC, NULL }
 };
 
-libc_hidden_proto(freeaddrinfo)
 void
-freeaddrinfo (struct addrinfo *ai)
+freeaddrinfo(struct addrinfo *ai)
 {
-    struct addrinfo *p;
-
-    while (ai != NULL)
-    {
-       p = ai;
-       ai = ai->ai_next;
-       free (p);
-    }
+       struct addrinfo *p;
+
+       while (ai != NULL) {
+               p = ai;
+               ai = ai->ai_next;
+               free(p);
+       }
 }
 libc_hidden_def(freeaddrinfo)
 
-libc_hidden_proto(getaddrinfo)
 int
-getaddrinfo (const char *name, const char *service,
+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_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;
+       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;
        }
-       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);
+       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;
 
-                   return -(i & GAIH_EAI);
+       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);
+                       }
                }
-               if (end)
-                   while(*end) end = &((*end)->ai_next);
-           }
+               ++g;
        }
-       ++g;
-    }
 
-    if (j == 0)
-       return EAI_FAMILY;
+       if (j == 0)
+               return EAI_FAMILY;
 
-    if (p)
-    {
-       *pai = p;
-       return 0;
-    }
+       if (p) {
+               *pai = p;
+               return 0;
+       }
 
-    if (pai == NULL && last_i == 0)
-       return 0;
+       if (pai == NULL && last_i == 0)
+               return 0;
 
-    if (p)
-       freeaddrinfo (p);
+       /* if (p) - never happens, see above */
+       /*      freeaddrinfo(p); */
 
-    return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
+       return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
 }
 libc_hidden_def(getaddrinfo)