changeset 6875:41254433d288

Native win32 support for getaddrinfo. Add getnameinfo function.
author Simon Josefsson <simon@josefsson.org>
date Wed, 28 Jun 2006 11:08:34 +0000
parents 8a5ae1656eca
children edba9bd2b197
files ChangeLog lib/ChangeLog lib/getaddrinfo.c lib/getaddrinfo.h m4/ChangeLog m4/getaddrinfo.m4 modules/getaddrinfo tests/test-getaddrinfo.c
diffstat 8 files changed, 267 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2006-06-28  Simon Josefsson  <jas@extundo.com>
+
+	* tests/test-getaddrinfo.c: Test getnameinfo too.  Call WSAStartup
+	on _WIN32.
+
+	* modules/getaddrinfo (Depends-on): Add inet_ntop, needed by
+	getnameinfo.
+
 2006-06-27  Simon Josefsson  <jas@extundo.com>
 
 	* modules/sys_select: New file, suggested by Paul Eggert and
--- a/lib/ChangeLog
+++ b/lib/ChangeLog
@@ -1,3 +1,19 @@
+2006-06-28  Simon Josefsson  <jas@extundo.com>
+
+	* getaddrinfo.c: Try to load ws2_32.dll on Windows, to find the
+	functions there.  It will succeed on Windows XP, but on Windows
+	2000 and (presumably) earlier, it will fail, and use the internal
+	re-implementation.
+	(use_win32_p): New function.
+	(getaddrinfo): Use strtoul on servname, to support numeric ports.
+	Support AI_NUMERICSERV to disable getservbyname.
+	(getnameinfo): New function, only supports
+	NI_NUMERICHOST|NI_NUMERICSERV for now.
+
+	* getaddrinfo.h: Test and check for AI_* flags separately, MinGW
+	only have some of them.  Add AI_NUMERICSERV.  Add prototype for
+	getnameinfo.
+
 2006-06-27  Bruno Haible  <bruno@clisp.org>
 
 	* stdlib_.h (intmax_t, uintmax_t): Undefine before typedef.
--- 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,51 @@
 
 #include "strdup.h"
 
+#ifdef _WIN32
+typedef void WSAAPI (*freeaddrinfo_func) (struct addrinfo*);
+typedef int WSAAPI (*getaddrinfo_func) (const char*, const char*,
+					const struct addrinfo*,
+					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;
+
+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 +111,7 @@
 	     struct addrinfo **restrict res)
 {
   struct addrinfo *tmp;
-  struct servent *se = NULL;
+  int port = 0;
   struct hostent *he;
   void *storage;
   size_t size;
@@ -83,6 +128,11 @@
   };
 #endif
 
+#ifdef _WIN32
+  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 +151,24 @@
 
   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;
+	  port = strtoul (servname, &c, 10);
+	  if (*c)
+	    return EAI_NONAME;
+	  port = htons (port);
+	}
+      else
+	port = se->s_port;
     }
 
   /* FIXME: Use gethostbyname_r if available. */
