123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444 |
- /* Get address information (partial implementation).
- Copyright (C) 1997, 2001-2002, 2004-2014 Free Software Foundation, Inc.
- Contributed by Simon Josefsson <simon@josefsson.org>.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>. */
- /* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc
- optimizes away the sa == NULL test below. */
- #define _GL_ARG_NONNULL(params)
- #include <config.h>
- #include <netdb.h>
- #if HAVE_NETINET_IN_H
- # include <netinet/in.h>
- #endif
- /* Get inet_ntop. */
- #include <arpa/inet.h>
- /* Get calloc. */
- #include <stdlib.h>
- /* Get memcpy, strdup. */
- #include <string.h>
- /* Get snprintf. */
- #include <stdio.h>
- #include <stdbool.h>
- #include "gettext.h"
- #define _(String) gettext (String)
- #define N_(String) String
- /* BeOS has AF_INET, but not PF_INET. */
- #ifndef PF_INET
- # define PF_INET AF_INET
- #endif
- /* BeOS also lacks PF_UNSPEC. */
- #ifndef PF_UNSPEC
- # define PF_UNSPEC 0
- #endif
- #if defined _WIN32 || defined __WIN32__
- # define WINDOWS_NATIVE
- #endif
- /* gl_sockets_startup */
- #include "sockets.h"
- #ifdef WINDOWS_NATIVE
- typedef int (WSAAPI *getaddrinfo_func) (const char*, const char*,
- const struct addrinfo*,
- struct addrinfo**);
- typedef void (WSAAPI *freeaddrinfo_func) (struct addrinfo*);
- typedef int (WSAAPI *getnameinfo_func) (const struct sockaddr*,
- socklen_t, char*, DWORD,
- char*, DWORD, int);
- static getaddrinfo_func getaddrinfo_ptr = NULL;
- static freeaddrinfo_func freeaddrinfo_ptr = NULL;
- static getnameinfo_func getnameinfo_ptr = NULL;
- static int
- use_win32_p (void)
- {
- static int done = 0;
- HMODULE h;
- if (done)
- return getaddrinfo_ptr ? 1 : 0;
- done = 1;
- h = GetModuleHandle ("ws2_32.dll");
- if (h)
- {
- getaddrinfo_ptr = (getaddrinfo_func) GetProcAddress (h, "getaddrinfo");
- freeaddrinfo_ptr = (freeaddrinfo_func) GetProcAddress (h, "freeaddrinfo");
- getnameinfo_ptr = (getnameinfo_func) GetProcAddress (h, "getnameinfo");
- }
- /* If either is missing, something is odd. */
- if (!getaddrinfo_ptr || !freeaddrinfo_ptr || !getnameinfo_ptr)
- {
- getaddrinfo_ptr = NULL;
- freeaddrinfo_ptr = NULL;
- getnameinfo_ptr = NULL;
- return 0;
- }
- gl_sockets_startup (SOCKETS_1_1);
- return 1;
- }
- #endif
- static bool
- validate_family (int family)
- {
- /* FIXME: Support more families. */
- #if HAVE_IPV4
- if (family == PF_INET)
- return true;
- #endif
- #if HAVE_IPV6
- if (family == PF_INET6)
- return true;
- #endif
- if (family == PF_UNSPEC)
- return true;
- return false;
- }
- /* Translate name of a service location and/or a service name to set of
- socket addresses. */
- int
- getaddrinfo (const char *restrict nodename,
- const char *restrict servname,
- const struct addrinfo *restrict hints,
- struct addrinfo **restrict res)
- {
- struct addrinfo *tmp;
- int port = 0;
- struct hostent *he;
- void *storage;
- size_t size;
- #if HAVE_IPV6
- struct v6_pair {
- struct addrinfo addrinfo;
- struct sockaddr_in6 sockaddr_in6;
- };
- #endif
- #if HAVE_IPV4
- struct v4_pair {
- struct addrinfo addrinfo;
- struct sockaddr_in sockaddr_in;
- };
- #endif
- #ifdef WINDOWS_NATIVE
- if (use_win32_p ())
- return getaddrinfo_ptr (nodename, servname, hints, res);
- #endif
- if (hints && (hints->ai_flags & ~(AI_CANONNAME|AI_PASSIVE)))
- /* FIXME: Support more flags. */
- return EAI_BADFLAGS;
- if (hints && !validate_family (hints->ai_family))
- return EAI_FAMILY;
- if (hints &&
- hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM)
- /* FIXME: Support other socktype. */
- return EAI_SOCKTYPE; /* FIXME: Better return code? */
- if (!nodename)
- {
- if (!(hints->ai_flags & AI_PASSIVE))
- return EAI_NONAME;
- #ifdef HAVE_IPV6
- nodename = (hints->ai_family == AF_INET6) ? "::" : "0.0.0.0";
- #else
- nodename = "0.0.0.0";
- #endif
- }
- if (servname)
- {
- struct servent *se = NULL;
- const char *proto =
- (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
- if (hints == NULL || !(hints->ai_flags & AI_NUMERICSERV))
- /* FIXME: Use getservbyname_r if available. */
- se = getservbyname (servname, proto);
- if (!se)
- {
- char *c;
- if (!(*servname >= '0' && *servname <= '9'))
- return EAI_NONAME;
- port = strtoul (servname, &c, 10);
- if (*c || port > 0xffff)
- return EAI_NONAME;
- port = htons (port);
- }
- else
- port = se->s_port;
- }
- /* FIXME: Use gethostbyname_r if available. */
- he = gethostbyname (nodename);
- if (!he || he->h_addr_list[0] == NULL)
- return EAI_NONAME;
- switch (he->h_addrtype)
- {
- #if HAVE_IPV6
- case PF_INET6:
- size = sizeof (struct v6_pair);
- break;
- #endif
- #if HAVE_IPV4
- case PF_INET:
- size = sizeof (struct v4_pair);
- break;
- #endif
- default:
- return EAI_NODATA;
- }
- storage = calloc (1, size);
- if (!storage)
- return EAI_MEMORY;
- switch (he->h_addrtype)
- {
- #if HAVE_IPV6
- case PF_INET6:
- {
- struct v6_pair *p = storage;
- struct sockaddr_in6 *sinp = &p->sockaddr_in6;
- tmp = &p->addrinfo;
- if (port)
- sinp->sin6_port = port;
- if (he->h_length != sizeof (sinp->sin6_addr))
- {
- free (storage);
- return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */
- }
- memcpy (&sinp->sin6_addr, he->h_addr_list[0], sizeof sinp->sin6_addr);
- tmp->ai_addr = (struct sockaddr *) sinp;
- tmp->ai_addrlen = sizeof *sinp;
- }
- break;
- #endif
- #if HAVE_IPV4
- case PF_INET:
- {
- struct v4_pair *p = storage;
- struct sockaddr_in *sinp = &p->sockaddr_in;
- tmp = &p->addrinfo;
- if (port)
- sinp->sin_port = port;
- if (he->h_length != sizeof (sinp->sin_addr))
- {
- free (storage);
- return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */
- }
- memcpy (&sinp->sin_addr, he->h_addr_list[0], sizeof sinp->sin_addr);
- tmp->ai_addr = (struct sockaddr *) sinp;
- tmp->ai_addrlen = sizeof *sinp;
- }
- break;
- #endif
- default:
- free (storage);
- return EAI_NODATA;
- }
- if (hints && hints->ai_flags & AI_CANONNAME)
- {
- const char *cn;
- if (he->h_name)
- cn = he->h_name;
- else
- cn = nodename;
- tmp->ai_canonname = strdup (cn);
- if (!tmp->ai_canonname)
- {
- free (storage);
- return EAI_MEMORY;
- }
- }
- tmp->ai_protocol = (hints) ? hints->ai_protocol : 0;
- tmp->ai_socktype = (hints) ? hints->ai_socktype : 0;
- tmp->ai_addr->sa_family = he->h_addrtype;
- tmp->ai_family = he->h_addrtype;
- #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
- switch (he->h_addrtype)
- {
- #if HAVE_IPV4
- case AF_INET:
- tmp->ai_addr->sa_len = sizeof (struct sockaddr_in);
- break;
- #endif
- #if HAVE_IPV6
- case AF_INET6:
- tmp->ai_addr->sa_len = sizeof (struct sockaddr_in6);
- break;
- #endif
- }
- #endif
- /* FIXME: If more than one address, create linked list of addrinfo's. */
- *res = tmp;
- return 0;
- }
- /* Free 'addrinfo' structure AI including associated storage. */
- void
- freeaddrinfo (struct addrinfo *ai)
- {
- #ifdef WINDOWS_NATIVE
- if (use_win32_p ())
- {
- freeaddrinfo_ptr (ai);
- return;
- }
- #endif
- while (ai)
- {
- struct addrinfo *cur;
- cur = ai;
- ai = ai->ai_next;
- free (cur->ai_canonname);
- free (cur);
- }
- }
- int
- getnameinfo (const struct sockaddr *restrict sa, socklen_t salen,
- char *restrict node, socklen_t nodelen,
- char *restrict service, socklen_t servicelen,
- int flags)
- {
- #ifdef WINDOWS_NATIVE
- if (use_win32_p ())
- return getnameinfo_ptr (sa, salen, node, nodelen,
- service, servicelen, flags);
- #endif
- /* FIXME: Support other flags. */
- if ((node && nodelen > 0 && !(flags & NI_NUMERICHOST)) ||
- (service && servicelen > 0 && !(flags & NI_NUMERICHOST)) ||
- (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV)))
- return EAI_BADFLAGS;
- if (sa == NULL || salen < sizeof (sa->sa_family))
- return EAI_FAMILY;
- switch (sa->sa_family)
- {
- #if HAVE_IPV4
- case AF_INET:
- if (salen < sizeof (struct sockaddr_in))
- return EAI_FAMILY;
- break;
- #endif
- #if HAVE_IPV6
- case AF_INET6:
- if (salen < sizeof (struct sockaddr_in6))
- return EAI_FAMILY;
- break;
- #endif
- default:
- return EAI_FAMILY;
- }
- if (node && nodelen > 0 && flags & NI_NUMERICHOST)
- {
- switch (sa->sa_family)
- {
- #if HAVE_IPV4
- case AF_INET:
- if (!inet_ntop (AF_INET,
- &(((const struct sockaddr_in *) sa)->sin_addr),
- node, nodelen))
- return EAI_SYSTEM;
- break;
- #endif
- #if HAVE_IPV6
- case AF_INET6:
- if (!inet_ntop (AF_INET6,
- &(((const struct sockaddr_in6 *) sa)->sin6_addr),
- node, nodelen))
- return EAI_SYSTEM;
- break;
- #endif
- default:
- return EAI_FAMILY;
- }
- }
- if (service && servicelen > 0 && flags & NI_NUMERICSERV)
- switch (sa->sa_family)
- {
- #if HAVE_IPV4
- case AF_INET:
- #endif
- #if HAVE_IPV6
- case AF_INET6:
- #endif
- {
- unsigned short int port
- = ntohs (((const struct sockaddr_in *) sa)->sin_port);
- if (servicelen <= snprintf (service, servicelen, "%u", port))
- return EAI_OVERFLOW;
- }
- break;
- }
- return 0;
- }
|