From bc96f6e827ba079006ae87914395942fc79f3f26 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 5 Mar 2016 12:12:58 +0200 Subject: [PATCH] Implement getaddrinfo fallback for MS-Windows 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 | 151 +++++++++++++++++++++++++++++++++++++++++++- nt/inc/sys/socket.h | 5 ++ nt/mingw-cfg.site | 4 ++ src/w32.c | 125 ++++++++++++++++++++++++++++++++++++ 4 files changed, 284 insertions(+), 1 deletion(-) diff --git a/lib-src/pop.c b/lib-src/pop.c index 812bd4ca24..21d721546b 100644 --- a/lib-src/pop.c +++ b/lib-src/pop.c @@ -28,7 +28,17 @@ along with GNU Emacs. If not, see . */ #include #ifdef WINDOWSNT #include "ntlib.h" -#include +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 /* for getaddrinfo stuff */ +#include +#include +#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 */ diff --git a/nt/inc/sys/socket.h b/nt/inc/sys/socket.h index d7ed92d615..2c72a78b0f 100644 --- a/nt/inc/sys/socket.h +++ b/nt/inc/sys/socket.h @@ -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. */ diff --git a/nt/mingw-cfg.site b/nt/mingw-cfg.site index 05034fedd4..ff9df60c27 100644 --- a/nt/mingw-cfg.site +++ b/nt/mingw-cfg.site @@ -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 diff --git a/src/w32.c b/src/w32.c index 998f696bdf..ccf7cc335c 100644 --- 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) { -- 2.39.2