]> git.eshelyaron.com Git - emacs.git/commitdiff
Implement getaddrinfo fallback for MS-Windows
authorEli Zaretskii <eliz@gnu.org>
Sat, 5 Mar 2016 10:12:58 +0000 (12:12 +0200)
committerEli Zaretskii <eliz@gnu.org>
Sat, 5 Mar 2016 10:12:58 +0000 (12:12 +0200)
See http://lists.gnu.org/archive/html/emacs-devel/2016-02/msg01602.html
for more details.

* nt/mingw-cfg.site (ac_cv_func_getaddrinfo)
(ac_cv_func_gai_strerror): Set to "yes", as the configure script's
test program is not smart enough to auto-detect these.
* nt/inc/sys/socket.h (getaddrinfo, freeaddrinfo): Redirect to
sys_getaddrinfo and sys_freeaddrinfo.  Provide prototypes for
sys_getaddrinfo and sys_freeaddrinfo.

* src/w32.c (init_winsock): Try loading getaddrinfo and
freeaddrinfo from ws2_32.dll.
(sys_getaddrinfo, sys_freeaddrinfo): New functions.

* lib-src/pop.c [WINDOWSNT]: Include winsock2.h, not winsock.h,
and also ws2tcpip.h.
(getaddrinfo, freeaddrinfo) [WINDOWSNT]: Redirect to
sys_getaddrinfo and sys_freeaddrinfo, respectively.
(load_ws2, sys_getaddrinfo, sys_freeaddrinfo) [WINDOWSNT]: New
functions.

lib-src/pop.c
nt/inc/sys/socket.h
nt/mingw-cfg.site
src/w32.c

index 812bd4ca24c738bd945470549f241dd03bc3f296..21d721546b7e49e3886b039109d5d69924bf39b0 100644 (file)
@@ -28,7 +28,17 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <sys/types.h>
 #ifdef WINDOWSNT
 #include "ntlib.h"
-#include <winsock.h>
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501    /* for getaddrinfo stuff */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#undef getaddrinfo
+#define getaddrinfo  sys_getaddrinfo
+#undef freeaddrinfo
+#define freeaddrinfo sys_freeaddrinfo
+int sys_getaddrinfo (const char * node, const char * service,
+                    const struct addrinfo * hints, struct addrinfo ** res);
+void sys_freeaddrinfo (struct addrinfo * ai);
 #undef SOCKET_ERROR
 #define RECV(s,buf,len,flags) recv (s,buf,len,flags)
 #define SEND(s,buf,len,flags) send (s,buf,len,flags)
@@ -1581,4 +1591,143 @@ find_crlf (char *in_string, int len)
   return (0);
 }
 
