OpenCores
URL https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk

Subversion Repositories openrisc_me

[/] [openrisc/] [trunk/] [gnu-src/] [newlib-1.17.0/] [newlib/] [libc/] [sys/] [linux/] [net/] [getaddrinfo.c] - Diff between revs 148 and 158

Only display areas with differences | Details | Blame | View Log

Rev 148 Rev 158
/* The Inner Net License, Version 2.00
/* The Inner Net License, Version 2.00
 
 
  The author(s) grant permission for redistribution and use in source and
  The author(s) grant permission for redistribution and use in source and
binary forms, with or without modification, of the software and documentation
binary forms, with or without modification, of the software and documentation
provided that the following conditions are met:
provided that the following conditions are met:
 
 
0. If you receive a version of the software that is specifically labelled
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),
   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
   you are not permitted to redistribute that version of the software in any
   way or form.
   way or form.
1. All terms of the all other applicable copyrights and licenses must be
1. All terms of the all other applicable copyrights and licenses must be
   followed.
   followed.
2. Redistributions of source code must retain the authors' copyright
2. Redistributions of source code must retain the authors' copyright
   notice(s), this list of conditions, and the following disclaimer.
   notice(s), this list of conditions, and the following disclaimer.
3. Redistributions in binary form must reproduce the authors' copyright
3. Redistributions in binary form must reproduce the authors' copyright
   notice(s), this list of conditions, and the following disclaimer in the
   notice(s), this list of conditions, and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
   documentation and/or other materials provided with the distribution.
4. [The copyright holder has authorized the removal of this clause.]
4. [The copyright holder has authorized the removal of this clause.]
5. Neither the name(s) of the author(s) nor the names of its 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
   may be used to endorse or promote products derived from this software
   without specific prior written permission.
   without specific prior written permission.
 
 
THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
  If these license terms cause you a real problem, contact the author.  */
  If these license terms cause you a real problem, contact the author.  */
 
 
/* This software is Copyright 1996 by Craig Metz, All Rights Reserved.  */
/* This software is Copyright 1996 by Craig Metz, All Rights Reserved.  */
 
 
#include <alloca.h>
#include <alloca.h>
#include <sys/types.h>
#include <sys/types.h>
#include <assert.h>
#include <assert.h>
#include <errno.h>
#include <errno.h>
#include <ifaddrs.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <netdb.h>
#include <resolv.h>
#include <resolv.h>
#include <stdbool.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <unistd.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <sys/utsname.h>
#include <net/if.h>
#include <net/if.h>
#include <nsswitch.h>
#include <nsswitch.h>
#include <not-cancel.h>
#include <not-cancel.h>
#include <nscd/nscd-client.h>
#include <nscd/nscd-client.h>
#include <nscd/nscd_proto.h>
#include <nscd/nscd_proto.h>
#include <limits.h>
#include <limits.h>
#include "local.h"
#include "local.h"
 
 
#ifdef HAVE_LIBIDN
#ifdef HAVE_LIBIDN
extern int __idna_to_ascii_lz (const char *input, char **output, int flags);
extern int __idna_to_ascii_lz (const char *input, char **output, int flags);
extern int __idna_to_unicode_lzlz (const char *input, char **output,
extern int __idna_to_unicode_lzlz (const char *input, char **output,
                                   int flags);
                                   int flags);
# include <libidn/idna.h>
# include <libidn/idna.h>
#endif
#endif
 
 
#define GAIH_OKIFUNSPEC 0x0100
#define GAIH_OKIFUNSPEC 0x0100
#define GAIH_EAI        ~(GAIH_OKIFUNSPEC)
#define GAIH_EAI        ~(GAIH_OKIFUNSPEC)
 
 
#ifndef UNIX_PATH_MAX
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX  108
#define UNIX_PATH_MAX  108
#endif
#endif
 
 
struct gaih_service
struct gaih_service
  {
  {
    const char *name;
    const char *name;
    int num;
    int num;
  };
  };
 
 
struct gaih_servtuple
struct gaih_servtuple
  {
  {
    struct gaih_servtuple *next;
    struct gaih_servtuple *next;
    int socktype;
    int socktype;
    int protocol;
    int protocol;
    int port;
    int port;
  };
  };
 
 
static const struct gaih_servtuple nullserv;
static const struct gaih_servtuple nullserv;
 
 
struct gaih_addrtuple
struct gaih_addrtuple
  {
  {
    struct gaih_addrtuple *next;
    struct gaih_addrtuple *next;
    char *name;
    char *name;
    int family;
    int family;
    uint32_t addr[4];
    uint32_t addr[4];
    uint32_t scopeid;
    uint32_t scopeid;
  };
  };
 
 
struct gaih_typeproto
struct gaih_typeproto
  {
  {
    int socktype;
    int socktype;
    int protocol;
    int protocol;
    char name[4];
    char name[4];
    int protoflag;
    int protoflag;
  };
  };
 
 
/* Values for `protoflag'.  */
/* Values for `protoflag'.  */
#define GAI_PROTO_NOSERVICE     1
#define GAI_PROTO_NOSERVICE     1
#define GAI_PROTO_PROTOANY      2
#define GAI_PROTO_PROTOANY      2
 
 
static const struct gaih_typeproto gaih_inet_typeproto[] =
static const struct gaih_typeproto gaih_inet_typeproto[] =
{
{
  { 0, 0, "", 0 },
  { 0, 0, "", 0 },
  { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
  { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
  { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
  { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
  { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
  { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
  { 0, 0, "", 0 }
  { 0, 0, "", 0 }
};
};
 
 
struct gaih
struct gaih
  {
  {
    int family;
    int family;
    int (*gaih)(const char *name, const struct gaih_service *service,
    int (*gaih)(const char *name, const struct gaih_service *service,
                const struct addrinfo *req, struct addrinfo **pai);
                const struct addrinfo *req, struct addrinfo **pai);
  };
  };
 
 
static const struct addrinfo default_hints =
static const struct addrinfo default_hints =
  {
  {
    .ai_flags = AI_DEFAULT,
    .ai_flags = AI_DEFAULT,
    .ai_family = PF_UNSPEC,
    .ai_family = PF_UNSPEC,
    .ai_socktype = 0,
    .ai_socktype = 0,
    .ai_protocol = 0,
    .ai_protocol = 0,
    .ai_addrlen = 0,
    .ai_addrlen = 0,
    .ai_addr = NULL,
    .ai_addr = NULL,
    .ai_canonname = NULL,
    .ai_canonname = NULL,
    .ai_next = NULL
    .ai_next = NULL
  };
  };
 
 
#define s6_addr32 __u6_addr.__u6_addr32
#define s6_addr32 __u6_addr.__u6_addr32
 
 
#if 0
#if 0
/* Using Unix sockets this way is a security risk.  */
/* Using Unix sockets this way is a security risk.  */
static int
static int
gaih_local (const char *name, const struct gaih_service *service,
gaih_local (const char *name, const struct gaih_service *service,
            const struct addrinfo *req, struct addrinfo **pai)
            const struct addrinfo *req, struct addrinfo **pai)
{
{
  struct utsname utsname;
  struct utsname utsname;
 
 
  if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
  if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
    return GAIH_OKIFUNSPEC | -EAI_NONAME;
    return GAIH_OKIFUNSPEC | -EAI_NONAME;
 
 
  if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
  if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
    if (uname (&utsname) < 0)
    if (uname (&utsname) < 0)
      return -EAI_SYSTEM;
      return -EAI_SYSTEM;
 
 
  if (name != NULL)
  if (name != NULL)
    {
    {
      if (strcmp(name, "localhost") &&
      if (strcmp(name, "localhost") &&
          strcmp(name, "local") &&
          strcmp(name, "local") &&
          strcmp(name, "unix") &&
          strcmp(name, "unix") &&
          strcmp(name, utsname.nodename))
          strcmp(name, utsname.nodename))
        return GAIH_OKIFUNSPEC | -EAI_NONAME;
        return GAIH_OKIFUNSPEC | -EAI_NONAME;
    }
    }
 
 
  if (req->ai_protocol || req->ai_socktype)
  if (req->ai_protocol || req->ai_socktype)
    {
    {
      const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
      const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
 
 
      while (tp->name[0]
      while (tp->name[0]
             && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
             && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
                 || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
                 || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
                 || (req->ai_protocol != 0
                 || (req->ai_protocol != 0
                     && !(tp->protoflag & GAI_PROTO_PROTOANY)
                     && !(tp->protoflag & GAI_PROTO_PROTOANY)
                     && req->ai_protocol != tp->protocol)))
                     && req->ai_protocol != tp->protocol)))
        ++tp;
        ++tp;
 
 
      if (! tp->name[0])
      if (! tp->name[0])
        {
        {
          if (req->ai_socktype)
          if (req->ai_socktype)
            return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
            return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
          else
          else
            return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
            return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
        }
        }
    }
    }
 
 
  *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
  *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
                 + ((req->ai_flags & AI_CANONNAME)
                 + ((req->ai_flags & AI_CANONNAME)
                    ? (strlen(utsname.nodename) + 1): 0));
                    ? (strlen(utsname.nodename) + 1): 0));
  if (*pai == NULL)
  if (*pai == NULL)
    return -EAI_MEMORY;
    return -EAI_MEMORY;
 
 
  (*pai)->ai_next = NULL;
  (*pai)->ai_next = NULL;
  (*pai)->ai_flags = req->ai_flags;
  (*pai)->ai_flags = req->ai_flags;
  (*pai)->ai_family = AF_LOCAL;
  (*pai)->ai_family = AF_LOCAL;
  (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
  (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
  (*pai)->ai_protocol = req->ai_protocol;
  (*pai)->ai_protocol = req->ai_protocol;
  (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
  (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
  (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
  (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
 
 
#if SALEN
#if SALEN
  ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
  ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
    sizeof (struct sockaddr_un);
    sizeof (struct sockaddr_un);
#endif /* SALEN */
#endif /* SALEN */
 
 
  ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
  ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
  memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
  memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
 
 
  if (service)
  if (service)
    {
    {
      struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
      struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
 
 
      if (strchr (service->name, '/') != NULL)
      if (strchr (service->name, '/') != NULL)
        {
        {
          if (strlen (service->name) >= sizeof (sunp->sun_path))
          if (strlen (service->name) >= sizeof (sunp->sun_path))
            return GAIH_OKIFUNSPEC | -EAI_SERVICE;
            return GAIH_OKIFUNSPEC | -EAI_SERVICE;
 
 
          strcpy (sunp->sun_path, service->name);
          strcpy (sunp->sun_path, service->name);
        }
        }
      else
      else
        {
        {
          if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
          if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
              sizeof (sunp->sun_path))
              sizeof (sunp->sun_path))
            return GAIH_OKIFUNSPEC | -EAI_SERVICE;
            return GAIH_OKIFUNSPEC | -EAI_SERVICE;
 
 
          __stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
          __stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
        }
        }
    }
    }
  else
  else
    {
    {
      /* This is a dangerous use of the interface since there is a time
      /* This is a dangerous use of the interface since there is a time
         window between the test for the file and the actual creation
         window between the test for the file and the actual creation
         (done by the caller) in which a file with the same name could
         (done by the caller) in which a file with the same name could
         be created.  */
         be created.  */
      char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
      char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
 
 
      if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
      if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
                            0) != 0
                            0) != 0
          || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
          || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
        return -EAI_SYSTEM;
        return -EAI_SYSTEM;
    }
    }
 
 
  if (req->ai_flags & AI_CANONNAME)
  if (req->ai_flags & AI_CANONNAME)
    (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
    (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
                                   + sizeof (struct sockaddr_un),
                                   + sizeof (struct sockaddr_un),
                                   utsname.nodename);
                                   utsname.nodename);
  else
  else
    (*pai)->ai_canonname = NULL;
    (*pai)->ai_canonname = NULL;
  return 0;
  return 0;
}
}
#endif  /* 0 */
#endif  /* 0 */
 
 
static int
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)
               const struct addrinfo *req, struct gaih_servtuple *st)
{
{
  struct servent *s;
  struct servent *s;
  size_t tmpbuflen = 1024;
  size_t tmpbuflen = 1024;
  struct servent ts;
  struct servent ts;
  char *tmpbuf;
  char *tmpbuf;
  int r;
  int r;
 
 
  do
  do
    {
    {
      tmpbuf = alloca (tmpbuflen);
      tmpbuf = alloca (tmpbuflen);
 
 
      r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
      r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
                             &s);
                             &s);
      if (r != 0 || s == NULL)
      if (r != 0 || s == NULL)
        {
        {
          if (r == ERANGE)
          if (r == ERANGE)
            tmpbuflen *= 2;
            tmpbuflen *= 2;
          else
          else
            return GAIH_OKIFUNSPEC | -EAI_SERVICE;
            return GAIH_OKIFUNSPEC | -EAI_SERVICE;
        }
        }
    }
    }
  while (r);
  while (r);
 
 
  st->next = NULL;
  st->next = NULL;
  st->socktype = tp->socktype;
  st->socktype = tp->socktype;
  st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
  st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
                  ? req->ai_protocol : tp->protocol);
                  ? req->ai_protocol : tp->protocol);
  st->port = s->s_port;
  st->port = s->s_port;
 
 
  return 0;
  return 0;
}
}
 
 
#define gethosts(_family, _type) \
#define gethosts(_family, _type) \
 {                                                                            \
 {                                                                            \
  int i;                                                                      \
  int i;                                                                      \
  int herrno;                                                                 \
  int herrno;                                                                 \
  struct hostent th;                                                          \
  struct hostent th;                                                          \
  struct hostent *h;                                                          \
  struct hostent *h;                                                          \
  char *localcanon = NULL;                                                    \
  char *localcanon = NULL;                                                    \
  no_data = 0;                                                                 \
  no_data = 0;                                                                 \
  while (1) {                                                                 \
  while (1) {                                                                 \
    rc = 0;                                                                    \
    rc = 0;                                                                    \
    status = DL_CALL_FCT (fct, (name, _family, &th, tmpbuf, tmpbuflen,        \
    status = DL_CALL_FCT (fct, (name, _family, &th, tmpbuf, tmpbuflen,        \
                                &rc, &herrno, NULL, &localcanon));            \
                                &rc, &herrno, NULL, &localcanon));            \
    if (rc != ERANGE || herrno != NETDB_INTERNAL)                             \
    if (rc != ERANGE || herrno != NETDB_INTERNAL)                             \
      break;                                                                  \
      break;                                                                  \
    tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);                \
    tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);                \
  }                                                                           \
  }                                                                           \
  if (status == NSS_STATUS_SUCCESS && rc == 0)                                 \
  if (status == NSS_STATUS_SUCCESS && rc == 0)                                 \
    h = &th;                                                                  \
    h = &th;                                                                  \
  else                                                                        \
  else                                                                        \
    h = NULL;                                                                 \
    h = NULL;                                                                 \
  if (rc != 0)                                                                 \
  if (rc != 0)                                                                 \
    {                                                                         \
    {                                                                         \
      if (herrno == NETDB_INTERNAL)                                           \
      if (herrno == NETDB_INTERNAL)                                           \
        {                                                                     \
        {                                                                     \
          h_errno = (herrno);                                         \
          h_errno = (herrno);                                         \
          return -EAI_SYSTEM;                                                 \
          return -EAI_SYSTEM;                                                 \
        }                                                                     \
        }                                                                     \
      if (herrno == TRY_AGAIN)                                                \
      if (herrno == TRY_AGAIN)                                                \
        no_data = EAI_AGAIN;                                                  \
        no_data = EAI_AGAIN;                                                  \
      else                                                                    \
      else                                                                    \
        no_data = herrno == NO_DATA;                                          \
        no_data = herrno == NO_DATA;                                          \
    }                                                                         \
    }                                                                         \
  else if (h != NULL)                                                         \
  else if (h != NULL)                                                         \
    {                                                                         \
    {                                                                         \
      for (i = 0; h->h_addr_list[i]; i++)                                      \
      for (i = 0; h->h_addr_list[i]; i++)                                      \
        {                                                                     \
        {                                                                     \
          if (*pat == NULL)                                                   \
          if (*pat == NULL)                                                   \
            {                                                                 \
            {                                                                 \
              *pat = alloca (sizeof (struct gaih_addrtuple));                 \
              *pat = alloca (sizeof (struct gaih_addrtuple));                 \
              (*pat)->scopeid = 0;                                             \
              (*pat)->scopeid = 0;                                             \
            }                                                                 \
            }                                                                 \
          uint32_t *addr = (*pat)->addr;                                      \
          uint32_t *addr = (*pat)->addr;                                      \
          (*pat)->next = NULL;                                                \
          (*pat)->next = NULL;                                                \
          if (i == 0)                                                          \
          if (i == 0)                                                          \
            {                                                                 \
            {                                                                 \
              (*pat)->name = alloca (strlen (h->h_name) + 1);                 \
              (*pat)->name = alloca (strlen (h->h_name) + 1);                 \
              strcpy ((*pat)->name, h->h_name);                               \
              strcpy ((*pat)->name, h->h_name);                               \
            }                                                                 \
            }                                                                 \
          else                                                                \
          else                                                                \
              (*pat)->name = NULL;                                            \
              (*pat)->name = NULL;                                            \
          if (_family == AF_INET && req->ai_family == AF_INET6)               \
          if (_family == AF_INET && req->ai_family == AF_INET6)               \
            {                                                                 \
            {                                                                 \
              (*pat)->family = AF_INET6;                                      \
              (*pat)->family = AF_INET6;                                      \
              addr[3] = *(uint32_t *) h->h_addr_list[i];                      \
              addr[3] = *(uint32_t *) h->h_addr_list[i];                      \
              addr[2] = htonl (0xffff);                                       \
              addr[2] = htonl (0xffff);                                       \
              addr[1] = 0;                                                     \
              addr[1] = 0;                                                     \
              addr[0] = 0;                                                      \
              addr[0] = 0;                                                      \
            }                                                                 \
            }                                                                 \
          else                                                                \
          else                                                                \
            {                                                                 \
            {                                                                 \
              (*pat)->family = _family;                                       \
              (*pat)->family = _family;                                       \
              memcpy (addr, h->h_addr_list[i], sizeof(_type));                \
              memcpy (addr, h->h_addr_list[i], sizeof(_type));                \
            }                                                                 \
            }                                                                 \
          pat = &((*pat)->next);                                              \
          pat = &((*pat)->next);                                              \
        }                                                                     \
        }                                                                     \
                                                                              \
                                                                              \
      if (localcanon != NULL && canon == NULL)                                \
      if (localcanon != NULL && canon == NULL)                                \
        {                                                                     \
        {                                                                     \
          canon = alloca (strlen (localcanon) + 1);                           \
          canon = alloca (strlen (localcanon) + 1);                           \
          strcpy (canon, localcanon);                                         \
          strcpy (canon, localcanon);                                         \
        }                                                                     \
        }                                                                     \
                                                                              \
                                                                              \
      if (_family == AF_INET6 && i > 0)                                        \
      if (_family == AF_INET6 && i > 0)                                        \
        got_ipv6 = true;                                                      \
        got_ipv6 = true;                                                      \
    }                                                                         \
    }                                                                         \
 }
 }
 
 
 
 
