/**************************************************************************** * 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 #include #include #include #include #include #include #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; }