diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2006-07-04 05:39:07 +0000 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2006-07-04 05:39:07 +0000 |
commit | 6a9740567292a983b05533901dc7a687ec2fb68c (patch) | |
tree | 942771c90c0eff8fc776a1b631e9032e60641ea7 /lib/getaddrinfo.c | |
parent | 8e19f6282b0621f63b47716bd2eadfb9d466c758 (diff) | |
download | coreutils-6a9740567292a983b05533901dc7a687ec2fb68c.tar.xz |
Sync from gnulib.
Diffstat (limited to 'lib/getaddrinfo.c')
-rw-r--r-- | lib/getaddrinfo.c | 175 |
1 files changed, 166 insertions, 9 deletions
diff --git a/lib/getaddrinfo.c b/lib/getaddrinfo.c index 7ea696778..2cba44387 100644 --- a/lib/getaddrinfo.c +++ b/lib/getaddrinfo.c @@ -1,5 +1,5 @@ /* Get address information (partial implementation). - Copyright (C) 1997, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1997, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by Simon Josefsson <simon@josefsson.org>. This program is free software; you can redistribute it and/or modify @@ -40,6 +40,56 @@ #include "strdup.h" +#if defined _WIN32 || defined __WIN32__ +# define WIN32_NATIVE +#endif + +#ifdef WIN32_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 = GetProcAddress (h, "getaddrinfo"); + freeaddrinfo_ptr = GetProcAddress (h, "freeaddrinfo"); + getnameinfo_ptr = 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; + } + + return 1; +} +#endif + static inline bool validate_family (int family) { @@ -66,7 +116,7 @@ getaddrinfo (const char *restrict nodename, struct addrinfo **restrict res) { struct addrinfo *tmp; - struct servent *se = NULL; + int port = 0; struct hostent *he; void *storage; size_t size; @@ -83,6 +133,11 @@ getaddrinfo (const char *restrict nodename, }; #endif +#ifdef WIN32_NATIVE + if (use_win32_p ()) + return getaddrinfo_ptr (nodename, servname, hints, res); +#endif + if (hints && (hints->ai_flags & ~AI_CANONNAME)) /* FIXME: Support more flags. */ return EAI_BADFLAGS; @@ -101,14 +156,26 @@ getaddrinfo (const char *restrict nodename, if (servname) { + struct servent *se = NULL; const char *proto = (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp"; - /* FIXME: Use getservbyname_r if available. */ - se = getservbyname (servname, proto); + if (!(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. */ @@ -147,8 +214,8 @@ getaddrinfo (const char *restrict nodename, 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)) { @@ -171,8 +238,8 @@ getaddrinfo (const char *restrict nodename, 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)) { @@ -212,6 +279,7 @@ getaddrinfo (const char *restrict nodename, 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; /* FIXME: If more than one address, create linked list of addrinfo's. */ @@ -224,6 +292,11 @@ getaddrinfo (const char *restrict nodename, void freeaddrinfo (struct addrinfo *ai) { +#ifdef WIN32_NATIVE + if (use_win32_p ()) + return freeaddrinfo_ptr (ai); +#endif + while (ai) { struct addrinfo *cur; @@ -235,3 +308,87 @@ freeaddrinfo (struct addrinfo *ai) 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) +{ +#if WIN32_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 + if (snprintf (service, servicelen, "%d", + ntohs (((const struct sockaddr_in *) sa)->sin_port)) + + 1 > servicelen) + return EAI_OVERFLOW; + break; + } + + return 0; +} |