typedef enum nss_status (*nss_gethostbyname3_r)
typedef enum nss_status (*nss_gethostbyname3_r)
  (const char *name, int af, struct hostent *host,
  (const char *name, int af, struct hostent *host,
   char *buffer, size_t buflen, int *errnop,
   char *buffer, size_t buflen, int *errnop,
   int *h_errnop, int32_t *ttlp, char **canonp);
   int *h_errnop, int32_t *ttlp, char **canonp);
typedef enum nss_status (*nss_getcanonname_r)
typedef enum nss_status (*nss_getcanonname_r)
  (const char *name, char *buffer, size_t buflen, char **result,
  (const char *name, char *buffer, size_t buflen, char **result,
   int *errnop, int *h_errnop);
   int *errnop, int *h_errnop);
extern service_user *__nss_hosts_database attribute_hidden;
extern service_user *__nss_hosts_database attribute_hidden;
 
 
static int
static int
gaih_inet (const char *name, const struct gaih_service *service,
gaih_inet (const char *name, const struct gaih_service *service,
           const struct addrinfo *req, struct addrinfo **pai)
           const struct addrinfo *req, struct addrinfo **pai)
{
{
  const struct gaih_typeproto *tp = gaih_inet_typeproto;
  const struct gaih_typeproto *tp = gaih_inet_typeproto;
  struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
  struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
  struct gaih_addrtuple *at = NULL;
  struct gaih_addrtuple *at = NULL;
  int rc;
  int rc;
  bool got_ipv6 = false;
  bool got_ipv6 = false;
  const char *canon = NULL;
  const char *canon = NULL;
  const char *orig_name = name;
  const char *orig_name = name;
 
 
  if (req->ai_protocol || req->ai_socktype)
  if (req->ai_protocol || req->ai_socktype)
    {
    {
      ++tp;
      ++tp;
 
 
      while (tp->name[0]
      while (tp->name[0]
             && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
             && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
                 || (req->ai_protocol != 0
                 || (req->ai_protocol != 0
                     && !(tp->protoflag & GAI_PROTO_PROTOANY)
                     && !(tp->protoflag & GAI_PROTO_PROTOANY)
                     && req->ai_protocol != tp->protocol)))
                     && req->ai_protocol != tp->protocol)))
        ++tp;
        ++tp;
 
 
      if (! tp->name[0])
      if (! tp->name[0])
        {
        {
          if (req->ai_socktype)
          if (req->ai_socktype)
            return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
            return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
          else
          else
            return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
            return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
        }
        }
    }
    }
 
 
  if (service != NULL)
  if (service != NULL)
    {
    {
      if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
      if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
        return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
        return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
 
 
      if (service->num < 0)
      if (service->num < 0)
        {
        {
          if (tp->name[0])
          if (tp->name[0])
            {
            {
              st = (struct gaih_servtuple *)
              st = (struct gaih_servtuple *)
                alloca (sizeof (struct gaih_servtuple));
                alloca (sizeof (struct gaih_servtuple));
 
 
              if ((rc = gaih_inet_serv (service->name, tp, req, st)))
              if ((rc = gaih_inet_serv (service->name, tp, req, st)))
                return rc;
                return rc;
            }
            }
          else
          else
            {
            {
              struct gaih_servtuple **pst = &st;
              struct gaih_servtuple **pst = &st;
              for (tp++; tp->name[0]; tp++)
              for (tp++; tp->name[0]; tp++)
                {
                {
                  struct gaih_servtuple *newp;
                  struct gaih_servtuple *newp;
 
 
                  if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
                  if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
                    continue;
                    continue;
 
 
                  if (req->ai_socktype != 0
                  if (req->ai_socktype != 0
                      && req->ai_socktype != tp->socktype)
                      && req->ai_socktype != tp->socktype)
                    continue;
                    continue;
                  if (req->ai_protocol != 0
                  if (req->ai_protocol != 0
                      && !(tp->protoflag & GAI_PROTO_PROTOANY)
                      && !(tp->protoflag & GAI_PROTO_PROTOANY)
                      && req->ai_protocol != tp->protocol)
                      && req->ai_protocol != tp->protocol)
                    continue;
                    continue;
 
 
                  newp = (struct gaih_servtuple *)
                  newp = (struct gaih_servtuple *)
                    alloca (sizeof (struct gaih_servtuple));
                    alloca (sizeof (struct gaih_servtuple));
 
 
                  if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
                  if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
                    {
                    {
                      if (rc & GAIH_OKIFUNSPEC)
                      if (rc & GAIH_OKIFUNSPEC)
                        continue;
                        continue;
                      return rc;
                      return rc;
                    }
                    }
 
 
                  *pst = newp;
                  *pst = newp;
                  pst = &(newp->next);
                  pst = &(newp->next);
                }
                }
              if (st == (struct gaih_servtuple *) &nullserv)
              if (st == (struct gaih_servtuple *) &nullserv)
                return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
                return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
            }
            }
        }
        }
      else
      else
        {
        {
          if (req->ai_socktype || req->ai_protocol)
          if (req->ai_socktype || req->ai_protocol)
            {
            {
              st = alloca (sizeof (struct gaih_servtuple));
              st = alloca (sizeof (struct gaih_servtuple));
              st->next = NULL;
              st->next = NULL;
              st->socktype = tp->socktype;
              st->socktype = tp->socktype;
              st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
              st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
                              ? req->ai_protocol : tp->protocol);
                              ? req->ai_protocol : tp->protocol);
              st->port = htons (service->num);
              st->port = htons (service->num);
            }
            }
          else
          else
            {
            {
              /* Neither socket type nor protocol is set.  Return all
              /* Neither socket type nor protocol is set.  Return all
                 socket types we know about.  */
                 socket types we know about.  */
              struct gaih_servtuple **lastp = &st;
              struct gaih_servtuple **lastp = &st;
              for (tp = gaih_inet_typeproto + 1; tp->name[0]; ++tp)
              for (tp = gaih_inet_typeproto + 1; tp->name[0]; ++tp)
                if ((tp->protoflag & GAI_PROTO_NOSERVICE) == 0)
                if ((tp->protoflag & GAI_PROTO_NOSERVICE) == 0)
                  {
                  {
                    struct gaih_servtuple *newp;
                    struct gaih_servtuple *newp;
 
 
                    newp = alloca (sizeof (struct gaih_servtuple));
                    newp = alloca (sizeof (struct gaih_servtuple));
                    newp->next = NULL;
                    newp->next = NULL;
                    newp->socktype = tp->socktype;
                    newp->socktype = tp->socktype;
                    newp->protocol = tp->protocol;
                    newp->protocol = tp->protocol;
                    newp->port = htons (service->num);
                    newp->port = htons (service->num);
 
 
                    *lastp = newp;
                    *lastp = newp;
                    lastp = &newp->next;
                    lastp = &newp->next;
                  }
                  }
            }
            }
        }
        }
    }
    }
  else if (req->ai_socktype || req->ai_protocol)
  else if (req->ai_socktype || req->ai_protocol)
    {
    {
      st = alloca (sizeof (struct gaih_servtuple));
      st = alloca (sizeof (struct gaih_servtuple));
      st->next = NULL;
      st->next = NULL;
      st->socktype = tp->socktype;
      st->socktype = tp->socktype;
      st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
      st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
                      ? req->ai_protocol : tp->protocol);
                      ? req->ai_protocol : tp->protocol);
      st->port = 0;
      st->port = 0;
    }
    }
  else
  else
    {
    {
      /* Neither socket type nor protocol is set.  Return all socket types
      /* Neither socket type nor protocol is set.  Return all socket types
         we know about.  */
         we know about.  */
      struct gaih_servtuple **lastp = &st;
      struct gaih_servtuple **lastp = &st;
      for (++tp; tp->name[0]; ++tp)
      for (++tp; tp->name[0]; ++tp)
        {
        {
          struct gaih_servtuple *newp;
          struct gaih_servtuple *newp;
 
 
          newp = alloca (sizeof (struct gaih_servtuple));
          newp = alloca (sizeof (struct gaih_servtuple));
          newp->next = NULL;
          newp->next = NULL;
          newp->socktype = tp->socktype;
          newp->socktype = tp->socktype;
          newp->protocol = tp->protocol;
          newp->protocol = tp->protocol;
          newp->port = 0;
          newp->port = 0;
 
 
          *lastp = newp;
          *lastp = newp;
          lastp = &newp->next;
          lastp = &newp->next;
        }
        }
    }
    }
 
 
  if (name != NULL)
  if (name != NULL)
    {
    {
      at = alloca (sizeof (struct gaih_addrtuple));
      at = alloca (sizeof (struct gaih_addrtuple));
 
 
      at->family = AF_UNSPEC;
      at->family = AF_UNSPEC;
      at->scopeid = 0;
      at->scopeid = 0;
      at->next = NULL;
      at->next = NULL;
 
 
#ifdef HAVE_LIBIDN
#ifdef HAVE_LIBIDN
      if (req->ai_flags & AI_IDN)
      if (req->ai_flags & AI_IDN)
        {
        {
          int idn_flags = 0;
          int idn_flags = 0;
          if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED)
          if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED)
            idn_flags |= IDNA_ALLOW_UNASSIGNED;
            idn_flags |= IDNA_ALLOW_UNASSIGNED;
          if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES)
          if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES)
            idn_flags |= IDNA_USE_STD3_ASCII_RULES;
            idn_flags |= IDNA_USE_STD3_ASCII_RULES;
 
 
          char *p = NULL;
          char *p = NULL;
          rc = __idna_to_ascii_lz (name, &p, idn_flags);
          rc = __idna_to_ascii_lz (name, &p, idn_flags);
          if (rc != IDNA_SUCCESS)
          if (rc != IDNA_SUCCESS)
            {
            {
              if (rc == IDNA_MALLOC_ERROR)
              if (rc == IDNA_MALLOC_ERROR)
                return -EAI_MEMORY;
                return -EAI_MEMORY;
              if (rc == IDNA_DLOPEN_ERROR)
              if (rc == IDNA_DLOPEN_ERROR)
                return -EAI_SYSTEM;
                return -EAI_SYSTEM;
              return -EAI_IDN_ENCODE;
              return -EAI_IDN_ENCODE;
            }
            }
          /* In case the output string is the same as the input string
          /* In case the output string is the same as the input string
             no new string has been allocated.  */
             no new string has been allocated.  */
          if (p != name)
          if (p != name)
            {
            {
              name = alloca (strlen (p) + 1);
              name = alloca (strlen (p) + 1);
              strcpy (name, p);
              strcpy (name, p);
              free (p);
              free (p);
            }
            }
        }
        }