+#ifdef WINDOWSNT
+/* The following 2 functions are only available since XP, so we load
+   them dynamically and provide fallbacks.  */
+
+int (WINAPI *pfn_getaddrinfo) (const char *, const char *,
+                              const struct addrinfo *, struct addrinfo **);
+void (WINAPI *pfn_freeaddrinfo) (struct addrinfo *);
+
+static int
+load_ws2 (void)
+{
+  static int ws2_loaded = 0;
+
+  if (!ws2_loaded)
+    {
+      HANDLE ws2_lib = LoadLibrary ("Ws2_32.dll");
+
+      if (ws2_lib != NULL)
+       {
+         ws2_loaded = 1;
+         pfn_getaddrinfo = (void *) GetProcAddress (ws2_lib, "getaddrinfo");
+         pfn_freeaddrinfo = (void *) GetProcAddress (ws2_lib, "freeaddrinfo");
+         /* Paranoia: these two functions should go together, so if
+            one is absent, we cannot use the other.  */
+         if (pfn_getaddrinfo == NULL)
+           pfn_freeaddrinfo = NULL;
+         else if (pfn_freeaddrinfo == NULL)
+           pfn_getaddrinfo = NULL;
+       }
+    }
+  if (!ws2_loaded)
+    {
+      errno = ENETDOWN;
+      return -1;
+    }
+  return 0;
+}
+
+
+int
+sys_getaddrinfo (const char *node, const char *service,
+                const struct addrinfo *hints, struct addrinfo **res)
+{
+  int rc;
+
+  if (load_ws2 () != 0)
+    {
+      errno = ENETDOWN;
+      return WSANO_RECOVERY;
+    }
+
+  if (pfn_getaddrinfo)
+    rc = pfn_getaddrinfo (node, service, hints, res);
+  else
+    {
+      int port = 0;
+      struct hostent *host_info;
+      struct gai_storage {
+       struct addrinfo addrinfo;
+       struct sockaddr_in sockaddr_in;
+      } *gai_storage;
+
+      /* We don't support any flags besides AI_CANONNAME.  */
+      if (hints && (hints->ai_flags & ~(AI_CANONNAME)) != 0)
+       return WSAEINVAL;
+      /* NODE cannot be NULL, since pop.c has fallbacks for that.  */
+      if (!node)
+       return WSAHOST_NOT_FOUND;
+
+      if (service)
+       {
+         const char *protocol =
+           (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
+         struct servent *srv = getservbyname (service, protocol);
+
+         if (srv)
+           port = srv->s_port;
+         else
+           return WSAHOST_NOT_FOUND;
+       }
+
+      gai_storage = calloc (1, sizeof *gai_storage);
+      gai_storage->sockaddr_in.sin_port = port;
+      host_info = gethostbyname (node);
+      if (host_info)
+       {
+         memcpy (&gai_storage->sockaddr_in.sin_addr,
+                 host_info->h_addr, host_info->h_length);
+         gai_storage->sockaddr_in.sin_family = host_info->h_addrtype;
+       }
+      else
+       {
+         free (gai_storage);
+         return WSAHOST_NOT_FOUND;
+       }
+
+      gai_storage->addrinfo.ai_addr =
+       (struct sockaddr *)&gai_storage->sockaddr_in;
+      gai_storage->addrinfo.ai_addrlen = sizeof (gai_storage->sockaddr_in);
+      if (hints && (hints->ai_flags & AI_CANONNAME) != 0)
+       {
+         gai_storage->addrinfo.ai_canonname = strdup (host_info->h_name);
+         if (!gai_storage->addrinfo.ai_canonname)
+           {
+             free (gai_storage);
+             return WSA_NOT_ENOUGH_MEMORY;
+           }
+       }
+      gai_storage->addrinfo.ai_protocol = (hints) ? hints->ai_protocol : 0;
+      gai_storage->addrinfo.ai_socktype = (hints) ? hints->ai_socktype : 0;
+      gai_storage->addrinfo.ai_family = gai_storage->sockaddr_in.sin_family;
+      gai_storage->addrinfo.ai_next = NULL;
+
+      *res = &gai_storage->addrinfo;
+      rc = 0;
+    }
+
+  return rc;
+}
+
+void
+sys_freeaddrinfo (struct addrinfo *ai)
+{
+  if (load_ws2 () != 0)
+    {
+      errno = ENETDOWN;
+      return;
+    }
+
+  if (pfn_freeaddrinfo)
+    pfn_freeaddrinfo (ai);
+  else
+    {
+      if (ai->ai_canonname)
+       free (ai->ai_canonname);
+      free (ai);
+    }
+}
+#endif /* WINDOWSNT */
 #endif /* MAIL_USE_POP */
index d7ed92d61581d0a678ba687cc9f8a30e38fbea5f..2c72a78b0f464513f8ea7ef39d3629f6250cb55d 100644 (file)
@@ -98,6 +98,8 @@ typedef unsigned short uint16_t;
 #define accept         sys_accept
 #define recvfrom       sys_recvfrom
 #define sendto         sys_sendto
+#define getaddrinfo    sys_getaddrinfo
+#define freeaddrinfo   sys_freeaddrinfo
 
 int sys_socket(int af, int type, int protocol);
 int sys_bind (int s, const struct sockaddr *addr, int namelen);
@@ -118,6 +120,9 @@ int sys_recvfrom (int s, char *buf, int len, int flags,
                  struct sockaddr *from, int * fromlen);
 int sys_sendto (int s, const char * buf, int len, int flags,
                const struct sockaddr *to, int tolen);
+int sys_getaddrinfo (const char * node, const char * service,
+                    const struct addrinfo * hints, struct addrinfo ** res);
+void sys_freeaddrinfo (struct addrinfo * ai);
 
 /* In addition to wrappers for the winsock functions, we also provide
    an fcntl function, for setting sockets to non-blocking mode.  */
index 05034fedd4889d8a9c93ce28b1b84c76b2fb112a..ff9df60c275f289cb60ae7a483591b8c9a9fa0a4 100644 (file)
@@ -68,6 +68,10 @@ ac_cv_func_getsockname=yes
 ac_cv_func_getpeername=yes
 # Implemented as sys_socket in w32.c
 ac_cv_func_socket=yes
+# Implemented as sys_getaddrinfo in w32.c
+ac_cv_func_getaddrinfo=yes
+# Implemented as an inline function in ws2tcpip.h
+ac_cv_func_gai_strerror=yes
 # Implemented in w32.c
 ac_cv_func_mkostemp=yes
 ac_cv_func_readlink=yes
index 998f696bdf8fe64d849a879892033bf9dded8b15..ccf7cc335cea9f99514ae93b472fad13c8147682 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -7202,6 +7202,10 @@ int (PASCAL *pfn_recvfrom) (SOCKET s, char * buf, int len, int flags,
 int (PASCAL *pfn_sendto) (SOCKET s, const char * buf, int len, int flags,
                          const struct sockaddr * to, int tolen);
 
+int (PASCAL *pfn_getaddrinfo) (const char *, const char *,
+                              const struct addrinfo *, struct addrinfo **);
+void (PASCAL *pfn_freeaddrinfo) (struct addrinfo *);
+
 /* SetHandleInformation is only needed to make sockets non-inheritable. */
 BOOL (WINAPI *pfn_SetHandleInformation) (HANDLE object, DWORD mask, DWORD flags);
 #ifndef HANDLE_FLAG_INHERIT
@@ -7284,6 +7288,16 @@ init_winsock (int load_now)
       LOAD_PROC (sendto);
 #undef LOAD_PROC
 
+      /* Try loading functions not available before XP.  */
+      pfn_getaddrinfo = (void *) GetProcAddress (winsock_lib, "getaddrinfo");
+      pfn_freeaddrinfo = (void *) GetProcAddress (winsock_lib, "freeaddrinfo");
+      /* Paranoia: these two functions should go together, so if one
+        is absent, we cannot use the other.  */
+      if (pfn_getaddrinfo == NULL)
+       pfn_freeaddrinfo = NULL;
+      else if (pfn_freeaddrinfo == NULL)
+       pfn_getaddrinfo = NULL;
+
       /* specify version 1.1 of winsock */
       if (pfn_WSAStartup (0x101, &winsockData) == 0)
         {
@@ -7733,6 +7747,117 @@ sys_getpeername (int s, struct sockaddr *addr, int * namelen)
   return SOCKET_ERROR;
 }
 
+int
+sys_getaddrinfo (const char *node, const char *service,
+                const struct addrinfo *hints, struct addrinfo **res)
+{
+  int rc;
+
+  if (winsock_lib == NULL)
+    {
+      errno = ENETDOWN;
+      return SOCKET_ERROR;
+    }
+
+  check_errno ();
+  if (pfn_getaddrinfo)
+    rc = pfn_getaddrinfo (node, service, hints, res);
+  else
+    {
+      int port = 0;
+      struct hostent *host_info;
+      struct gai_storage {
+       struct addrinfo addrinfo;
+       struct sockaddr_in sockaddr_in;
+      } *gai_storage;
+
+      /* We don't (yet) support any flags, as Emacs doesn't need that.  */
+      if (hints && hints->ai_flags != 0)
+       return WSAEINVAL;
+      /* NODE cannot be NULL, since process.c has fallbacks for that.  */
+      if (!node)
+       return WSAHOST_NOT_FOUND;
+
+      if (service)
+       {
+         const char *protocol =
+           (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
+         struct servent *srv = sys_getservbyname (service, protocol);
+
+         if (srv)
+           port = srv->s_port;
+         else if (*service >= '0' && *service <= '9')
+           {
+             char *endp;
+
+             port = strtoul (service, &endp, 10);
+             if (*endp || port > 65536)
+               return WSAHOST_NOT_FOUND;
+             port = sys_htons ((unsigned short) port);
+           }
+         else
+           return WSAHOST_NOT_FOUND;
+       }
+
+      gai_storage = xzalloc (sizeof *gai_storage);
+      gai_storage->sockaddr_in.sin_port = port;
+      host_info = sys_gethostbyname (node);
+      if (host_info)
+       {
+         memcpy (&gai_storage->sockaddr_in.sin_addr,
+                 host_info->h_addr, host_info->h_length);
+         gai_storage->sockaddr_in.sin_family = host_info->h_addrtype;
+       }
+      else
+       {
+         /* Attempt to interpret host as numeric inet address.  */
+         unsigned long numeric_addr = sys_inet_addr (node);
+
+         if (numeric_addr == -1)
+           {
+             free (gai_storage);
+             return WSAHOST_NOT_FOUND;
+           }
+
+         memcpy (&gai_storage->sockaddr_in.sin_addr, &numeric_addr,
+                 sizeof (gai_storage->sockaddr_in.sin_addr));
+         gai_storage->sockaddr_in.sin_family = (hints) ? hints->ai_family : 0;
+       }
+
+      gai_storage->addrinfo.ai_addr =
+       (struct sockaddr *)&gai_storage->sockaddr_in;
+      gai_storage->addrinfo.ai_addrlen = sizeof (gai_storage->sockaddr_in);
+      gai_storage->addrinfo.ai_protocol = (hints) ? hints->ai_protocol : 0;
+      gai_storage->addrinfo.ai_socktype = (hints) ? hints->ai_socktype : 0;
+      gai_storage->addrinfo.ai_family = gai_storage->sockaddr_in.sin_family;
+      gai_storage->addrinfo.ai_next = NULL;
+
+      *res = &gai_storage->addrinfo;
+      rc = 0;
+    }
+
+  return rc;
+}
+
+void
+sys_freeaddrinfo (struct addrinfo *ai)
+{
+  if (winsock_lib == NULL)
+    {
+      errno = ENETDOWN;
+      return;
+    }
+
+  check_errno ();
+  if (pfn_freeaddrinfo)
+    pfn_freeaddrinfo (ai);
+  else
+    {
+      eassert (ai->ai_next == NULL);
+      xfree (ai);
+    }
+}
+
 int
 sys_shutdown (int s, int how)
 {