nuttx/libs/libc/netdb/lib_getaddrinfo.c

369 lines
9.7 KiB
C
Raw Normal View History

/****************************************************************************
* libs/libc/netdb/lib_getaddrinfo.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <string.h>
#include <arpa/inet.h>
#include <nuttx/net/loopback.h>
#include <netpacket/rpmsg.h>
#include <netdb.h>
#include <sys/un.h>
#include "libc.h"
#include "lib_netdb.h"
/****************************************************************************
* Private Data Types
****************************************************************************/
struct ai_s
{
struct addrinfo ai;
union
{
struct sockaddr_un sun;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
struct sockaddr_rpmsg srp;
} sa;
};
/****************************************************************************
* Private Functions
****************************************************************************/
FAR static struct ai_s *alloc_ai(int family, int socktype, int protocol,
int port, FAR const void *addr)
{
FAR struct ai_s *ai;
ai = lib_zalloc(sizeof(struct ai_s));
if (ai == NULL)
{
return ai;
}
ai->ai.ai_addr = (FAR struct sockaddr *)&ai->sa;
ai->ai.ai_family = family;
ai->ai.ai_socktype = socktype;
ai->ai.ai_protocol = protocol;
switch (family)
{
#ifdef CONFIG_NET_LOCAL
case AF_LOCAL:
ai->ai.ai_addrlen = sizeof(struct sockaddr_un);
ai->sa.sun.sun_family = AF_LOCAL;
strlcpy(ai->sa.sun.sun_path, addr, sizeof(ai->sa.sun.sun_path));
break;
#endif
#ifdef CONFIG_NET_IPv4
case AF_INET:
ai->ai.ai_addrlen = sizeof(struct sockaddr_in);
ai->sa.sin.sin_family = AF_INET;
ai->sa.sin.sin_port = port; /* Already network order */
memcpy(&ai->sa.sin.sin_addr, addr, sizeof(ai->sa.sin.sin_addr));
break;
#endif
#ifdef CONFIG_NET_IPv6
case AF_INET6:
ai->ai.ai_addrlen = sizeof(struct sockaddr_in6);
ai->sa.sin6.sin6_family = AF_INET6;
ai->sa.sin6.sin6_port = port; /* Already network order */
memcpy(&ai->sa.sin6.sin6_addr, addr, sizeof(ai->sa.sin6.sin6_addr));
break;
#endif
#ifdef CONFIG_NET_RPMSG
case AF_RPMSG:
ai->ai.ai_addrlen = sizeof(struct sockaddr_rpmsg);
ai->sa.srp.rp_family = AF_RPMSG;
strlcpy(ai->sa.srp.rp_cpu, addr, sizeof(ai->sa.srp.rp_cpu));
snprintf(ai->sa.srp.rp_name, sizeof(ai->sa.srp.rp_name), "%d", port);
break;
#endif
}
return ai;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: getaddrinfo
****************************************************************************/
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_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)
{
struct in6_addr addr;
memset(&addr, 0, sizeof(struct in6_addr));
#ifdef CONFIG_NET_IPv4
if (family == AF_INET || family == AF_UNSPEC)
{
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)
{
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
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
return (*res != NULL) ? OK : EAI_MEMORY;
#else
/* Local service, but no loopback so cannot succeed. */
return EAI_FAIL;
#endif /* CONFIG_NET_LOOPBACK */
}
#if defined(CONFIG_NET_LOCAL) || defined(CONFIG_NET_RPMSG)
if (family == AF_LOCAL || family == AF_RPMSG)
{
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;
}