#endif
#endif
 
 
      if (inet_aton (name, (struct in_addr *) at->addr) != 0)
      if (inet_aton (name, (struct in_addr *) at->addr) != 0)
        {
        {
          if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
          if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
            at->family = AF_INET;
            at->family = AF_INET;
          else if (req->ai_family == AF_INET6 && req->ai_flags & AI_V4MAPPED)
          else if (req->ai_family == AF_INET6 && req->ai_flags & AI_V4MAPPED)
            {
            {
              at->addr[3] = at->addr[0];
              at->addr[3] = at->addr[0];
              at->addr[2] = htonl (0xffff);
              at->addr[2] = htonl (0xffff);
              at->addr[1] = 0;
              at->addr[1] = 0;
              at->addr[0] = 0;
              at->addr[0] = 0;
              at->family = AF_INET6;
              at->family = AF_INET6;
            }
            }
          else
          else
            return -EAI_ADDRFAMILY;
            return -EAI_ADDRFAMILY;
 
 
        dupname:
        dupname:
          if (req->ai_flags & AI_CANONNAME)
          if (req->ai_flags & AI_CANONNAME)
            {
            {
              canon = strdup (name);
              canon = strdup (name);
              if (canon == NULL)
              if (canon == NULL)
                return -EAI_MEMORY;
                return -EAI_MEMORY;
            }
            }
        }
        }
 
 
      if (at->family == AF_UNSPEC)
      if (at->family == AF_UNSPEC)
        {
        {
          char *scope_delim;
          char *scope_delim;
          char *namebuf = alloca (strlen (name) + 1);
          char *namebuf = alloca (strlen (name) + 1);
          strcpy (namebuf, name);
          strcpy (namebuf, name);
 
 
          scope_delim = strchr (namebuf, SCOPE_DELIMITER);
          scope_delim = strchr (namebuf, SCOPE_DELIMITER);
          if (scope_delim != NULL)
          if (scope_delim != NULL)
            *scope_delim = '\0';
            *scope_delim = '\0';
 
 
          if (inet_pton (AF_INET6, namebuf, at->addr) > 0)
          if (inet_pton (AF_INET6, namebuf, at->addr) > 0)
            {
            {
              if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
              if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
                at->family = AF_INET6;
                at->family = AF_INET6;
              else if (req->ai_family == AF_INET
              else if (req->ai_family == AF_INET
                       && IN6_IS_ADDR_V4MAPPED ((struct in6_addr *)at->addr))
                       && IN6_IS_ADDR_V4MAPPED ((struct in6_addr *)at->addr))
                {
                {
                  at->addr[0] = at->addr[3];
                  at->addr[0] = at->addr[3];
                  at->family = AF_INET;
                  at->family = AF_INET;
                }
                }
              else
              else
                return -EAI_ADDRFAMILY;
                return -EAI_ADDRFAMILY;
 
 
              if (scope_delim != NULL)
              if (scope_delim != NULL)
                {
                {
                  int try_numericscope = 0;
                  int try_numericscope = 0;
                  if (IN6_IS_ADDR_LINKLOCAL ((struct in6_addr *)at->addr)
                  if (IN6_IS_ADDR_LINKLOCAL ((struct in6_addr *)at->addr)
                      || IN6_IS_ADDR_MC_LINKLOCAL ((struct in6_addr *)at->addr))
                      || IN6_IS_ADDR_MC_LINKLOCAL ((struct in6_addr *)at->addr))
                    {
                    {
                      at->scopeid = if_nametoindex (scope_delim + 1);
                      at->scopeid = if_nametoindex (scope_delim + 1);
                      if (at->scopeid == 0)
                      if (at->scopeid == 0)
                        try_numericscope = 1;
                        try_numericscope = 1;
                    }
                    }
                  else
                  else
                    try_numericscope = 1;
                    try_numericscope = 1;
 
 
                  if (try_numericscope != 0)
                  if (try_numericscope != 0)
                    {
                    {
                      char *end;
                      char *end;
                      assert (sizeof (uint32_t) <= sizeof (unsigned long));
                      assert (sizeof (uint32_t) <= sizeof (unsigned long));
                      at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
                      at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
                                                        10);
                                                        10);
                      if (*end != '\0')
                      if (*end != '\0')
                        return GAIH_OKIFUNSPEC | -EAI_NONAME;
                        return GAIH_OKIFUNSPEC | -EAI_NONAME;
                    }
                    }
                }
                }
 
 
              goto dupname;
              goto dupname;
            }
            }
        }
        }
 
 
      if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
      if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
        {
        {
          struct gaih_addrtuple **pat = &at;
          struct gaih_addrtuple **pat = &at;
          int no_data = 0;
          int no_data = 0;
          int no_inet6_data = 0;
          int no_inet6_data = 0;
          service_user *nip = NULL;
          service_user *nip = NULL;
          enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
          enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
          enum nss_status status = NSS_STATUS_UNAVAIL;
          enum nss_status status = NSS_STATUS_UNAVAIL;
          int no_more;
          int no_more;
          int old_res_options;
          int old_res_options;
 
 
          /* If we do not have to look for IPv4 and IPv6 together, use
          /* If we do not have to look for IPv4 and IPv6 together, use
             the simple, old functions.  */
             the simple, old functions.  */
          if (req->ai_family == AF_INET || req->ai_family == AF_INET6)
          if (req->ai_family == AF_INET || req->ai_family == AF_INET6)
            {
            {
              int family = req->ai_family;
              int family = req->ai_family;
              size_t tmpbuflen = 512;
              size_t tmpbuflen = 512;
              char *tmpbuf = alloca (tmpbuflen);
              char *tmpbuf = alloca (tmpbuflen);
              int rc;
              int rc;
              struct hostent th;
              struct hostent th;
              struct hostent *h;
              struct hostent *h;
              int herrno;
              int herrno;
 
 
            simple_again:
            simple_again:
              while (1)
              while (1)
                {
                {
                  rc = __gethostbyname2_r (name, family, &th, tmpbuf,
                  rc = __gethostbyname2_r (name, family, &th, tmpbuf,
                                           tmpbuflen, &h, &herrno);
                                           tmpbuflen, &h, &herrno);
                  if (rc != ERANGE || herrno != NETDB_INTERNAL)
                  if (rc != ERANGE || herrno != NETDB_INTERNAL)
                    break;
                    break;
                  tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
                  tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
                }
                }
 
 
              if (rc == 0)
              if (rc == 0)
                {
                {
                  if (h == NULL)
                  if (h == NULL)
                    {
                    {
                      if (req->ai_family == AF_INET6
                      if (req->ai_family == AF_INET6
                          && (req->ai_flags & AI_V4MAPPED)
                          && (req->ai_flags & AI_V4MAPPED)
                          && family == AF_INET6)
                          && family == AF_INET6)
                        {
                        {
                          /* Try again, this time looking for IPv4
                          /* Try again, this time looking for IPv4
                             addresses.  */
                             addresses.  */
                          family = AF_INET;
                          family = AF_INET;
                          goto simple_again;
                          goto simple_again;
                        }
                        }
                    }
                    }
                  else
                  else
                    {
                    {
                      /* We found data, now convert it into the list.  */
                      /* We found data, now convert it into the list.  */
                      int i = 0;
                      int i = 0;
                      for (i = 0; h->h_addr_list[i]; ++i)
                      for (i = 0; h->h_addr_list[i]; ++i)
                        {
                        {
                          if (*pat == NULL)
                          if (*pat == NULL)
                            {
                            {
                              *pat = alloca (sizeof (struct gaih_addrtuple));
                              *pat = alloca (sizeof (struct gaih_addrtuple));
                              (*pat)->scopeid = 0;
                              (*pat)->scopeid = 0;
                            }
                            }
                          (*pat)->next = NULL;
                          (*pat)->next = NULL;
                          (*pat)->family = req->ai_family;
                          (*pat)->family = req->ai_family;
                          if (family == req->ai_family)
                          if (family == req->ai_family)
                            memcpy ((*pat)->addr, h->h_addr_list[i],
                            memcpy ((*pat)->addr, h->h_addr_list[i],
                                    h->h_length);
                                    h->h_length);
                          else
                          else
                            {
                            {
                              int32_t *addr = (uint32_t *) (*pat)->addr;
                              int32_t *addr = (uint32_t *) (*pat)->addr;
                              addr[3] = *(uint32_t *) h->h_addr_list[i];
                              addr[3] = *(uint32_t *) h->h_addr_list[i];
                              addr[2] = htonl (0xffff);
                              addr[2] = htonl (0xffff);
                              addr[1] = 0;
                              addr[1] = 0;
                              addr[0] = 0;
                              addr[0] = 0;
                            }
                            }
                          pat = &((*pat)->next);
                          pat = &((*pat)->next);
                        }
                        }
                    }
                    }
                }
                }
              else
              else
                {
                {
                  if (herrno == NETDB_INTERNAL)
                  if (herrno == NETDB_INTERNAL)
                    {
                    {
                      h_errno = (herrno);
                      h_errno = (herrno);
                      return -EAI_SYSTEM;
                      return -EAI_SYSTEM;
                    }
                    }
                  if (herrno == TRY_AGAIN)
                  if (herrno == TRY_AGAIN)
                    {
                    {
                      return -EAI_AGAIN;
                      return -EAI_AGAIN;
                    }
                    }
                  /* We made requests but they turned out no data.
                  /* We made requests but they turned out no data.
                     The name is known, though.  */
                     The name is known, though.  */
                  return (GAIH_OKIFUNSPEC | -EAI_NODATA);
                  return (GAIH_OKIFUNSPEC | -EAI_NODATA);
                }
                }
 
 
              goto process_list;
              goto process_list;
            }
            }
 
 
#ifdef USE_NSCD
#ifdef USE_NSCD
          if (__nss_not_use_nscd_hosts > 0
          if (__nss_not_use_nscd_hosts > 0
              && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
              && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
            __nss_not_use_nscd_hosts = 0;
            __nss_not_use_nscd_hosts = 0;
 
 
          if (!__nss_not_use_nscd_hosts)
          if (!__nss_not_use_nscd_hosts)
            {
            {
              /* Try to use nscd.  */
              /* Try to use nscd.  */
              struct nscd_ai_result *air = NULL;
              struct nscd_ai_result *air = NULL;
              int herrno;
              int herrno;
              int err = __nscd_getai (name, &air, &herrno);
              int err = __nscd_getai (name, &air, &herrno);
              if (air != NULL)
              if (air != NULL)
                {
                {
                  /* Transform into gaih_addrtuple list.  */
                  /* Transform into gaih_addrtuple list.  */
                  bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
                  bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
                  char *addrs = air->addrs;
                  char *addrs = air->addrs;
 
 
                  for (int i = 0; i < air->naddrs; ++i)
                  for (int i = 0; i < air->naddrs; ++i)
                    {
                    {
                      socklen_t size = (air->family[i] == AF_INET
                      socklen_t size = (air->family[i] == AF_INET
                                        ? INADDRSZ : IN6ADDRSZ);
                                        ? INADDRSZ : IN6ADDRSZ);
                      if (*pat == NULL)
                      if (*pat == NULL)
                        {
                        {
                          *pat = alloca (sizeof (struct gaih_addrtuple));
                          *pat = alloca (sizeof (struct gaih_addrtuple));
                          (*pat)->scopeid = 0;
                          (*pat)->scopeid = 0;
                        }
                        }
                      uint32_t *pataddr = (*pat)->addr;
                      uint32_t *pataddr = (*pat)->addr;
                      (*pat)->next = NULL;
                      (*pat)->next = NULL;
                      if (added_canon || air->canon == NULL)
                      if (added_canon || air->canon == NULL)
                        (*pat)->name = NULL;
                        (*pat)->name = NULL;
                      else {
                      else {
                        canon = (*pat)->name = alloca (strlen (air->canon) + 1);
                        canon = (*pat)->name = alloca (strlen (air->canon) + 1);
                        strcpy (canon, air->canon);
                        strcpy (canon, air->canon);
                      }
                      }
 
 
                      if (air->family[i] == AF_INET
                      if (air->family[i] == AF_INET
                          && req->ai_family == AF_INET6
                          && req->ai_family == AF_INET6
                          && (req->ai_flags & AI_V4MAPPED))
                          && (req->ai_flags & AI_V4MAPPED))
                        {
                        {
                          (*pat)->family = AF_INET6;
                          (*pat)->family = AF_INET6;
                          pataddr[3] = *(uint32_t *) addrs;
                          pataddr[3] = *(uint32_t *) addrs;
                          pataddr[2] = htonl (0xffff);
                          pataddr[2] = htonl (0xffff);
                          pataddr[1] = 0;
                          pataddr[1] = 0;
                          pataddr[0] = 0;
                          pataddr[0] = 0;
                          pat = &((*pat)->next);
                          pat = &((*pat)->next);
                          added_canon = true;
                          added_canon = true;
                        }
                        }
                      else if (req->ai_family == AF_UNSPEC
                      else if (req->ai_family == AF_UNSPEC
                               || air->family[i] == req->ai_family)
                               || air->family[i] == req->ai_family)
                        {
                        {
                          (*pat)->family = air->family[i];
                          (*pat)->family = air->family[i];
                          memcpy (pataddr, addrs, size);
                          memcpy (pataddr, addrs, size);
                          pat = &((*pat)->next);
                          pat = &((*pat)->next);
                          added_canon = true;
                          added_canon = true;
                          if (air->family[i] == AF_INET6)
                          if (air->family[i] == AF_INET6)
                            got_ipv6 = true;
                            got_ipv6 = true;
                        }
                        }
                      addrs += size;
                      addrs += size;
                    }
                    }
 
 
                  free (air);
                  free (air);
 
 
                  if (at->family == AF_UNSPEC)
                  if (at->family == AF_UNSPEC)
                    return (GAIH_OKIFUNSPEC | -EAI_NONAME);
                    return (GAIH_OKIFUNSPEC | -EAI_NONAME);
 
 
                  goto process_list;
                  goto process_list;
                }
                }
              else if (err != 0 && __nss_not_use_nscd_hosts == 0)
              else if (err != 0 && __nss_not_use_nscd_hosts == 0)
                {
                {
                  if (herrno == NETDB_INTERNAL && errno == ENOMEM)
                  if (herrno == NETDB_INTERNAL && errno == ENOMEM)
                    return -EAI_MEMORY;
                    return -EAI_MEMORY;
                  if (herrno == TRY_AGAIN)
                  if (herrno == TRY_AGAIN)
                    return -EAI_AGAIN;
                    return -EAI_AGAIN;
                  return -EAI_SYSTEM;
                  return -EAI_SYSTEM;
                }
                }
            }
            }
#endif
#endif
 
 
          if (__nss_hosts_database != NULL)
          if (__nss_hosts_database != NULL)
            {
            {
              no_more = 0;
              no_more = 0;
              nip = __nss_hosts_database;
              nip = __nss_hosts_database;
            }
            }
          else
          else
            no_more = __nss_database_lookup ("hosts", NULL,
            no_more = __nss_database_lookup ("hosts", NULL,
                                             "dns [!UNAVAIL=return] files",
                                             "dns [!UNAVAIL=return] files",
                                             &nip);
                                             &nip);
 
 
          if (__res_maybe_init (&_res, 0) == -1)
          if (__res_maybe_init (&_res, 0) == -1)
            no_more = 1;
            no_more = 1;
 
 
          /* If we are looking for both IPv4 and IPv6 address we don't
          /* If we are looking for both IPv4 and IPv6 address we don't
             want the lookup functions to automatically promote IPv4
             want the lookup functions to automatically promote IPv4
             addresses to IPv6 addresses.  Currently this is decided
             addresses to IPv6 addresses.  Currently this is decided
             by setting the RES_USE_INET6 bit in _res.options.  */
             by setting the RES_USE_INET6 bit in _res.options.  */
          old_res_options = _res.options;
          old_res_options = _res.options;
          _res.options &= ~RES_USE_INET6;
          _res.options &= ~RES_USE_INET6;
 
 
          size_t tmpbuflen = 512;
          size_t tmpbuflen = 512;
          char *tmpbuf = alloca (tmpbuflen);
          char *tmpbuf = alloca (tmpbuflen);
 
 
          while (!no_more)
          while (!no_more)
            {
            {
              nss_gethostbyname3_r fct = NULL;
              nss_gethostbyname3_r fct = NULL;
              if (req->ai_flags & AI_CANONNAME)
              if (req->ai_flags & AI_CANONNAME)
                /* No need to use this function if we do not look for
                /* No need to use this function if we do not look for
                   the canonical name.  The function does not exist in
                   the canonical name.  The function does not exist in
                   all NSS modules and therefore the lookup would
                   all NSS modules and therefore the lookup would
                   often fail.  */
                   often fail.  */
                fct = __nss_lookup_function (nip, "gethostbyname3_r");
                fct = __nss_lookup_function (nip, "gethostbyname3_r");
              if (fct == NULL)
              if (fct == NULL)
                /* We are cheating here.  The gethostbyname2_r function does
                /* We are cheating here.  The gethostbyname2_r function does
                   not have the same interface as gethostbyname3_r but the
                   not have the same interface as gethostbyname3_r but the
                   extra arguments the latter takes are added at the end.
                   extra arguments the latter takes are added at the end.
                   So the gethostbyname2_r code will just ignore them.  */
                   So the gethostbyname2_r code will just ignore them.  */
                fct = __nss_lookup_function (nip, "gethostbyname2_r");
                fct = __nss_lookup_function (nip, "gethostbyname2_r");
 
 
              if (fct != NULL)
              if (fct != NULL)
                {
                {
                  if (req->ai_family == AF_INET6
                  if (req->ai_family == AF_INET6
                      || req->ai_family == AF_UNSPEC)
                      || req->ai_family == AF_UNSPEC)
                    {
                    {
                      gethosts (AF_INET6, struct in6_addr);
                      gethosts (AF_INET6, struct in6_addr);
                      no_inet6_data = no_data;
                      no_inet6_data = no_data;
                      inet6_status = status;
                      inet6_status = status;
                    }
                    }
                  if (req->ai_family == AF_INET
                  if (req->ai_family == AF_INET
                      || req->ai_family == AF_UNSPEC
                      || req->ai_family == AF_UNSPEC
                      || (req->ai_family == AF_INET6
                      || (req->ai_family == AF_INET6
                          && (req->ai_flags & AI_V4MAPPED)
                          && (req->ai_flags & AI_V4MAPPED)
                          /* Avoid generating the mapped addresses if we
                          /* Avoid generating the mapped addresses if we
                             know we are not going to need them.  */
                             know we are not going to need them.  */
                          && ((req->ai_flags & AI_ALL) || !got_ipv6)))
                          && ((req->ai_flags & AI_ALL) || !got_ipv6)))
                    {
                    {
                      gethosts (AF_INET, struct in_addr);
                      gethosts (AF_INET, struct in_addr);
 
 
                      if (req->ai_family == AF_INET)
                      if (req->ai_family == AF_INET)
                        {
                        {
                          no_inet6_data = no_data;
                          no_inet6_data = no_data;
                          inet6_status = status;
                          inet6_status = status;
                        }
                        }
                    }
                    }
 
 
                  /* If we found one address for AF_INET or AF_INET6,
                  /* If we found one address for AF_INET or AF_INET6,
                     don't continue the search.  */
                     don't continue the search.  */
                  if (inet6_status == NSS_STATUS_SUCCESS
                  if (inet6_status == NSS_STATUS_SUCCESS
                      || status == NSS_STATUS_SUCCESS)
                      || status == NSS_STATUS_SUCCESS)
                    {
                    {
                      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
                      if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
                        {
                        {
                          /* If we need the canonical name, get it
                          /* If we need the canonical name, get it
                             from the same service as the result.  */
                             from the same service as the result.  */
                          nss_getcanonname_r cfct;
                          nss_getcanonname_r cfct;
                          int herrno;
                          int herrno;
 
 
                          cfct = __nss_lookup_function (nip, "getcanonname_r");
                          cfct = __nss_lookup_function (nip, "getcanonname_r");
                          if (cfct != NULL)
                          if (cfct != NULL)
                            {
                            {
                              const size_t max_fqdn_len = 256;
                              const size_t max_fqdn_len = 256;
                              char *buf = alloca (max_fqdn_len);
                              char *buf = alloca (max_fqdn_len);
                              char *s;
                              char *s;
 
 
                              if (DL_CALL_FCT (cfct, (at->name ?: name, buf,
                              if (DL_CALL_FCT (cfct, (at->name ?: name, buf,
                                                      max_fqdn_len, &s, &rc,
                                                      max_fqdn_len, &s, &rc,
                                                      &herrno))
                                                      &herrno))
                                  == NSS_STATUS_SUCCESS)
                                  == NSS_STATUS_SUCCESS)
                                canon = s;
                                canon = s;
                              else
                              else
                                /* Set to name now to avoid using
                                /* Set to name now to avoid using
                                   gethostbyaddr.  */
                                   gethostbyaddr.  */
                                canon = name;
                                canon = name;
                            }
                            }
                        }
                        }
 
 
                      break;
                      break;
                    }
                    }
 
 
                  /* We can have different states for AF_INET and
                  /* We can have different states for AF_INET and
                     AF_INET6.  Try to find a useful one for both.  */
                     AF_INET6.  Try to find a useful one for both.  */
                  if (inet6_status == NSS_STATUS_TRYAGAIN)
                  if (inet6_status == NSS_STATUS_TRYAGAIN)
                    status = NSS_STATUS_TRYAGAIN;
                    status = NSS_STATUS_TRYAGAIN;
                  else if (status == NSS_STATUS_UNAVAIL &&
                  else if (status == NSS_STATUS_UNAVAIL &&
                           inet6_status != NSS_STATUS_UNAVAIL)
                           inet6_status != NSS_STATUS_UNAVAIL)
                    status = inet6_status;
                    status = inet6_status;
                }
                }
 
 
              if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
              if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
                break;
                break;
 
 
              if (nip->next == NULL)
              if (nip->next == NULL)
                no_more = -1;
                no_more = -1;
              else
              else
                nip = nip->next;
                nip = nip->next;
            }
            }
 
 
          _res.options = old_res_options;
          _res.options = old_res_options;
 
 
          if (no_data != 0 && no_inet6_data != 0)
          if (no_data != 0 && no_inet6_data != 0)
            {
            {
              /* If both requests timed out report this.  */
              /* If both requests timed out report this.  */
              if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
              if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
                return -EAI_AGAIN;
                return -EAI_AGAIN;
 
 
              /* We made requests but they turned out no data.  The name
              /* We made requests but they turned out no data.  The name
                 is known, though.  */
                 is known, though.  */
              return (GAIH_OKIFUNSPEC | -EAI_NODATA);
              return (GAIH_OKIFUNSPEC | -EAI_NODATA);
            }
            }
        }
        }
 
 
    process_list:
    process_list:
      if (at->family == AF_UNSPEC)
      if (at->family == AF_UNSPEC)
        return (GAIH_OKIFUNSPEC | -EAI_NONAME);
        return (GAIH_OKIFUNSPEC | -EAI_NONAME);
    }
    }
  else
  else
    {
    {
      struct gaih_addrtuple *atr;
      struct gaih_addrtuple *atr;
      atr = at = alloca (sizeof (struct gaih_addrtuple));
      atr = at = alloca (sizeof (struct gaih_addrtuple));
      memset (at, '\0', sizeof (struct gaih_addrtuple));
      memset (at, '\0', sizeof (struct gaih_addrtuple));
 
 
      if (req->ai_family == AF_UNSPEC)
      if (req->ai_family == AF_UNSPEC)
        {
        {
          at->next = alloca (sizeof (struct gaih_addrtuple));
          at->next = alloca (sizeof (struct gaih_addrtuple));
          memset (at->next, '\0', sizeof (struct gaih_addrtuple));
          memset (at->next, '\0', sizeof (struct gaih_addrtuple));
        }
        }
 
 
      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
        {
        {
          at->family = AF_INET6;
          at->family = AF_INET6;
          if ((req->ai_flags & AI_PASSIVE) == 0)
          if ((req->ai_flags & AI_PASSIVE) == 0)
            memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
            memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
          atr = at->next;
          atr = at->next;
        }
        }
 
 
      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
        {
        {
          atr->family = AF_INET;
          atr->family = AF_INET;
          if ((req->ai_flags & AI_PASSIVE) == 0)
          if ((req->ai_flags & AI_PASSIVE) == 0)
            atr->addr[0] = htonl (INADDR_LOOPBACK);
            atr->addr[0] = htonl (INADDR_LOOPBACK);
        }
        }
    }
    }
 
 
  if (pai == NULL)
  if (pai == NULL)
    return 0;
    return 0;
 
 
  {
  {
    struct gaih_servtuple *st2;
    struct gaih_servtuple *st2;
    struct gaih_addrtuple *at2 = at;
    struct gaih_addrtuple *at2 = at;
    size_t socklen;
    size_t socklen;
    sa_family_t family;
    sa_family_t family;
 
 
    /*
    /*
      buffer is the size of an unformatted IPv6 address in printable format.
      buffer is the size of an unformatted IPv6 address in printable format.
     */
     */
    while (at2 != NULL)
    while (at2 != NULL)
      {
      {
        /* Only the first entry gets the canonical name.  */
        /* Only the first entry gets the canonical name.  */
        if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0)
        if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0)
          {
          {
            if (canon == NULL)
            if (canon == NULL)
              {
              {
                struct hostent *h = NULL;
                struct hostent *h = NULL;
                int herrno;
                int herrno;
                struct hostent th;
                struct hostent th;
                size_t tmpbuflen = 512;
                size_t tmpbuflen = 512;
                char *tmpbuf = NULL;
                char *tmpbuf = NULL;
 
 
                do
                do
                  {
                  {
                    tmpbuf = extend_alloca (tmpbuf, tmpbuflen, tmpbuflen * 2);
                    tmpbuf = extend_alloca (tmpbuf, tmpbuflen, tmpbuflen * 2);
                    rc = __gethostbyaddr_r (at2->addr,
                    rc = __gethostbyaddr_r (at2->addr,
                                            ((at2->family == AF_INET6)
                                            ((at2->family == AF_INET6)
                                             ? sizeof (struct in6_addr)
                                             ? sizeof (struct in6_addr)
                                             : sizeof (struct in_addr)),
                                             : sizeof (struct in_addr)),
                                            at2->family, &th, tmpbuf,
                                            at2->family, &th, tmpbuf,
                                            tmpbuflen, &h, &herrno);
                                            tmpbuflen, &h, &herrno);
                  }
                  }
                while (rc == ERANGE && herrno == NETDB_INTERNAL);
                while (rc == ERANGE && herrno == NETDB_INTERNAL);
 
 
                if (rc != 0 && herrno == NETDB_INTERNAL)
                if (rc != 0 && herrno == NETDB_INTERNAL)
                  {
                  {
                    h_errno = (herrno);
                    h_errno = (herrno);
                    return -EAI_SYSTEM;
                    return -EAI_SYSTEM;
                  }
                  }
 
 
                if (h != NULL)
                if (h != NULL)
                  canon = h->h_name;
                  canon = h->h_name;
                else
                else
                  {
                  {
                    assert (orig_name != NULL);
                    assert (orig_name != NULL);
                    /* If the canonical name cannot be determined, use
                    /* If the canonical name cannot be determined, use
                       the passed in string.  */
                       the passed in string.  */
                    canon = orig_name;
                    canon = orig_name;
                  }
                  }
              }
              }
 
 
#ifdef HAVE_LIBIDN
#ifdef HAVE_LIBIDN
            if (req->ai_flags & AI_CANONIDN)
            if (req->ai_flags & AI_CANONIDN)
              {
              {
                int idn_flags = 0;
                int idn_flags = 0;
                if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED)
                if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED)
                  idn_flags |= IDNA_ALLOW_UNASSIGNED;
                  idn_flags |= IDNA_ALLOW_UNASSIGNED;
                if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES)
                if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES)
                  idn_flags |= IDNA_USE_STD3_ASCII_RULES;
                  idn_flags |= IDNA_USE_STD3_ASCII_RULES;
 
 
                char *out;
                char *out;
                int rc = __idna_to_unicode_lzlz (canon, &out, idn_flags);
                int rc = __idna_to_unicode_lzlz (canon, &out, idn_flags);
                if (rc != IDNA_SUCCESS)
                if (rc != IDNA_SUCCESS)
                  {
                  {
                    if (rc == IDNA_MALLOC_ERROR)
                    if (rc == IDNA_MALLOC_ERROR)
                      return -EAI_MEMORY;
                      return -EAI_MEMORY;
                    if (rc == IDNA_DLOPEN_ERROR)
                    if (rc == IDNA_DLOPEN_ERROR)
                      return -EAI_SYSTEM;
                      return -EAI_SYSTEM;
                    return -EAI_IDN_ENCODE;
                    return -EAI_IDN_ENCODE;
                  }
                  }
                /* In case the output string is the same as the input
                /* In case the output string is the same as the input
                   string no new string has been allocated.  Otherwise
                   string no new string has been allocated.  Otherwise
                   make a copy.  */
                   make a copy.  */
                if (out == canon)
                if (out == canon)
                  goto make_copy;
                  goto make_copy;
              }
              }
            else
            else
