diff options
Diffstat (limited to 'lib-src/pop.c')
-rw-r--r-- | lib-src/pop.c | 223 |
1 files changed, 169 insertions, 54 deletions
diff --git a/lib-src/pop.c b/lib-src/pop.c index 6e3b6a9a773..99424c584c2 100644 --- a/lib-src/pop.c +++ b/lib-src/pop.c @@ -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) @@ -53,10 +63,12 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ extern struct servent *hes_getservbyname (/* char *, char * */); #endif +#include <alloca.h> #include <pwd.h> #include <netdb.h> #include <errno.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> @@ -80,6 +92,7 @@ extern struct servent *hes_getservbyname (/* char *, char * */); # endif #endif /* KERBEROS */ +#include <c-ctype.h> #include <min-max.h> #ifdef KERBEROS @@ -92,12 +105,6 @@ extern char *krb_realmofhost (/* char * */); #endif /* ! KERBEROS5 */ #endif /* KERBEROS */ -#ifndef WINDOWSNT -#ifndef HAVE_H_ERRNO -extern int h_errno; -#endif -#endif - static int socket_connection (char *, int); static int pop_getline (popserver, char **); static int sendline (popserver, const char *); @@ -714,7 +721,8 @@ pop_multi_next (popserver server, char **line) return (-1); } - if ((ret = pop_getline (server, &fromserver)) < 0) + ret = pop_getline (server, &fromserver); + if (ret < 0) { return (-1); } @@ -962,13 +970,9 @@ static int have_winsock = 0; static int socket_connection (char *host, int flags) { -#ifdef HAVE_GETADDRINFO struct addrinfo *res, *it; struct addrinfo hints; int ret; -#else /* !HAVE_GETADDRINFO */ - struct hostent *hostent; -#endif struct servent *servent; struct sockaddr_in addr; char found_port = 0; @@ -1055,7 +1059,6 @@ socket_connection (char *host, int flags) } -#ifdef HAVE_GETADDRINFO memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; @@ -1087,34 +1090,6 @@ socket_connection (char *host, int flags) } freeaddrinfo (res); -#else /* !HAVE_GETADDRINFO */ - do - { - hostent = gethostbyname (host); - try_count++; - if ((! hostent) && ((h_errno != TRY_AGAIN) || (try_count == 5))) - { - strcpy (pop_error, "Could not determine POP server's address"); - return (-1); - } - } while (! hostent); - - while (*hostent->h_addr_list) - { - memcpy (&addr.sin_addr, *hostent->h_addr_list, hostent->h_length); - if (! connect (sock, (struct sockaddr *) &addr, sizeof (addr))) - break; - hostent->h_addr_list++; - } - connect_ok = *hostent->h_addr_list != NULL; - if (! connect_ok) - { - realhost = alloca (strlen (hostent->h_name) + 1); - strcpy (realhost, hostent->h_name); - } - -#endif /* !HAVE_GETADDRINFO */ - #define CONNECT_ERROR "Could not connect to POP server: " if (! connect_ok) @@ -1131,7 +1106,8 @@ socket_connection (char *host, int flags) if (! (flags & POP_NO_KERBEROS)) { #ifdef KERBEROS5 - if ((rem = krb5_init_context (&kcontext))) + rem = krb5_init_context (&kcontext); + if (rem) { krb5error: if (auth_context) @@ -1144,29 +1120,29 @@ socket_connection (char *host, int flags) return (-1); } - if ((rem = krb5_auth_con_init (kcontext, &auth_context))) + rem = krb5_auth_con_init (kcontext, &auth_context); + if (rem) goto krb5error; - if (rem = krb5_cc_default (kcontext, &ccdef)) + rem = krb5_cc_default (kcontext, &ccdef); + if (rem) goto krb5error; - if (rem = krb5_cc_get_principal (kcontext, ccdef, &client)) + rem = krb5_cc_get_principal (kcontext, ccdef, &client); + if (rem) goto krb5error; for (cp = realhost; *cp; cp++) - { - if (isupper (*cp)) - { - *cp = tolower (*cp); - } - } + *cp = c_tolower (*cp); - if (rem = krb5_sname_to_principal (kcontext, realhost, - POP_SERVICE, FALSE, &server)) + rem = krb5_sname_to_principal (kcontext, realhost, + POP_SERVICE, FALSE, &server); + if (rem) goto krb5error; rem = krb5_sendauth (kcontext, &auth_context, - (krb5_pointer) &sock, "KPOPV1.0", client, server, + (krb5_pointer) &sock, (char *) "KPOPV1.0", + client, server, AP_OPTS_MUTUAL_REQUIRED, 0, /* no checksum */ 0, /* no creds, use ccache instead */ @@ -1581,4 +1557,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 */ |