int getaddrinfo()

in libs/libc/netdb/lib_getaddrinfo.c [133:442]


int getaddrinfo(FAR const char *hostname, FAR const char *servname,
                FAR const struct addrinfo *hint, FAR struct addrinfo **res)
{
  int family = AF_UNSPEC;
  int port = 0;
  int flags = 0;
  int proto = 0;
  int socktype = 0;
  FAR char *hostbuffer;
  FAR struct hostent_s host;
  FAR struct ai_s *ai;
  FAR struct ai_s *prev_ai = NULL;
  const int valid_flags = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST |
                          AI_NUMERICSERV | AI_V4MAPPED | AI_ALL |
                          AI_ADDRCONFIG;
  int ret = OK;
  int i;

  if (hostname == NULL && servname == NULL)
    {
      return EAI_NONAME;
    }

  if (hint)
    {
      family   = hint->ai_family;
      flags    = hint->ai_flags;
      proto    = hint->ai_protocol;
      socktype = hint->ai_socktype;

      if ((flags & valid_flags) != flags)
        {
          return EAI_BADFLAGS;
        }

      if (family != AF_INET &&
          family != AF_INET6 &&
          family != AF_LOCAL &&
          family != AF_RPMSG &&
          family != AF_VSOCK &&
          family != AF_UNSPEC)
        {
          return EAI_FAMILY;
        }
    }

  if (servname != NULL)
    {
      struct servent ent;
      FAR struct servent *sp;
      FAR char *endp;

      port = strtol(servname, &endp, 10);
      if (port >= 0 && port <= 65535 && *endp == '\0')
        {
          /* Force network byte order */

          port = HTONS(port);
        }
      else if ((flags & AI_NUMERICSERV) != 0)
        {
          return EAI_NONAME;
        }
      else if (getservbyname_r(servname, NULL, &ent, NULL, 0, &sp) == OK)
        {
          /* The s_port field of struct servent is required to
           * be in network byte order (per OpenGroup.org)
           */

          port = sp->s_port;
        }
      else
        {
          return EAI_SERVICE;
        }
    }

  *res = NULL;

  /* If hostname is not NULL, then the AI_PASSIVE flag is ignored. */

  if ((flags & AI_PASSIVE) != 0 && hostname == NULL)
    {
#ifdef CONFIG_NET_IPv4
      if (family == AF_INET || family == AF_UNSPEC)
        {
          struct in_addr addr;
          memset(&addr, 0, sizeof(struct in_addr));
          ai = alloc_ai(AF_INET, socktype, proto, port, &addr);
          if (ai != NULL)
            {
              *res = (FAR struct addrinfo *)ai;
            }
        }
#endif

#ifdef CONFIG_NET_IPv6
      if (family == AF_INET6 || family == AF_UNSPEC)
        {
          struct in6_addr addr;
          memset(&addr, 0, sizeof(struct in6_addr));
          ai = alloc_ai(AF_INET6, socktype, proto, port, &addr);
          if (ai != NULL)
            {
              /* Can return both IPv4 and IPv6 loopback. */

              if (*res != NULL)
                {
                  (*res)->ai_next = (FAR struct addrinfo *)ai;
                }
              else
                {
                  *res = (FAR struct addrinfo *)ai;
                }
            }
        }
#endif

#if defined(CONFIG_NET_RPMSG)
      if (family == AF_RPMSG || family == AF_UNSPEC)
        {
          ai = alloc_ai(AF_RPMSG, socktype, proto, port, "");
          if (ai != NULL)
            {
              if (*res != NULL)
                {
                  (*res)->ai_next = (FAR struct addrinfo *)ai;
                }
              else
                {
                  *res = (FAR struct addrinfo *)ai;
                }
            }
        }
#endif

#if defined(CONFIG_NET_VSOCK)
      if (family == AF_VSOCK || family == AF_UNSPEC)
        {
          /* "-1" <--> VMADDR_CID_ANY */

          ai = alloc_ai(AF_VSOCK, socktype, proto, port, "-1");
          if (ai != NULL)
            {
              if (*res != NULL)
                {
                  (*res)->ai_next = (FAR struct addrinfo *)ai;
                }
              else
                {
                  *res = (FAR struct addrinfo *)ai;
                }
            }
        }
#endif

      return (*res != NULL) ? OK : EAI_MEMORY;
    }

  if (hostname == NULL)
    {
#ifdef CONFIG_NET_LOOPBACK
      /* Local service. */

#ifdef CONFIG_NET_IPv4
      if (family == AF_INET || family == AF_UNSPEC)
        {
          ai = alloc_ai(AF_INET, socktype, proto, port,
                        &g_lo_ipv4addr);
          if (ai != NULL)
            {
              *res = (FAR struct addrinfo *)ai;
            }
        }
#endif

#ifdef CONFIG_NET_IPv6
      if (family == AF_INET6 || family == AF_UNSPEC)
        {
          ai = alloc_ai(AF_INET6, socktype, proto, port,
                        &g_lo_ipv6addr);
          if (ai != NULL)
            {
              /* Can return both IPv4 and IPv6 loopback. */

              if (*res != NULL)
                {
                  (*res)->ai_next = (FAR struct addrinfo *)ai;
                }
              else
                {
                  *res = (FAR struct addrinfo *)ai;
                }
            }
        }
#endif
#endif

#if defined(CONFIG_NET_VSOCK)
      if (family == AF_VSOCK || family == AF_UNSPEC)
        {
          /* "1" <--> VMADDR_CID_LOCAL */

          ai = alloc_ai(AF_VSOCK, socktype, proto, port, "1");
          if (ai != NULL)
            {
              if (*res != NULL)
                {
                  (*res)->ai_next = (FAR struct addrinfo *)ai;
                }
              else
                {
                  *res = (FAR struct addrinfo *)ai;
                }
            }
        }
#endif

#if defined(CONFIG_NET_LOOPBACK) || defined(CONFIG_NET_VSOCK)
      return (*res != NULL) ? OK : EAI_MEMORY;
#else
      /* Local service, but no loopback so cannot succeed. */

      return EAI_FAIL;
#endif
    }

#if defined(CONFIG_NET_LOCAL) || defined(CONFIG_NET_RPMSG) || defined(CONFIG_NET_VSOCK)
  if (family == AF_LOCAL || family == AF_RPMSG || family == AF_VSOCK)
    {
      ai = alloc_ai(family, socktype, proto, port, hostname);
      if (ai != NULL)
        {
          *res = (FAR struct addrinfo *)ai;
          if (flags & AI_CANONNAME)
            {
              ai->ai.ai_canonname = (FAR char *)hostname;
            }
        }

      return (*res != NULL) ? OK : EAI_MEMORY;
    }
#endif

  hostbuffer = lib_malloc(CONFIG_NETDB_BUFSIZE);
  if (hostbuffer == NULL)
    {
      return EAI_MEMORY;
    }

  gethostentbyname_r(hostname, &host,
                     hostbuffer, CONFIG_NETDB_BUFSIZE, &ret, flags);
  if (ret != OK)
    {
      lib_free(hostbuffer);
      return ret;
    }

  for (i = 0; host.h_addr_list[i]; i++)
    {
      if (family != AF_UNSPEC && host.h_addrtypes[i] != family)
        {
          /* Filter by protocol family. */

          continue;
        }

      /* REVISIT: filter by socktype and protocol not implemented. */

      ai = alloc_ai(host.h_addrtypes[i], socktype, proto, port,
                    host.h_addr_list[i]);
      if (ai == NULL)
        {
          if (*res)
            {
              freeaddrinfo(*res);
            }

          lib_free(hostbuffer);
          return EAI_MEMORY;
        }

      /* REVISIT: grok canonical name.
       *
       * OpenGroup: "if the canonical name is not available, then
       * ai_canonname shall refer to the hostname argument or a string
       * with the same contents."
       */

      ai->ai.ai_canonname = (FAR char *)hostname;

      /* Add result to linked list.
       * TODO: RFC 3484/6724 destination address sort not implemented.
       */

      if (prev_ai != NULL)
        {
          prev_ai->ai.ai_next = (FAR struct addrinfo *)ai;
        }
      else
        {
          *res = (FAR struct addrinfo *)ai;
        }

      prev_ai = ai;
    }

  lib_free(hostbuffer);
  return (*res != NULL) ? OK : EAI_FAMILY;
}