#endif
#endif
              {
              {
#ifdef HAVE_LIBIDN
#ifdef HAVE_LIBIDN
              make_copy:
              make_copy:
#endif
#endif
                canon = strdup (canon);
                canon = strdup (canon);
                if (canon == NULL)
                if (canon == NULL)
                  return -EAI_MEMORY;
                  return -EAI_MEMORY;
              }
              }
          }
          }
 
 
        if (at2->family == AF_INET6)
        if (at2->family == AF_INET6)
          {
          {
            family = AF_INET6;
            family = AF_INET6;
            socklen = sizeof (struct sockaddr_in6);
            socklen = sizeof (struct sockaddr_in6);
 
 
            /* If we looked up IPv4 mapped address discard them here if
            /* If we looked up IPv4 mapped address discard them here if
               the caller isn't interested in all address and we have
               the caller isn't interested in all address and we have
               found at least one IPv6 address.  */
               found at least one IPv6 address.  */
            if (got_ipv6
            if (got_ipv6
                && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
                && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
                && IN6_IS_ADDR_V4MAPPED ((struct in6_addr *)at2->addr))
                && IN6_IS_ADDR_V4MAPPED ((struct in6_addr *)at2->addr))
              goto ignore;
              goto ignore;
          }
          }
        else
        else
          {
          {
            family = AF_INET;
            family = AF_INET;
            socklen = sizeof (struct sockaddr_in);
            socklen = sizeof (struct sockaddr_in);
          }
          }
 
 
        for (st2 = st; st2 != NULL; st2 = st2->next)
        for (st2 = st; st2 != NULL; st2 = st2->next)
          {
          {
            struct addrinfo *ai;
            struct addrinfo *ai;
            ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
            ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
            if (ai == NULL)
            if (ai == NULL)
              return -EAI_MEMORY;
              return -EAI_MEMORY;
 
 
            ai->ai_flags = req->ai_flags;
            ai->ai_flags = req->ai_flags;
            ai->ai_family = family;
            ai->ai_family = family;
            ai->ai_socktype = st2->socktype;
            ai->ai_socktype = st2->socktype;
            ai->ai_protocol = st2->protocol;
            ai->ai_protocol = st2->protocol;
            ai->ai_addrlen = socklen;
            ai->ai_addrlen = socklen;
            ai->ai_addr = (void *) (ai + 1);
            ai->ai_addr = (void *) (ai + 1);
 
 
            /* We only add the canonical name once.  */
            /* We only add the canonical name once.  */
            ai->ai_canonname = (char *) canon;
            ai->ai_canonname = (char *) canon;
            canon = NULL;
            canon = NULL;
 
 
#if SALEN
#if SALEN
            ai->ai_addr->sa_len = socklen;
            ai->ai_addr->sa_len = socklen;
#endif /* SALEN */
#endif /* SALEN */
            ai->ai_addr->sa_family = family;
            ai->ai_addr->sa_family = family;
 
 
            if (family == AF_INET6)
            if (family == AF_INET6)
              {
              {
                struct sockaddr_in6 *sin6p =
                struct sockaddr_in6 *sin6p =
                  (struct sockaddr_in6 *) ai->ai_addr;
                  (struct sockaddr_in6 *) ai->ai_addr;
 
 
                sin6p->sin6_port = st2->port;
                sin6p->sin6_port = st2->port;
                sin6p->sin6_flowinfo = 0;
                sin6p->sin6_flowinfo = 0;
                memcpy (&sin6p->sin6_addr,
                memcpy (&sin6p->sin6_addr,
                        at2->addr, sizeof (struct in6_addr));
                        at2->addr, sizeof (struct in6_addr));
                sin6p->sin6_scope_id = at2->scopeid;
                sin6p->sin6_scope_id = at2->scopeid;
              }
              }
            else
            else
              {
              {
                struct sockaddr_in *sinp =
                struct sockaddr_in *sinp =
                  (struct sockaddr_in *) ai->ai_addr;
                  (struct sockaddr_in *) ai->ai_addr;
                sinp->sin_port = st2->port;
                sinp->sin_port = st2->port;
                memcpy (&sinp->sin_addr,
                memcpy (&sinp->sin_addr,
                        at2->addr, sizeof (struct in_addr));
                        at2->addr, sizeof (struct in_addr));
                memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
                memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
              }
              }
 
 
            pai = &(ai->ai_next);
            pai = &(ai->ai_next);
          }
          }
        *pai = NULL;
        *pai = NULL;
 
 
      ignore:
      ignore:
        at2 = at2->next;
        at2 = at2->next;
      }
      }
  }
  }
  return 0;
  return 0;
}
}
 
 
static struct gaih gaih[] =
static struct gaih gaih[] =
  {
  {
    { PF_INET6, gaih_inet },
    { PF_INET6, gaih_inet },
    { PF_INET, gaih_inet },
    { PF_INET, gaih_inet },
#if 0
#if 0
    { PF_LOCAL, gaih_local },
    { PF_LOCAL, gaih_local },
#endif
#endif
    { PF_UNSPEC, NULL }
    { PF_UNSPEC, NULL }
  };
  };
 
 