@@ -147,8 +207,8 @@
 	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 +231,8 @@
 	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))
 	  {
@@ -225,6 +285,11 @@
 void
 freeaddrinfo (struct addrinfo *ai)
 {
+#ifdef _WIN32
+  if (use_win32_p ())
+    return freeaddrinfo_ptr (ai);
+#endif
+
   while (ai)
     {
       struct addrinfo *cur;
@@ -236,3 +301,89 @@
       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
+  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 void *)
+			  &(((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 void *)
+			  &(((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;
+}
--- a/lib/getaddrinfo.h
+++ b/lib/getaddrinfo.h
@@ -48,13 +48,26 @@
 
 /* Possible values for `ai_flags' field in `addrinfo' structure.  */
 # ifndef AI_PASSIVE
-#  define AI_PASSIVE	0x0001	/* Socket address is intended for `bind'.  */
-#  define AI_CANONNAME	0x0002	/* Request for canonical name.  */
+#  define AI_PASSIVE	 0x0001	/* Socket address is intended for `bind'.  */
+# endif
+# ifndef AI_CANONNAME
+#  define AI_CANONNAME	 0x0002	/* Request for canonical name.  */
+# endif
+# ifndef AI_NUMERICHOST
 #  define AI_NUMERICHOST 0x0004	/* Don't use name resolution.  */
-#  define AI_V4MAPPED	0x0008	/* IPv4 mapped addresses are acceptable.  */
-#  define AI_ALL	0x0010	/* Return IPv4 mapped and IPv6 addresses.  */
-#  define AI_ADDRCONFIG	0x0020	/* Use configuration of this host to choose
+# endif
+# ifndef AI_V4MAPPED
+#  define AI_V4MAPPED	 0x0008	/* IPv4 mapped addresses are acceptable.  */
+# endif
+# ifndef AI_ALL
+#  define AI_ALL	 0x0010	/* Return IPv4 mapped and IPv6 addresses.  */
+# endif
+# ifndef AI_ADDRCONFIG
+#  define AI_ADDRCONFIG	 0x0020	/* Use configuration of this host to choose
 				   returned address type..  */
+#endif
+#ifndef AI_NUMERICSERV
+#  define AI_NUMERICSERV 0x0400	/* Don't use name resolution.  */
 # endif
 
 /* Error values for `getaddrinfo' function.  */
@@ -68,16 +81,17 @@
 #  define EAI_SOCKTYPE	  -7	/* `ai_socktype' not supported.  */
 #  define EAI_SERVICE	  -8	/* SERVICE not supported for `ai_socktype'.  */
 #  define EAI_MEMORY	  -10	/* Memory allocation failure.  */
+#endif
+#ifndef EAI_OVERFLOW
+/* Not defined on mingw32. */
 #  define EAI_OVERFLOW	  -12	/* Argument buffer overflow.  */
 #endif
 # ifndef EAI_ADDRFAMILY
-/* Not defined on mingw32.  XXX May be incorrect? Perhaps it is never
-   returned? */
+/* Not defined on mingw32. */
 #  define EAI_ADDRFAMILY  -9	/* Address family for NAME not supported.  */
 # endif
 # ifndef EAI_SYSTEM
-/* Not defined on mingw32.  XXX May be incorrect? Perhaps it is never
-   returned? */
+/* Not defined on mingw32. */
 #  define EAI_SYSTEM	  -11	/* System error returned in `errno'.  */
 # endif
 
@@ -117,4 +131,15 @@
 extern const char *gai_strerror (int ecode);
 # endif
 
+# if !HAVE_DECL_GETNAMEINFO
+/* Convert socket address to printable node and service names.
+   For more details, see the POSIX:2001 specification
+   <http://www.opengroup.org/susv3xsh/gai_strerror.html>.  */
+extern int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen,
+		       char *restrict node, socklen_t nodelen,
+		       char *restrict service, socklen_t servicelen,
+		       int flags);
+
+# endif
+
 #endif /* GETADDRINFO_H */
--- a/m4/ChangeLog
+++ b/m4/ChangeLog
@@ -1,3 +1,7 @@
+2006-06-28  Simon Josefsson  <jas@extundo.com>
+
+	* getaddrinfo.m4: Look for getnameinfo prototypes too.
+
 2006-06-27  Simon Josefsson  <jas@extundo.com>
 
 	* sys_select_h.m4: New file, suggested by Paul Eggert and Martin
--- a/m4/getaddrinfo.m4
+++ b/m4/getaddrinfo.m4
@@ -1,4 +1,4 @@
-# getaddrinfo.m4 serial 9
+# getaddrinfo.m4 serial 10
 dnl Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -6,7 +6,7 @@
 
 AC_DEFUN([gl_GETADDRINFO],
 [
-  AC_MSG_NOTICE([checking how to do getaddrinfo])
+  AC_MSG_NOTICE([checking how to do getaddrinfo, freeaddrinfo and getnameinfo])
 
   AC_SEARCH_LIBS(getaddrinfo, [nsl socket])
   AC_CHECK_FUNCS(getaddrinfo,, [
@@ -58,7 +58,7 @@
   AC_REQUIRE([AC_C_INLINE])
   AC_REQUIRE([AC_GNU_SOURCE])
   AC_CHECK_HEADERS_ONCE(netinet/in.h netdb.h)
-  AC_CHECK_DECLS([getaddrinfo, freeaddrinfo, gai_strerror],,,[
+  AC_CHECK_DECLS([getaddrinfo, freeaddrinfo, gai_strerror, getnameinfo],,,[
   /* sys/types.h is not needed according to POSIX, but the
      sys/socket.h in i386-unknown-freebsd4.10 and
      powerpc-apple-darwin5.5 required it. */
--- a/modules/getaddrinfo
+++ b/modules/getaddrinfo
@@ -15,6 +15,7 @@
 stdbool
 strdup
 sys_socket
+inet_ntop
 
 configure.ac:
 gl_GETADDRINFO
--- a/tests/test-getaddrinfo.c
+++ b/tests/test-getaddrinfo.c
@@ -15,9 +15,9 @@
 
   memset (&hints, 0, sizeof (hints));
   hints.ai_flags = AI_CANONNAME;
-  hints.ai_family = AF_INET;
+  hints.ai_family = AF_UNSPEC;
   hints.ai_socktype = SOCK_STREAM;
-  res = getaddrinfo (host, 0, 0, &ai0);
+  res = getaddrinfo (host, service, 0, &ai0);
 
   printf ("res %d: %s\n", res, gai_strerror (res));
 
@@ -38,6 +38,23 @@
 			 buf, sizeof (buf) - 1));
       if (ai->ai_canonname)
 	printf ("\tFound %s...\n", ai->ai_canonname);
+
+      {
+	char ipbuf[BUFSIZ];
+	char portbuf[BUFSIZ];
+
+	res = getnameinfo (ai->ai_addr, ai->ai_addrlen,
+			   ipbuf, sizeof (ipbuf) - 1,
+			   portbuf, sizeof (portbuf) - 1,
+			   NI_NUMERICHOST|NI_NUMERICSERV);
+	printf ("\t\tgetnameinfo %d: %s\n", res, gai_strerror (res));
+	if (res == 0)
+	  {
+	    printf ("\t\tip %s\n", ipbuf);
+	    printf ("\t\tport %s\n", portbuf);
+	  }
+      }
+
     }
 
   freeaddrinfo (ai0);
@@ -48,14 +65,33 @@
 #define HOST1 "www.gnu.org"
 #define SERV1 "http"
 #define HOST2 "www.ibm.com"
-#define SERV2 "http"
-#define HOST3 "ibm.org"
+#define SERV2 "https"
+#define HOST3 "microsoft.com"
 #define SERV3 "http"
 #define HOST4 "google.org"
-#define SERV4 "http"
+#define SERV4 "ldap"
 
 int main (void)
 {
+#if _WIN32
+  {
+    WORD requested;
+    WSADATA data;
+    int err;
+
+    requested = MAKEWORD (1, 1);
+    err = WSAStartup (requested, &data);
+    if (err != 0)
+      return 1;
+
+    if (data.wVersion < requested)
+      {
+	WSACleanup ();
+	return 2;
+      }
+  }
+#endif
+
   return simple (HOST1, SERV1)
     + simple (HOST2, SERV2)
     + simple (HOST3, SERV3)