X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fgetaddrinfo.c;h=a6a4066323bd7edc3c82e3679645eb661de829d0;hb=533101a268dc5015a140ff4695d8eeb04fbe57b6;hp=145eac519bfc77de7193a9acfe9959b448d05f89;hpb=f34b5ec45f70109f4d3d321ea17f996ff4b1c14d;p=gnulib.git diff --git a/lib/getaddrinfo.c b/lib/getaddrinfo.c index 145eac519..a6a406632 100644 --- a/lib/getaddrinfo.c +++ b/lib/getaddrinfo.c @@ -1,5 +1,5 @@ /* Get address information (partial implementation). - Copyright (C) 1997, 2001, 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 1997, 2001-2002, 2004-2013 Free Software Foundation, Inc. Contributed by Simon Josefsson . This program is free software; you can redistribute it and/or modify @@ -13,21 +13,31 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + along with this program; if not, see . */ -#if HAVE_CONFIG_H -# include +/* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc + optimizes away the sa == NULL test below. */ +#define _GL_ARG_NONNULL(params) + +#include + +#include + +#if HAVE_NETINET_IN_H +# include #endif +/* Get inet_ntop. */ +#include + /* Get calloc. */ #include -/* Get memcpy. */ +/* Get memcpy, strdup. */ #include -/* Get struct hostent. */ -#include +/* Get snprintf. */ +#include #include @@ -35,9 +45,71 @@ #define _(String) gettext (String) #define N_(String) String -#include "getaddrinfo.h" +/* 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 inline bool +static bool validate_family (int family) { /* FIXME: Support more families. */ @@ -58,45 +130,79 @@ validate_family (int family) socket addresses. */ int getaddrinfo (const char *restrict nodename, - const char *restrict servname, - const struct addrinfo *restrict hints, - struct addrinfo **restrict res) + const char *restrict servname, + const struct addrinfo *restrict hints, + struct addrinfo **restrict res) { struct addrinfo *tmp; - struct servent *se; + int port = 0; struct hostent *he; - size_t sinlen; + 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) + 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) - /* FIXME: Support more socket types. */ - return EAI_SOCKTYPE; - if (hints && - hints->ai_protocol != SOCK_STREAM && hints->ai_protocol != SOCK_DGRAM) - /* FIXME: Support other protocols. */ - return EAI_SERVICE; /* FIXME: Better return code? */ + hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM) + /* FIXME: Support other socktype. */ + return EAI_SOCKTYPE; /* FIXME: Better return code? */ if (!nodename) - /* FIXME: Support server bind mode. */ - return EAI_NONAME; + { + 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_protocol == SOCK_DGRAM) ? "udp" : "tcp"; + (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp"; - /* FIXME: Use getservbyname_r if available. */ - se = getservbyname (servname, proto); + if (hints == NULL || !(hints->ai_flags & AI_NUMERICSERV)) + /* FIXME: Use getservbyname_r if available. */ + se = getservbyname (servname, proto); if (!se) - return EAI_SERVICE; + { + 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. */ @@ -108,13 +214,13 @@ getaddrinfo (const char *restrict nodename, { #if HAVE_IPV6 case PF_INET6: - sinlen = sizeof (struct sockaddr_in6); + size = sizeof (struct v6_pair); break; #endif #if HAVE_IPV4 case PF_INET: - sinlen = sizeof (struct sockaddr_in); + size = sizeof (struct v4_pair); break; #endif @@ -122,8 +228,8 @@ getaddrinfo (const char *restrict nodename, return EAI_NODATA; } - tmp = calloc (1, sizeof (*tmp) + sinlen); - if (!tmp) + storage = calloc (1, size); + if (!storage) return EAI_MEMORY; switch (he->h_addrtype) @@ -131,18 +237,23 @@ getaddrinfo (const char *restrict nodename, #if HAVE_IPV6 case PF_INET6: { - struct sockaddr_in6 *sinp = (void *) tmp + sizeof (*tmp); + struct v6_pair *p = storage; + struct sockaddr_in6 *sinp = &p->sockaddr_in6; + tmp = &p->addrinfo; - if (se) - sinp->sin6_port = se->s_port; + if (port) + sinp->sin6_port = port; - if (he->h_length != sizeof (sinp->sin6_addr)) - return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */ + 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], he->h_length); + memcpy (&sinp->sin6_addr, he->h_addr_list[0], sizeof sinp->sin6_addr); - tmp->ai_addr = (struct sockaddr *) sinp; - tmp->ai_addrlen = sinlen; + tmp->ai_addr = (struct sockaddr *) sinp; + tmp->ai_addrlen = sizeof *sinp; } break; #endif @@ -150,28 +261,68 @@ getaddrinfo (const char *restrict nodename, #if HAVE_IPV4 case PF_INET: { - struct sockaddr_in *sinp = (void *) tmp + sizeof (*tmp); + struct v4_pair *p = storage; + struct sockaddr_in *sinp = &p->sockaddr_in; + tmp = &p->addrinfo; - if (se) - sinp->sin_port = se->s_port; + if (port) + sinp->sin_port = port; - if (he->h_length != sizeof (sinp->sin_addr)) - return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */ + 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], he->h_length); + memcpy (&sinp->sin_addr, he->h_addr_list[0], sizeof sinp->sin_addr); - tmp->ai_addr = (struct sockaddr *) sinp; - tmp->ai_addrlen = sinlen; + tmp->ai_addr = (struct sockaddr *) sinp; + tmp->ai_addrlen = sizeof *sinp; } break; #endif default: - free (tmp); + 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. */ @@ -180,16 +331,113 @@ getaddrinfo (const char *restrict nodename, return 0; } -/* Free `addrinfo' structure AI including associated storage. */ +/* 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; +}