struct sort_result
struct sort_result
{
{
  struct addrinfo *dest_addr;
  struct addrinfo *dest_addr;
  struct sockaddr_storage source_addr;
  struct sockaddr_storage source_addr;
  uint8_t source_addr_len;
  uint8_t source_addr_len;
  bool got_source_addr;
  bool got_source_addr;
};
};
 
 
 
 
static int
static int
get_scope (const struct sockaddr_storage *ss)
get_scope (const struct sockaddr_storage *ss)
{
{
  int scope;
  int scope;
  if (ss->ss_family == PF_INET6)
  if (ss->ss_family == PF_INET6)
    {
    {
      const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *) ss;
      const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *) ss;
 
 
      if (! IN6_IS_ADDR_MULTICAST (&in6->sin6_addr))
      if (! IN6_IS_ADDR_MULTICAST (&in6->sin6_addr))
        {
        {
          if (IN6_IS_ADDR_LINKLOCAL (&in6->sin6_addr))
          if (IN6_IS_ADDR_LINKLOCAL (&in6->sin6_addr))
            scope = 2;
            scope = 2;
          else if (IN6_IS_ADDR_SITELOCAL (&in6->sin6_addr))
          else if (IN6_IS_ADDR_SITELOCAL (&in6->sin6_addr))
            scope = 5;
            scope = 5;
          else
          else
            /* XXX Is this the correct default behavior?  */
            /* XXX Is this the correct default behavior?  */
            scope = 14;
            scope = 14;
        }
        }
      else
      else
        scope = in6->sin6_addr.s6_addr[1] & 0xf;
        scope = in6->sin6_addr.s6_addr[1] & 0xf;
    }
    }
  else if (ss->ss_family == PF_INET)
  else if (ss->ss_family == PF_INET)
    {
    {
      const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
      const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
      const uint8_t *addr = (const uint8_t *) &in->sin_addr;
      const uint8_t *addr = (const uint8_t *) &in->sin_addr;
 
 
      /* RFC 3484 specifies how to map IPv6 addresses to scopes.
      /* RFC 3484 specifies how to map IPv6 addresses to scopes.
         169.254/16 and 127/8 are link-local.  */
         169.254/16 and 127/8 are link-local.  */
      if ((addr[0] == 169 && addr[1] == 254) || addr[0] == 127)
      if ((addr[0] == 169 && addr[1] == 254) || addr[0] == 127)
        scope = 2;
        scope = 2;
      else if (addr[0] == 10 || (addr[0] == 172 && addr[1] == 16)
      else if (addr[0] == 10 || (addr[0] == 172 && addr[1] == 16)
               || (addr[0] == 192 && addr[1] == 168))
               || (addr[0] == 192 && addr[1] == 168))
        scope = 5;
        scope = 5;
      else
      else
        scope = 14;
        scope = 14;
    }
    }
  else
  else
    /* XXX What is a good default?  */
    /* XXX What is a good default?  */
    scope = 15;
    scope = 15;
 
 
  return scope;
  return scope;
}
}
 
 
 
 
/* XXX The system administrator should be able to install other
/* XXX The system administrator should be able to install other
   tables.  We need to make this configurable.  The problem is that
   tables.  We need to make this configurable.  The problem is that
   the kernel is also involved since it needs the same table.  */
   the kernel is also involved since it needs the same table.  */
static const struct prefixlist
static const struct prefixlist
{
{
  struct in6_addr prefix;
  struct in6_addr prefix;
  unsigned int bits;
  unsigned int bits;
  int val;
  int val;
} default_labels[] =
} default_labels[] =
  {
  {
    /* See RFC 3484 for the details.  */
    /* See RFC 3484 for the details.  */
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
                                        0x0000, 0x0000, 0x0000, 0x0001 } } },
                                        0x0000, 0x0000, 0x0000, 0x0001 } } },
      128, 0 },
      128, 0 },
    { { .__u6_addr = { .__u6_addr16 = { 0x2002, 0x0000, 0x0000, 0x0000,
    { { .__u6_addr = { .__u6_addr16 = { 0x2002, 0x0000, 0x0000, 0x0000,
                                       0x0000, 0x0000, 0x0000, 0x0000 } } },
                                       0x0000, 0x0000, 0x0000, 0x0000 } } },
      16, 2 },
      16, 2 },
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
                                       0x0000, 0x0000, 0x0000, 0x0000 } } },
                                       0x0000, 0x0000, 0x0000, 0x0000 } } },
      96, 3 },
      96, 3 },
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
                                       0x0000, 0xffff, 0x0000, 0x0000 } } },
                                       0x0000, 0xffff, 0x0000, 0x0000 } } },
      96, 4 },
      96, 4 },
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
                                       0x0000, 0x0000, 0x0000, 0x0000 } } },
                                       0x0000, 0x0000, 0x0000, 0x0000 } } },
      0, 1 }
      0, 1 }
  };
  };
 
 
 
 
static const struct prefixlist default_precedence[] =
static const struct prefixlist default_precedence[] =
  {
  {
    /* See RFC 3484 for the details.  */
    /* See RFC 3484 for the details.  */
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
                                       0x0000, 0x0000, 0x0000, 0x0001 } } },
                                       0x0000, 0x0000, 0x0000, 0x0001 } } },
      128, 50 },
      128, 50 },
    { { .__u6_addr = { .__u6_addr16 = { 0x2002, 0x0000, 0x0000, 0x0000,
    { { .__u6_addr = { .__u6_addr16 = { 0x2002, 0x0000, 0x0000, 0x0000,
                                       0x0000, 0x0000, 0x0000, 0x0000 } } },
                                       0x0000, 0x0000, 0x0000, 0x0000 } } },
      16, 30 },
      16, 30 },
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
                                       0x0000, 0x0000, 0x0000, 0x0000 } } },
                                       0x0000, 0x0000, 0x0000, 0x0000 } } },
      96, 20 },
      96, 20 },
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
                                       0x0000, 0xffff, 0x0000, 0x0000 } } },
                                       0x0000, 0xffff, 0x0000, 0x0000 } } },
      96, 10 },
      96, 10 },
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
    { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
                                       0x0000, 0x0000, 0x0000, 0x0000 } } },
                                       0x0000, 0x0000, 0x0000, 0x0000 } } },
      0, 40 }
      0, 40 }
  };
  };
 
 
 
 
static int
static int
match_prefix (const struct sockaddr_storage *ss, const struct prefixlist *list,
match_prefix (const struct sockaddr_storage *ss, const struct prefixlist *list,
              int default_val)
              int default_val)
{
{
  int idx;
  int idx;
  struct sockaddr_in6 in6_mem;
  struct sockaddr_in6 in6_mem;
  const struct sockaddr_in6 *in6;
  const struct sockaddr_in6 *in6;
 
 
  if (ss->ss_family == PF_INET6)
  if (ss->ss_family == PF_INET6)
    in6 = (const struct sockaddr_in6 *) ss;
    in6 = (const struct sockaddr_in6 *) ss;
  else if (ss->ss_family == PF_INET)
  else if (ss->ss_family == PF_INET)
    {
    {
      const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
      const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
 
 
      /* Convert to IPv6 address.  */
      /* Convert to IPv6 address.  */
      in6_mem.sin6_family = PF_INET6;
      in6_mem.sin6_family = PF_INET6;
      in6_mem.sin6_port = in->sin_port;
      in6_mem.sin6_port = in->sin_port;
      in6_mem.sin6_flowinfo = 0;
      in6_mem.sin6_flowinfo = 0;
      if (in->sin_addr.s_addr == htonl (0x7f000001))
      if (in->sin_addr.s_addr == htonl (0x7f000001))
        in6_mem.sin6_addr = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
        in6_mem.sin6_addr = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
      else
      else
        {
        {
          /* Construct a V4-to-6 mapped address.  */
          /* Construct a V4-to-6 mapped address.  */
          memset (&in6_mem.sin6_addr, '\0', sizeof (in6_mem.sin6_addr));
          memset (&in6_mem.sin6_addr, '\0', sizeof (in6_mem.sin6_addr));
          in6_mem.sin6_addr.__u6_addr.__u6_addr16[5] = 0xffff;
          in6_mem.sin6_addr.__u6_addr.__u6_addr16[5] = 0xffff;
          in6_mem.sin6_addr.__u6_addr.__u6_addr32[3] = in->sin_addr.s_addr;
          in6_mem.sin6_addr.__u6_addr.__u6_addr32[3] = in->sin_addr.s_addr;
          in6_mem.sin6_scope_id = 0;
          in6_mem.sin6_scope_id = 0;
        }
        }
 
 
      in6 = &in6_mem;
      in6 = &in6_mem;
    }
    }
  else
  else
    return default_val;
    return default_val;
 
 
  for (idx = 0; ; ++idx)
  for (idx = 0; ; ++idx)
    {
    {
      unsigned int bits = list[idx].bits;
      unsigned int bits = list[idx].bits;
      uint8_t *mask = list[idx].prefix.s6_addr;
      uint8_t *mask = list[idx].prefix.s6_addr;
      uint8_t *val = in6->sin6_addr.s6_addr;
      uint8_t *val = in6->sin6_addr.s6_addr;
 
 
      while (bits > 8)
      while (bits > 8)
        {
        {
          if (*mask != *val)
          if (*mask != *val)
            break;
            break;
 
 
          ++mask;
          ++mask;
          ++val;
          ++val;
          bits -= 8;
          bits -= 8;
        }
        }
 
 
      if (bits < 8)
      if (bits < 8)
        {
        {
          if ((*mask & (0xff00 >> bits)) == (*val & (0xff00 >> bits)))
          if ((*mask & (0xff00 >> bits)) == (*val & (0xff00 >> bits)))
            /* Match!  */
            /* Match!  */
            break;
            break;
        }
        }
    }
    }
 
 
  return list[idx].val;
  return list[idx].val;
}
}
 
 
 
 
static int
static int
get_label (const struct sockaddr_storage *ss)
get_label (const struct sockaddr_storage *ss)
{
{
  /* XXX What is a good default value?  */
  /* XXX What is a good default value?  */
  return match_prefix (ss, default_labels, INT_MAX);
  return match_prefix (ss, default_labels, INT_MAX);
}
}
 
 
 
 
static int
static int
get_precedence (const struct sockaddr_storage *ss)
get_precedence (const struct sockaddr_storage *ss)
{
{
  /* XXX What is a good default value?  */
  /* XXX What is a good default value?  */
  return match_prefix (ss, default_precedence, 0);
  return match_prefix (ss, default_precedence, 0);
}
}
 
 
 
 
static int
static int
rfc3484_sort (const void *p1, const void *p2)
rfc3484_sort (const void *p1, const void *p2)
{
{
  const struct sort_result *a1 = (const struct sort_result *) p1;
  const struct sort_result *a1 = (const struct sort_result *) p1;
  const struct sort_result *a2 = (const struct sort_result *) p2;
  const struct sort_result *a2 = (const struct sort_result *) p2;
 
 
  /* Rule 1: Avoid unusable destinations.
  /* Rule 1: Avoid unusable destinations.
     We have the got_source_addr flag set if the destination is reachable.  */
     We have the got_source_addr flag set if the destination is reachable.  */
  if (a1->got_source_addr && ! a2->got_source_addr)
  if (a1->got_source_addr && ! a2->got_source_addr)
    return -1;
    return -1;
  if (! a1->got_source_addr && a2->got_source_addr)
  if (! a1->got_source_addr && a2->got_source_addr)
    return 1;
    return 1;
 
 
 
 
  /* Rule 2: Prefer matching scope.  Only interesting if both
  /* Rule 2: Prefer matching scope.  Only interesting if both
     destination addresses are IPv6.  */
     destination addresses are IPv6.  */
  int a1_dst_scope
  int a1_dst_scope
    = get_scope ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
    = get_scope ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
 
 
  int a2_dst_scope
  int a2_dst_scope
    = get_scope ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
    = get_scope ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
 
 
  if (a1->got_source_addr)
  if (a1->got_source_addr)
    {
    {
      int a1_src_scope = get_scope (&a1->source_addr);
      int a1_src_scope = get_scope (&a1->source_addr);
      int a2_src_scope = get_scope (&a2->source_addr);
      int a2_src_scope = get_scope (&a2->source_addr);
 
 
      if (a1_dst_scope == a1_src_scope && a2_dst_scope != a2_src_scope)
      if (a1_dst_scope == a1_src_scope && a2_dst_scope != a2_src_scope)
        return -1;
        return -1;
      if (a1_dst_scope != a1_src_scope && a2_dst_scope == a2_src_scope)
      if (a1_dst_scope != a1_src_scope && a2_dst_scope == a2_src_scope)
        return 1;
        return 1;
    }
    }
 
 
 
 
  /* Rule 3: Avoid deprecated addresses.
  /* Rule 3: Avoid deprecated addresses.
     That's something only the kernel could decide.  */
     That's something only the kernel could decide.  */
 
 
  /* Rule 4: Prefer home addresses.
  /* Rule 4: Prefer home addresses.
     Another thing only the kernel can decide.  */
     Another thing only the kernel can decide.  */
 
 
  /* Rule 5: Prefer matching label.  */
  /* Rule 5: Prefer matching label.  */
  if (a1->got_source_addr)
  if (a1->got_source_addr)
    {
    {
      int a1_dst_label
      int a1_dst_label
        = get_label ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
        = get_label ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
      int a1_src_label = get_label (&a1->source_addr);
      int a1_src_label = get_label (&a1->source_addr);
 
 
      int a2_dst_label
      int a2_dst_label
        = get_label ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
        = get_label ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
      int a2_src_label = get_label (&a2->source_addr);
      int a2_src_label = get_label (&a2->source_addr);
 
 
      if (a1_dst_label == a1_src_label && a2_dst_label != a2_src_label)
      if (a1_dst_label == a1_src_label && a2_dst_label != a2_src_label)
        return -1;
        return -1;
      if (a1_dst_label != a1_src_label && a2_dst_label == a2_src_label)
      if (a1_dst_label != a1_src_label && a2_dst_label == a2_src_label)
        return 1;
        return 1;
    }
    }
 
 
 
 
  /* Rule 6: Prefer higher precedence.  */
  /* Rule 6: Prefer higher precedence.  */
  int a1_prec
  int a1_prec
    = get_precedence ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
    = get_precedence ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
  int a2_prec
  int a2_prec
    = get_precedence ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
    = get_precedence ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
 
 
  if (a1_prec > a2_prec)
  if (a1_prec > a2_prec)
    return -1;
    return -1;
  if (a1_prec < a2_prec)
  if (a1_prec < a2_prec)
    return 1;
    return 1;
 
 
 
 
  /* Rule 7: Prefer native transport.
  /* Rule 7: Prefer native transport.
     XXX How to recognize tunnels?  */
     XXX How to recognize tunnels?  */
 
 
 
 
  /* Rule 8: Prefer smaller scope.  */
  /* Rule 8: Prefer smaller scope.  */
  if (a1_dst_scope < a2_dst_scope)
  if (a1_dst_scope < a2_dst_scope)
    return -1;
    return -1;
  if (a1_dst_scope > a2_dst_scope)
  if (a1_dst_scope > a2_dst_scope)
    return 1;
    return 1;
 
 
 
 
  /* Rule 9: Use longest matching prefix.  */
  /* Rule 9: Use longest matching prefix.  */
  if (a1->got_source_addr
  if (a1->got_source_addr
      && a1->dest_addr->ai_family == a2->dest_addr->ai_family)
      && a1->dest_addr->ai_family == a2->dest_addr->ai_family)
    {
    {
      int bit1 = 0;
      int bit1 = 0;
      int bit2 = 0;
      int bit2 = 0;
 
 
      if (a1->dest_addr->ai_family == PF_INET)
      if (a1->dest_addr->ai_family == PF_INET)
        {
        {
          assert (a1->source_addr.ss_family == PF_INET);
          assert (a1->source_addr.ss_family == PF_INET);
          assert (a2->source_addr.ss_family == PF_INET);
          assert (a2->source_addr.ss_family == PF_INET);
 
 
          struct sockaddr_in *in1_dst;
          struct sockaddr_in *in1_dst;
          struct sockaddr_in *in1_src;
          struct sockaddr_in *in1_src;
          struct sockaddr_in *in2_dst;
          struct sockaddr_in *in2_dst;
          struct sockaddr_in *in2_src;
          struct sockaddr_in *in2_src;
 
 
          in1_dst = (struct sockaddr_in *) a1->dest_addr->ai_addr;
          in1_dst = (struct sockaddr_in *) a1->dest_addr->ai_addr;
          in1_src = (struct sockaddr_in *) &a1->source_addr;
          in1_src = (struct sockaddr_in *) &a1->source_addr;
          in2_dst = (struct sockaddr_in *) a2->dest_addr->ai_addr;
          in2_dst = (struct sockaddr_in *) a2->dest_addr->ai_addr;
          in2_src = (struct sockaddr_in *) &a2->source_addr;
          in2_src = (struct sockaddr_in *) &a2->source_addr;
 
 
          bit1 = ffs (in1_dst->sin_addr.s_addr ^ in1_src->sin_addr.s_addr);
          bit1 = ffs (in1_dst->sin_addr.s_addr ^ in1_src->sin_addr.s_addr);
          bit2 = ffs (in2_dst->sin_addr.s_addr ^ in2_src->sin_addr.s_addr);
          bit2 = ffs (in2_dst->sin_addr.s_addr ^ in2_src->sin_addr.s_addr);
        }
        }
      else if (a1->dest_addr->ai_family == PF_INET6)
      else if (a1->dest_addr->ai_family == PF_INET6)
        {
        {
          assert (a1->source_addr.ss_family == PF_INET6);
          assert (a1->source_addr.ss_family == PF_INET6);
          assert (a2->source_addr.ss_family == PF_INET6);
          assert (a2->source_addr.ss_family == PF_INET6);
 
 
          struct sockaddr_in6 *in1_dst;
          struct sockaddr_in6 *in1_dst;
          struct sockaddr_in6 *in1_src;
          struct sockaddr_in6 *in1_src;
          struct sockaddr_in6 *in2_dst;
          struct sockaddr_in6 *in2_dst;
          struct sockaddr_in6 *in2_src;
          struct sockaddr_in6 *in2_src;
 
 
          in1_dst = (struct sockaddr_in6 *) a1->dest_addr->ai_addr;
          in1_dst = (struct sockaddr_in6 *) a1->dest_addr->ai_addr;
          in1_src = (struct sockaddr_in6 *) &a1->source_addr;
          in1_src = (struct sockaddr_in6 *) &a1->source_addr;
          in2_dst = (struct sockaddr_in6 *) a2->dest_addr->ai_addr;
          in2_dst = (struct sockaddr_in6 *) a2->dest_addr->ai_addr;
          in2_src = (struct sockaddr_in6 *) &a2->source_addr;
          in2_src = (struct sockaddr_in6 *) &a2->source_addr;
 
 
          int i;
          int i;
          for (i = 0; i < 4; ++i)
          for (i = 0; i < 4; ++i)
            if (in1_dst->sin6_addr.s6_addr32[i]
            if (in1_dst->sin6_addr.s6_addr32[i]
                != in1_src->sin6_addr.s6_addr32[i]
                != in1_src->sin6_addr.s6_addr32[i]
                || (in2_dst->sin6_addr.s6_addr32[i]
                || (in2_dst->sin6_addr.s6_addr32[i]
                    != in2_src->sin6_addr.s6_addr32[i]))
                    != in2_src->sin6_addr.s6_addr32[i]))
              break;
              break;
 
 
          if (i < 4)
          if (i < 4)
            {
            {
              bit1 = ffs (in1_dst->sin6_addr.s6_addr32[i]
              bit1 = ffs (in1_dst->sin6_addr.s6_addr32[i]
                          ^ in1_src->sin6_addr.s6_addr32[i]);
                          ^ in1_src->sin6_addr.s6_addr32[i]);
              bit2 = ffs (in2_dst->sin6_addr.s6_addr32[i]
              bit2 = ffs (in2_dst->sin6_addr.s6_addr32[i]
                          ^ in2_src->sin6_addr.s6_addr32[i]);
                          ^ in2_src->sin6_addr.s6_addr32[i]);
            }
            }
        }
        }
 
 
      if (bit1 > bit2)
      if (bit1 > bit2)
        return -1;
        return -1;
      if (bit1 < bit2)
      if (bit1 < bit2)
        return 1;
        return 1;
    }
    }
 
 
 
 
  /* Rule 10: Otherwise, leave the order unchanged.  */
  /* Rule 10: Otherwise, leave the order unchanged.  */
  return 0;
  return 0;
}
}
 
 
 
 
int
int
getaddrinfo (const char *name, const char *service,
getaddrinfo (const char *name, const char *service,
             const struct addrinfo *hints, struct addrinfo **pai)
             const struct addrinfo *hints, struct addrinfo **pai)
{
{
  int i = 0, j = 0, last_i = 0;
  int i = 0, j = 0, last_i = 0;
  int nresults = 0;
  int nresults = 0;
  struct addrinfo *p = NULL, **end;
  struct addrinfo *p = NULL, **end;
  struct gaih *g = gaih, *pg = NULL;
  struct gaih *g = gaih, *pg = NULL;
  struct gaih_service gaih_service, *pservice;
  struct gaih_service gaih_service, *pservice;
  struct addrinfo local_hints;
  struct addrinfo local_hints;
 
 
  if (name != NULL && name[0] == '*' && name[1] == 0)
  if (name != NULL && name[0] == '*' && name[1] == 0)
    name = NULL;
    name = NULL;
 
 
  if (service != NULL && service[0] == '*' && service[1] == 0)
  if (service != NULL && service[0] == '*' && service[1] == 0)
    service = NULL;
    service = NULL;
 
 
  if (name == NULL && service == NULL)
  if (name == NULL && service == NULL)
    return EAI_NONAME;
    return EAI_NONAME;
 
 
  if (hints == NULL)
  if (hints == NULL)
    hints = &default_hints;
    hints = &default_hints;
 
 
  if (hints->ai_flags
  if (hints->ai_flags
      & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|AI_ADDRCONFIG|AI_V4MAPPED
      & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|AI_ADDRCONFIG|AI_V4MAPPED
#ifdef HAVE_LIBIDN
#ifdef HAVE_LIBIDN
          |AI_IDN|AI_CANONIDN|AI_IDN_ALLOW_UNASSIGNED
          |AI_IDN|AI_CANONIDN|AI_IDN_ALLOW_UNASSIGNED
          |AI_IDN_USE_STD3_ASCII_RULES
          |AI_IDN_USE_STD3_ASCII_RULES
#endif
#endif
          |AI_NUMERICSERV|AI_ALL))
          |AI_NUMERICSERV|AI_ALL))
    return EAI_BADFLAGS;
    return EAI_BADFLAGS;
 
 
  if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
  if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
    return EAI_BADFLAGS;
    return EAI_BADFLAGS;
 
 
  if (hints->ai_flags & AI_ADDRCONFIG)
  if (hints->ai_flags & AI_ADDRCONFIG)
    {
    {
      /* Determine whether we have IPv4 or IPv6 interfaces or both.
      /* Determine whether we have IPv4 or IPv6 interfaces or both.
         We cannot cache the results since new interfaces could be
         We cannot cache the results since new interfaces could be
         added at any time.  */
         added at any time.  */
      bool seen_ipv4;
      bool seen_ipv4;
      bool seen_ipv6;
      bool seen_ipv6;
      __check_pf (&seen_ipv4, &seen_ipv6);
      __check_pf (&seen_ipv4, &seen_ipv6);
 
 
      /* Now make a decision on what we return, if anything.  */
      /* Now make a decision on what we return, if anything.  */
      if (hints->ai_family == PF_UNSPEC && (seen_ipv4 || seen_ipv6))
      if (hints->ai_family == PF_UNSPEC && (seen_ipv4 || seen_ipv6))
        {
        {
          /* If we haven't seen both IPv4 and IPv6 interfaces we can
          /* If we haven't seen both IPv4 and IPv6 interfaces we can
             narrow down the search.  */
             narrow down the search.  */
          if (! seen_ipv4 || ! seen_ipv6)
          if (! seen_ipv4 || ! seen_ipv6)
            {
            {
              local_hints = *hints;
              local_hints = *hints;
              local_hints.ai_family = seen_ipv4 ? PF_INET : PF_INET6;
              local_hints.ai_family = seen_ipv4 ? PF_INET : PF_INET6;
              hints = &local_hints;
              hints = &local_hints;
            }
            }
        }
        }
      else if ((hints->ai_family == PF_INET && ! seen_ipv4)
      else if ((hints->ai_family == PF_INET && ! seen_ipv4)
               || (hints->ai_family == PF_INET6 && ! seen_ipv6))
               || (hints->ai_family == PF_INET6 && ! seen_ipv6))
        /* We cannot possibly return a valid answer.  */
        /* We cannot possibly return a valid answer.  */
        return EAI_NONAME;
        return EAI_NONAME;
    }
    }
 
 
  if (service && service[0])
  if (service && service[0])
    {
    {
      char *c;
      char *c;
      gaih_service.name = service;
      gaih_service.name = service;
      gaih_service.num = strtoul (gaih_service.name, &c, 10);
      gaih_service.num = strtoul (gaih_service.name, &c, 10);
      if (*c != '\0')
      if (*c != '\0')
        {
        {
          if (hints->ai_flags & AI_NUMERICSERV)
          if (hints->ai_flags & AI_NUMERICSERV)
            return EAI_NONAME;
            return EAI_NONAME;
 
 
          gaih_service.num = -1;
          gaih_service.num = -1;
        }
        }
 
 
      pservice = &gaih_service;
      pservice = &gaih_service;
    }
    }
  else
  else
    pservice = NULL;
    pservice = NULL;
 
 
  if (pai)
  if (pai)
    end = &p;
    end = &p;
  else
  else
    end = NULL;
    end = NULL;
 
 
  while (g->gaih)
  while (g->gaih)
    {
    {
      if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
      if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
        {
        {
          j++;
          j++;
          if (pg == NULL || pg->gaih != g->gaih)
          if (pg == NULL || pg->gaih != g->gaih)
            {
            {
              pg = g;
              pg = g;
              i = g->gaih (name, pservice, hints, end);
              i = g->gaih (name, pservice, hints, end);
              if (i != 0)
              if (i != 0)
                {
                {
                  /* EAI_NODATA is a more specific result as it says that
                  /* EAI_NODATA is a more specific result as it says that
                     we found a result but it is not usable.  */
                     we found a result but it is not usable.  */
                  if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
                  if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
                    last_i = i;
                    last_i = i;
 
 
                  if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
                  if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
                    {
                    {
                      ++g;
                      ++g;
                      continue;
                      continue;
                    }
                    }
 
 
                  freeaddrinfo (p);
                  freeaddrinfo (p);
 
 
                  return -(i & GAIH_EAI);
                  return -(i & GAIH_EAI);
                }
                }
              if (end)
              if (end)
                while (*end)
                while (*end)
                  {
                  {
                    end = &((*end)->ai_next);
                    end = &((*end)->ai_next);
                    ++nresults;
                    ++nresults;
                  }
                  }
            }
            }
        }
        }
      ++g;
      ++g;
    }
    }
 
 
  if (j == 0)
  if (j == 0)
    return EAI_FAMILY;
    return EAI_FAMILY;
 
 
  if (nresults > 1)
  if (nresults > 1)
    {
    {
      /* Sort results according to RFC 3484.  */
      /* Sort results according to RFC 3484.  */
      struct sort_result results[nresults];
      struct sort_result results[nresults];
      struct addrinfo *q;
      struct addrinfo *q;
      struct addrinfo *last = NULL;
      struct addrinfo *last = NULL;
      char *canonname = NULL;
      char *canonname = NULL;
 
 
      for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next)
      for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next)
        {
        {
          results[i].dest_addr = q;
          results[i].dest_addr = q;
          results[i].got_source_addr = false;
          results[i].got_source_addr = false;
 
 
          /* If we just looked up the address for a different
          /* If we just looked up the address for a different
             protocol, reuse the result.  */
             protocol, reuse the result.  */
          if (last != NULL && last->ai_addrlen == q->ai_addrlen
          if (last != NULL && last->ai_addrlen == q->ai_addrlen
              && memcmp (last->ai_addr, q->ai_addr, q->ai_addrlen) == 0)
              && memcmp (last->ai_addr, q->ai_addr, q->ai_addrlen) == 0)
            {
            {
              memcpy (&results[i].source_addr, &results[i - 1].source_addr,
              memcpy (&results[i].source_addr, &results[i - 1].source_addr,
                      results[i - 1].source_addr_len);
                      results[i - 1].source_addr_len);
              results[i].source_addr_len = results[i - 1].source_addr_len;
              results[i].source_addr_len = results[i - 1].source_addr_len;
              results[i].got_source_addr = results[i - 1].got_source_addr;
              results[i].got_source_addr = results[i - 1].got_source_addr;
            }
            }
          else
          else
            {
            {
              /* We overwrite the type with SOCK_DGRAM since we do not
              /* We overwrite the type with SOCK_DGRAM since we do not
                 want connect() to connect to the other side.  If we
                 want connect() to connect to the other side.  If we
                 cannot determine the source address remember this
                 cannot determine the source address remember this
                 fact.  */
                 fact.  */
              int fd = socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP);
              int fd = socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP);
              socklen_t sl = sizeof (results[i].source_addr);
              socklen_t sl = sizeof (results[i].source_addr);
              if (fd != -1
              if (fd != -1
                  && connect (fd, q->ai_addr, q->ai_addrlen) == 0
                  && connect (fd, q->ai_addr, q->ai_addrlen) == 0
                  && getsockname (fd,
                  && getsockname (fd,
                                    (struct sockaddr *) &results[i].source_addr,
                                    (struct sockaddr *) &results[i].source_addr,
                                    &sl) == 0)
                                    &sl) == 0)
                {
                {
                  results[i].source_addr_len = sl;
                  results[i].source_addr_len = sl;
                  results[i].got_source_addr = true;
                  results[i].got_source_addr = true;
                }
                }
              else
              else
                /* Just make sure that if we have to process the same
                /* Just make sure that if we have to process the same
                   address again we do not copy any memory.  */
                   address again we do not copy any memory.  */
                results[i].source_addr_len = 0;
                results[i].source_addr_len = 0;
 
 
              if (fd != -1)
              if (fd != -1)
                close_not_cancel_no_status (fd);
                close_not_cancel_no_status (fd);
            }
            }
 
 
          /* Remember the canonical name.  */
          /* Remember the canonical name.  */
          if (q->ai_canonname != NULL)
          if (q->ai_canonname != NULL)
            {
            {
              assert (canonname == NULL);
              assert (canonname == NULL);
              canonname = q->ai_canonname;
              canonname = q->ai_canonname;
              q->ai_canonname = NULL;
              q->ai_canonname = NULL;
            }
            }
        }
        }
 
 
      /* We got all the source addresses we can get, now sort using
      /* We got all the source addresses we can get, now sort using
         the information.  */
         the information.  */
      qsort (results, nresults, sizeof (results[0]), rfc3484_sort);
      qsort (results, nresults, sizeof (results[0]), rfc3484_sort);
 
 
      /* Queue the results up as they come out of sorting.  */
      /* Queue the results up as they come out of sorting.  */
      q = p = results[0].dest_addr;
      q = p = results[0].dest_addr;
      for (i = 1; i < nresults; ++i)
      for (i = 1; i < nresults; ++i)
        q = q->ai_next = results[i].dest_addr;
        q = q->ai_next = results[i].dest_addr;
      q->ai_next = NULL;
      q->ai_next = NULL;
 
 
      /* Fill in the canonical name into the new first entry.  */
      /* Fill in the canonical name into the new first entry.  */
      p->ai_canonname = canonname;
      p->ai_canonname = canonname;
    }
    }
 
 
  if (p)
  if (p)
    {
    {
      *pai = p;
      *pai = p;
      return 0;
      return 0;
    }
    }
 
 
  if (pai == NULL && last_i == 0)
  if (pai == NULL && last_i == 0)
    return 0;
    return 0;
 
 
  return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
  return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
}
}
libc_hidden_def (getaddrinfo)
libc_hidden_def (getaddrinfo)
 
 
static_link_warning (getaddrinfo)
static_link_warning (getaddrinfo)
 
 
void
void
freeaddrinfo (struct addrinfo *ai)
freeaddrinfo (struct addrinfo *ai)
{
{
  struct addrinfo *p;
  struct addrinfo *p;
 
 
  while (ai != NULL)
  while (ai != NULL)
    {
    {
      p = ai;
      p = ai;
      ai = ai->ai_next;
      ai = ai->ai_next;
      free (p->ai_canonname);
      free (p->ai_canonname);
      free (p);
      free (p);
    }
    }
}
}
libc_hidden_def (freeaddrinfo)
libc_hidden_def (freeaddrinfo)
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.