X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/e5eb3a1ca14cf38028d076b06701792af954c90b..7440fd4419acfc9c784f142fb9dee3e64c9a18c2:/winnet.c diff --git a/winnet.c b/winnet.c index 1a80a80c..25cfc191 100644 --- a/winnet.c +++ b/winnet.c @@ -38,14 +38,6 @@ */ /* #define IPV6 1 */ -#ifdef IPV6 -#include -#include -#include -#else -#include -#endif -#include #include #include #include @@ -55,21 +47,32 @@ #include "network.h" #include "tree234.h" +#ifdef IPV6 +#include +#include +#endif + +#define ipv4_is_loopback(addr) \ + ((p_ntohl(addr.s_addr) & 0xFF000000L) == 0x7F000000L) + struct Socket_tag { - struct socket_function_table *fn; + const struct socket_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ char *error; SOCKET s; Plug plug; void *private_ptr; bufchain output_data; + int connected; int writable; int frozen; /* this causes readability notifications to be ignored */ int frozen_readable; /* this means we missed at least one readability * notification while we were frozen */ + int localhost_only; /* for listening sockets */ char oobdata[1]; int sending_oob; int oobinline; + int pending_error; /* in case send() returns error */ }; /* @@ -84,12 +87,18 @@ typedef struct Socket_tag *Actual_Socket; struct SockAddr_tag { char *error; - /* address family this belongs to, AF_INET for IPv4, AF_INET6 for IPv6. */ + /* + * Which address family this address belongs to. AF_INET for + * IPv4; AF_INET6 for IPv6; AF_UNSPEC indicates that name + * resolution has not been done and a simple host name is held + * in this SockAddr structure. + */ int family; unsigned long address; /* Address IPv4 style. */ #ifdef IPV6 struct addrinfo *ai; /* Address IPv6 style. */ #endif + char hostname[512]; /* Store an unresolved host name. */ }; static tree234 *sktree; @@ -116,11 +125,120 @@ static int cmpforsearch(void *av, void *bv) return 0; } +#define NOTHING +#define DECL_WINSOCK_FUNCTION(linkage, rettype, name, params) \ + typedef rettype (WINAPI *t_##name) params; \ + linkage t_##name p_##name +#define GET_WINSOCK_FUNCTION(name) \ + p_##name = (t_##name) GetProcAddress(winsock_module, #name) + +DECL_WINSOCK_FUNCTION(NOTHING, int, WSAAsyncSelect, + (SOCKET, HWND, u_int, long)); +DECL_WINSOCK_FUNCTION(NOTHING, int, WSAEventSelect, (SOCKET, WSAEVENT, long)); +DECL_WINSOCK_FUNCTION(NOTHING, int, select, + (int, fd_set FAR *, fd_set FAR *, + fd_set FAR *, const struct timeval FAR *)); +DECL_WINSOCK_FUNCTION(NOTHING, int, WSAGetLastError, (void)); +DECL_WINSOCK_FUNCTION(NOTHING, int, WSAEnumNetworkEvents, + (SOCKET, WSAEVENT, LPWSANETWORKEVENTS)); +DECL_WINSOCK_FUNCTION(static, int, WSAStartup, (WORD, LPWSADATA)); +DECL_WINSOCK_FUNCTION(static, int, WSACleanup, (void)); +DECL_WINSOCK_FUNCTION(static, int, closesocket, (SOCKET)); +DECL_WINSOCK_FUNCTION(static, u_long, ntohl, (u_long)); +DECL_WINSOCK_FUNCTION(static, u_long, htonl, (u_long)); +DECL_WINSOCK_FUNCTION(static, u_short, htons, (u_short)); +DECL_WINSOCK_FUNCTION(static, u_short, ntohs, (u_short)); +DECL_WINSOCK_FUNCTION(static, struct hostent FAR *, gethostbyname, + (const char FAR *)); +DECL_WINSOCK_FUNCTION(static, struct servent FAR *, getservbyname, + (const char FAR *, const char FAR *)); +DECL_WINSOCK_FUNCTION(static, unsigned long, inet_addr, (const char FAR *)); +DECL_WINSOCK_FUNCTION(static, char FAR *, inet_ntoa, (struct in_addr)); +DECL_WINSOCK_FUNCTION(static, int, connect, + (SOCKET, const struct sockaddr FAR *, int)); +DECL_WINSOCK_FUNCTION(static, int, bind, + (SOCKET, const struct sockaddr FAR *, int)); +DECL_WINSOCK_FUNCTION(static, int, setsockopt, + (SOCKET, int, int, const char FAR *, int)); +DECL_WINSOCK_FUNCTION(static, SOCKET, socket, (int, int, int)); +DECL_WINSOCK_FUNCTION(static, int, listen, (SOCKET, int)); +DECL_WINSOCK_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int)); +DECL_WINSOCK_FUNCTION(static, int, ioctlsocket, + (SOCKET, long, u_long FAR *)); +DECL_WINSOCK_FUNCTION(static, SOCKET, accept, + (SOCKET, struct sockaddr FAR *, int FAR *)); +DECL_WINSOCK_FUNCTION(static, int, recv, (SOCKET, char FAR *, int, int)); + +static HMODULE winsock_module; + void sk_init(void) { + WORD winsock_ver; + WSADATA wsadata; + + winsock_ver = MAKEWORD(2, 0); + winsock_module = LoadLibrary("WS2_32.DLL"); + if (!winsock_module) { + winsock_module = LoadLibrary("WSOCK32.DLL"); + winsock_ver = MAKEWORD(1, 1); + } + if (!winsock_module) + fatalbox("Unable to load any WinSock library"); + + GET_WINSOCK_FUNCTION(WSAAsyncSelect); + GET_WINSOCK_FUNCTION(WSAEventSelect); + GET_WINSOCK_FUNCTION(select); + GET_WINSOCK_FUNCTION(WSAGetLastError); + GET_WINSOCK_FUNCTION(WSAEnumNetworkEvents); + GET_WINSOCK_FUNCTION(WSAStartup); + GET_WINSOCK_FUNCTION(WSACleanup); + GET_WINSOCK_FUNCTION(closesocket); + GET_WINSOCK_FUNCTION(ntohl); + GET_WINSOCK_FUNCTION(htonl); + GET_WINSOCK_FUNCTION(htons); + GET_WINSOCK_FUNCTION(ntohs); + GET_WINSOCK_FUNCTION(gethostbyname); + GET_WINSOCK_FUNCTION(getservbyname); + GET_WINSOCK_FUNCTION(inet_addr); + GET_WINSOCK_FUNCTION(inet_ntoa); + GET_WINSOCK_FUNCTION(connect); + GET_WINSOCK_FUNCTION(bind); + GET_WINSOCK_FUNCTION(setsockopt); + GET_WINSOCK_FUNCTION(socket); + GET_WINSOCK_FUNCTION(listen); + GET_WINSOCK_FUNCTION(send); + GET_WINSOCK_FUNCTION(ioctlsocket); + GET_WINSOCK_FUNCTION(accept); + GET_WINSOCK_FUNCTION(recv); + + if (p_WSAStartup(winsock_ver, &wsadata)) { + fatalbox("Unable to initialise WinSock"); + } + if (LOBYTE(wsadata.wVersion) != LOBYTE(winsock_ver)) { + p_WSACleanup(); + fatalbox("WinSock version is incompatible with %d.%d", + LOBYTE(winsock_ver), HIBYTE(winsock_ver)); + } + sktree = newtree234(cmpfortree); } +void sk_cleanup(void) +{ + Actual_Socket s; + int i; + + if (sktree) { + for (i = 0; (s = index234(sktree, i)) != NULL; i++) { + p_closesocket(s->s); + } + } + + p_WSACleanup(); + if (winsock_module) + FreeLibrary(winsock_module); +} + char *winsock_error_string(int error) { switch (error) { @@ -200,9 +318,9 @@ char *winsock_error_string(int error) } } -SockAddr sk_namelookup(char *host, char **canonicalname) +SockAddr sk_namelookup(const char *host, char **canonicalname) { - SockAddr ret = smalloc(sizeof(struct SockAddr_tag)); + SockAddr ret = snew(struct SockAddr_tag); unsigned long a; struct hostent *h = NULL; char realhost[8192]; @@ -212,7 +330,7 @@ SockAddr sk_namelookup(char *host, char **canonicalname) ret->family = 0; /* We set this one when we have resolved the host. */ *realhost = '\0'; - if ((a = inet_addr(host)) == (unsigned long) INADDR_NONE) { + if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) { #ifdef IPV6 /* Try to get the getaddrinfo() function from wship6.dll */ @@ -248,14 +366,14 @@ SockAddr sk_namelookup(char *host, char **canonicalname) */ if (ret->family == 0) { /*debug(("Resolving \"%s\" with gethostbyname() (IPv4 only)...\n", host)); */ - if ( (h = gethostbyname(host)) ) + if ( (h = p_gethostbyname(host)) ) ret->family = AF_INET; } } /*debug(("Done resolving...(family is %d) AF_INET = %d, AF_INET6 = %d\n", ret->family, AF_INET, AF_INET6)); */ if (ret->family == 0) { - DWORD err = WSAGetLastError(); + DWORD err = p_WSAGetLastError(); ret->error = (err == WSAENETDOWN ? "Network is down" : err == WSAHOST_NOT_FOUND ? "Host does not exist" : err @@ -331,13 +449,88 @@ SockAddr sk_namelookup(char *host, char **canonicalname) ret->family = AF_INET; strncpy(realhost, host, sizeof(realhost)); } - ret->address = ntohl(a); + ret->address = p_ntohl(a); realhost[lenof(realhost)-1] = '\0'; - *canonicalname = smalloc(1+strlen(realhost)); + *canonicalname = snewn(1+strlen(realhost), char); strcpy(*canonicalname, realhost); return ret; } +SockAddr sk_nonamelookup(const char *host) +{ + SockAddr ret = snew(struct SockAddr_tag); + ret->error = NULL; + ret->family = AF_UNSPEC; + strncpy(ret->hostname, host, lenof(ret->hostname)); + ret->hostname[lenof(ret->hostname)-1] = '\0'; + return ret; +} + +void sk_getaddr(SockAddr addr, char *buf, int buflen) +{ +#ifdef IPV6 + if (addr->family == AF_INET6) { + FIXME; /* I don't know how to get a text form of an IPv6 address. */ + } else +#endif + if (addr->family == AF_INET) { + struct in_addr a; + a.s_addr = p_htonl(addr->address); + strncpy(buf, p_inet_ntoa(a), buflen); + buf[buflen-1] = '\0'; + } else { + assert(addr->family == AF_UNSPEC); + strncpy(buf, addr->hostname, buflen); + buf[buflen-1] = '\0'; + } +} + +int sk_hostname_is_local(char *name) +{ + return !strcmp(name, "localhost"); +} + +int sk_address_is_local(SockAddr addr) +{ +#ifdef IPV6 + if (addr->family == AF_INET6) { + FIXME; /* someone who can compile for IPV6 had better do this bit */ + } else +#endif + if (addr->family == AF_INET) { + struct in_addr a; + a.s_addr = p_htonl(addr->address); + return ipv4_is_loopback(a); + } else { + assert(addr->family == AF_UNSPEC); + return 0; /* we don't know; assume not */ + } +} + +int sk_addrtype(SockAddr addr) +{ + return (addr->family == AF_INET ? ADDRTYPE_IPV4 : +#ifdef IPV6 + addr->family == AF_INET6 ? ADDRTYPE_IPV6 : +#endif + ADDRTYPE_NAME); +} + +void sk_addrcopy(SockAddr addr, char *buf) +{ + assert(addr->family != AF_UNSPEC); +#ifdef IPV6 + if (addr->family == AF_INET6) { + memcpy(buf, (char*) addr->ai, 16); + } else +#endif + if (addr->family == AF_INET) { + struct in_addr a; + a.s_addr = p_htonl(addr->address); + memcpy(buf, (char*) &a.s_addr, 4); + } +} + void sk_addr_free(SockAddr addr) { sfree(addr); @@ -361,20 +554,26 @@ static void sk_tcp_flush(Socket s) } static void sk_tcp_close(Socket s); -static int sk_tcp_write(Socket s, char *data, int len); -static int sk_tcp_write_oob(Socket s, char *data, int len); -static char *sk_tcp_socket_error(Socket s); +static int sk_tcp_write(Socket s, const char *data, int len); +static int sk_tcp_write_oob(Socket s, const char *data, int len); +static void sk_tcp_set_private_ptr(Socket s, void *ptr); +static void *sk_tcp_get_private_ptr(Socket s); +static void sk_tcp_set_frozen(Socket s, int is_frozen); +static const char *sk_tcp_socket_error(Socket s); extern char *do_select(SOCKET skt, int startup); Socket sk_register(void *sock, Plug plug) { - static struct socket_function_table fn_table = { + static const struct socket_function_table fn_table = { sk_tcp_plug, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, sk_tcp_flush, + sk_tcp_set_private_ptr, + sk_tcp_get_private_ptr, + sk_tcp_set_frozen, sk_tcp_socket_error }; @@ -385,7 +584,7 @@ Socket sk_register(void *sock, Plug plug) /* * Create Socket structure. */ - ret = smalloc(sizeof(struct Socket_tag)); + ret = snew(struct Socket_tag); ret->fn = &fn_table; ret->error = NULL; ret->plug = plug; @@ -394,11 +593,13 @@ Socket sk_register(void *sock, Plug plug) ret->sending_oob = 0; ret->frozen = 1; ret->frozen_readable = 0; + ret->localhost_only = 0; /* unused, but best init anyway */ + ret->pending_error = 0; ret->s = (SOCKET)sock; if (ret->s == INVALID_SOCKET) { - err = WSAGetLastError(); + err = p_WSAGetLastError(); ret->error = winsock_error_string(err); return (Socket) ret; } @@ -419,14 +620,17 @@ Socket sk_register(void *sock, Plug plug) } Socket sk_new(SockAddr addr, int port, int privport, int oobinline, - Plug plug) + int nodelay, Plug plug) { - static struct socket_function_table fn_table = { + static const struct socket_function_table fn_table = { sk_tcp_plug, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, sk_tcp_flush, + sk_tcp_set_private_ptr, + sk_tcp_get_private_ptr, + sk_tcp_set_frozen, sk_tcp_socket_error }; @@ -443,24 +647,28 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, /* * Create Socket structure. */ - ret = smalloc(sizeof(struct Socket_tag)); + ret = snew(struct Socket_tag); ret->fn = &fn_table; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); - ret->writable = 1; /* to start with */ + ret->connected = 0; /* to start with */ + ret->writable = 0; /* to start with */ ret->sending_oob = 0; ret->frozen = 0; ret->frozen_readable = 0; + ret->localhost_only = 0; /* unused, but best init anyway */ + ret->pending_error = 0; /* * Open socket. */ - s = socket(addr->family, SOCK_STREAM, 0); + assert(addr->family != AF_UNSPEC); + s = p_socket(addr->family, SOCK_STREAM, 0); ret->s = s; if (s == INVALID_SOCKET) { - err = WSAGetLastError(); + err = p_WSAGetLastError(); ret->error = winsock_error_string(err); return (Socket) ret; } @@ -468,7 +676,12 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, ret->oobinline = oobinline; if (oobinline) { BOOL b = TRUE; - setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b)); + p_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b)); + } + + if (nodelay) { + BOOL b = TRUE; + p_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b)); } /* @@ -488,28 +701,28 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, memset(&a6, 0, sizeof(a6)); a6.sin6_family = AF_INET6; /*a6.sin6_addr = in6addr_any; *//* == 0 */ - a6.sin6_port = htons(localport); + a6.sin6_port = p_htons(localport); } else #endif { a.sin_family = AF_INET; - a.sin_addr.s_addr = htonl(INADDR_ANY); - a.sin_port = htons(localport); + a.sin_addr.s_addr = p_htonl(INADDR_ANY); + a.sin_port = p_htons(localport); } #ifdef IPV6 - retcode = bind(s, (addr->family == AF_INET6 ? + retcode = p_bind(s, (addr->family == AF_INET6 ? (struct sockaddr *) &a6 : (struct sockaddr *) &a), (addr->family == AF_INET6 ? sizeof(a6) : sizeof(a))); #else - retcode = bind(s, (struct sockaddr *) &a, sizeof(a)); + retcode = p_bind(s, (struct sockaddr *) &a, sizeof(a)); #endif if (retcode != SOCKET_ERROR) { err = 0; break; /* done */ } else { - err = WSAGetLastError(); + err = p_WSAGetLastError(); if (err != WSAEADDRINUSE) /* failed, for a bad reason */ break; } @@ -533,28 +746,15 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, if (addr->family == AF_INET6) { memset(&a, 0, sizeof(a)); a6.sin6_family = AF_INET6; - a6.sin6_port = htons((short) port); + a6.sin6_port = p_htons((short) port); a6.sin6_addr = ((struct sockaddr_in6 *) addr->ai->ai_addr)->sin6_addr; } else #endif { a.sin_family = AF_INET; - a.sin_addr.s_addr = htonl(addr->address); - a.sin_port = htons((short) port); - } - if (( -#ifdef IPV6 - connect(s, ((addr->family == AF_INET6) ? - (struct sockaddr *) &a6 : (struct sockaddr *) &a), - (addr->family == AF_INET6) ? sizeof(a6) : sizeof(a)) -#else - connect(s, (struct sockaddr *) &a, sizeof(a)) -#endif - ) == SOCKET_ERROR) { - err = WSAGetLastError(); - ret->error = winsock_error_string(err); - return (Socket) ret; + a.sin_addr.s_addr = p_htonl(addr->address); + a.sin_port = p_htons((short) port); } /* Set up a select mechanism. This could be an AsyncSelect on a @@ -565,19 +765,53 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, return (Socket) ret; } + if (( +#ifdef IPV6 + p_connect(s, ((addr->family == AF_INET6) ? + (struct sockaddr *) &a6 : (struct sockaddr *) &a), + (addr->family == AF_INET6) ? sizeof(a6) : sizeof(a)) +#else + p_connect(s, (struct sockaddr *) &a, sizeof(a)) +#endif + ) == SOCKET_ERROR) { + err = p_WSAGetLastError(); + /* + * We expect a potential EWOULDBLOCK here, because the + * chances are the front end has done a select for + * FD_CONNECT, so that connect() will complete + * asynchronously. + */ + if ( err != WSAEWOULDBLOCK ) { + ret->error = winsock_error_string(err); + return (Socket) ret; + } + } else { + /* + * If we _don't_ get EWOULDBLOCK, the connect has completed + * and we should set the socket as writable. + */ + ret->writable = 1; + } + add234(sktree, ret); + /* We're done with 'addr' now. */ + sk_addr_free(addr); + return (Socket) ret; } -Socket sk_newlistener(int port, Plug plug, int local_host_only) +Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only) { - static struct socket_function_table fn_table = { + static const struct socket_function_table fn_table = { sk_tcp_plug, sk_tcp_close, sk_tcp_write, sk_tcp_write_oob, sk_tcp_flush, + sk_tcp_set_private_ptr, + sk_tcp_get_private_ptr, + sk_tcp_set_frozen, sk_tcp_socket_error }; @@ -595,7 +829,7 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only) /* * Create Socket structure. */ - ret = smalloc(sizeof(struct Socket_tag)); + ret = snew(struct Socket_tag); ret->fn = &fn_table; ret->error = NULL; ret->plug = plug; @@ -604,57 +838,80 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only) ret->sending_oob = 0; ret->frozen = 0; ret->frozen_readable = 0; + ret->localhost_only = local_host_only; + ret->pending_error = 0; /* * Open socket. */ - s = socket(AF_INET, SOCK_STREAM, 0); + s = p_socket(AF_INET, SOCK_STREAM, 0); ret->s = s; if (s == INVALID_SOCKET) { - err = WSAGetLastError(); + err = p_WSAGetLastError(); ret->error = winsock_error_string(err); return (Socket) ret; } ret->oobinline = 0; - - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); - + p_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); #ifdef IPV6 if (addr->family == AF_INET6) { memset(&a6, 0, sizeof(a6)); a6.sin6_family = AF_INET6; + /* FIXME: srcaddr is ignored for IPv6, because I (SGT) don't + * know how to do it. :-) */ if (local_host_only) a6.sin6_addr = in6addr_loopback; else a6.sin6_addr = in6addr_any; - a6.sin6_port = htons(port); + a6.sin6_port = p_htons(port); } else #endif { + int got_addr = 0; a.sin_family = AF_INET; - if (local_host_only) - a.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - else - a.sin_addr.s_addr = htonl(INADDR_ANY); - a.sin_port = htons((short)port); + + /* + * Bind to source address. First try an explicitly + * specified one... + */ + if (srcaddr) { + a.sin_addr.s_addr = p_inet_addr(srcaddr); + if (a.sin_addr.s_addr != INADDR_NONE) { + /* Override localhost_only with specified listen addr. */ + ret->localhost_only = ipv4_is_loopback(a.sin_addr); + got_addr = 1; + } + } + + /* + * ... and failing that, go with one of the standard ones. + */ + if (!got_addr) { + if (local_host_only) + a.sin_addr.s_addr = p_htonl(INADDR_LOOPBACK); + else + a.sin_addr.s_addr = p_htonl(INADDR_ANY); + } + + a.sin_port = p_htons((short)port); } #ifdef IPV6 - retcode = bind(s, (addr->family == AF_INET6 ? + retcode = p_bind(s, (addr->family == AF_INET6 ? (struct sockaddr *) &a6 : (struct sockaddr *) &a), (addr->family == AF_INET6 ? sizeof(a6) : sizeof(a))); #else - retcode = bind(s, (struct sockaddr *) &a, sizeof(a)); + retcode = p_bind(s, (struct sockaddr *) &a, sizeof(a)); #endif if (retcode != SOCKET_ERROR) { err = 0; } else { - err = WSAGetLastError(); + err = p_WSAGetLastError(); } if (err) { @@ -663,8 +920,8 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only) } - if (listen(s, SOMAXCONN) == SOCKET_ERROR) { - closesocket(s); + if (p_listen(s, SOMAXCONN) == SOCKET_ERROR) { + p_closesocket(s); ret->error = winsock_error_string(err); return (Socket) ret; } @@ -689,7 +946,7 @@ static void sk_tcp_close(Socket sock) del234(sktree, s); do_select(s->s, 0); - closesocket(s->s); + p_closesocket(s->s); sfree(s); } @@ -713,11 +970,10 @@ void try_send(Actual_Socket s) urgentflag = 0; bufchain_prefix(&s->output_data, &data, &len); } - - nsent = send(s->s, data, len, urgentflag); + nsent = p_send(s->s, data, len, urgentflag); noise_ultralight(nsent); if (nsent <= 0) { - err = (nsent < 0 ? WSAGetLastError() : 0); + err = (nsent < 0 ? p_WSAGetLastError() : 0); if ((err < WSABASEERR && nsent < 0) || err == WSAEWOULDBLOCK) { /* * Perfectly normal: we've sent all we can for the moment. @@ -733,19 +989,22 @@ void try_send(Actual_Socket s) } else if (nsent == 0 || err == WSAECONNABORTED || err == WSAECONNRESET) { /* - * FIXME. This will have to be done better when we - * start managing multiple sockets (e.g. SSH port - * forwarding), because if we get CONNRESET while - * trying to write a particular forwarded socket - * then it isn't necessarily the end of the world. - * Ideally I'd like to pass the error code back to - * somewhere the next select_result() will see it, - * but that might be hard. Perhaps I should pass it - * back to be queued in the Windows front end bit. + * If send() returns CONNABORTED or CONNRESET, we + * unfortunately can't just call plug_closing(), + * because it's quite likely that we're currently + * _in_ a call from the code we'd be calling back + * to, so we'd have to make half the SSH code + * reentrant. Instead we flag a pending error on + * the socket, to be dealt with (by calling + * plug_closing()) at some suitable future moment. */ - fatalbox(winsock_error_string(err)); + s->pending_error = err; + return; } else { - fatalbox(winsock_error_string(err)); + /* We're inside the Windows frontend here, so we know + * that the frontend handle is unnecessary. */ + logevent(NULL, winsock_error_string(err)); + fatalbox("%s", winsock_error_string(err)); } } else { if (s->sending_oob) { @@ -762,7 +1021,7 @@ void try_send(Actual_Socket s) } } -static int sk_tcp_write(Socket sock, char *buf, int len) +static int sk_tcp_write(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; @@ -780,7 +1039,7 @@ static int sk_tcp_write(Socket sock, char *buf, int len) return bufchain_size(&s->output_data); } -static int sk_tcp_write_oob(Socket sock, char *buf, int len) +static int sk_tcp_write_oob(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; @@ -810,6 +1069,10 @@ int select_result(WPARAM wParam, LPARAM lParam) u_long atmark; /* wParam is the socket itself */ + + if (wParam == 0) + return 1; /* boggle */ + s = find234(sktree, (void *) wParam, cmpforsearch); if (!s) return 1; /* boggle */ @@ -825,6 +1088,9 @@ int select_result(WPARAM wParam, LPARAM lParam) noise_ultralight(lParam); switch (WSAGETSELECTEVENT(lParam)) { + case FD_CONNECT: + s->connected = s->writable = 1; + break; case FD_READ: /* In the case the socket is still frozen, we don't even bother */ if (s->frozen) { @@ -840,7 +1106,7 @@ int select_result(WPARAM wParam, LPARAM lParam) */ if (s->oobinline) { atmark = 1; - ioctlsocket(s->s, SIOCATMARK, &atmark); + p_ioctlsocket(s->s, SIOCATMARK, &atmark); /* * Avoid checking the return value from ioctlsocket(), * on the grounds that some WinSock wrappers don't @@ -851,10 +1117,10 @@ int select_result(WPARAM wParam, LPARAM lParam) } else atmark = 1; - ret = recv(s->s, buf, sizeof(buf), 0); + ret = p_recv(s->s, buf, sizeof(buf), 0); noise_ultralight(ret); if (ret < 0) { - err = WSAGetLastError(); + err = p_WSAGetLastError(); if (err == WSAEWOULDBLOCK) { break; } @@ -875,11 +1141,15 @@ int select_result(WPARAM wParam, LPARAM lParam) * and get back OOB data, which we will send to the back * end with type==2 (urgent data). */ - ret = recv(s->s, buf, sizeof(buf), MSG_OOB); + ret = p_recv(s->s, buf, sizeof(buf), MSG_OOB); noise_ultralight(ret); if (ret <= 0) { - fatalbox(ret == 0 ? "Internal networking trouble" : - winsock_error_string(WSAGetLastError())); + char *str = (ret == 0 ? "Internal networking trouble" : + winsock_error_string(p_WSAGetLastError())); + /* We're inside the Windows frontend here, so we know + * that the frontend handle is unnecessary. */ + logevent(NULL, str); + fatalbox("%s", str); } else { return plug_receive(s->plug, 2, buf, ret); } @@ -899,9 +1169,9 @@ int select_result(WPARAM wParam, LPARAM lParam) /* Signal a close on the socket. First read any outstanding data. */ open = 1; do { - ret = recv(s->s, buf, sizeof(buf), 0); + ret = p_recv(s->s, buf, sizeof(buf), 0); if (ret < 0) { - err = WSAGetLastError(); + err = p_WSAGetLastError(); if (err == WSAEWOULDBLOCK) break; return plug_closing(s->plug, winsock_error_string(err), @@ -916,22 +1186,24 @@ int select_result(WPARAM wParam, LPARAM lParam) return open; case FD_ACCEPT: { - struct sockaddr isa; - int addrlen = sizeof(struct sockaddr); + struct sockaddr_in isa; + int addrlen = sizeof(struct sockaddr_in); SOCKET t; /* socket of connection */ - memset(&isa, 0, sizeof(struct sockaddr)); + memset(&isa, 0, sizeof(struct sockaddr_in)); err = 0; - t = accept(s->s,&isa,&addrlen); + t = p_accept(s->s,(struct sockaddr *)&isa,&addrlen); if (t == INVALID_SOCKET) { - err = WSAGetLastError(); + err = p_WSAGetLastError(); if (err == WSATRY_AGAIN) break; } - if (plug_accepting(s->plug, (void*)t)) { - closesocket(t); /* denied or error */ + if (s->localhost_only && !ipv4_is_loopback(isa.sin_addr)) { + p_closesocket(t); /* dodgy WinSock let nonlocal through */ + } else if (plug_accepting(s->plug, (void*)t)) { + p_closesocket(t); /* denied or error */ } } } @@ -940,16 +1212,54 @@ int select_result(WPARAM wParam, LPARAM lParam) } /* + * Deal with socket errors detected in try_send(). + */ +void net_pending_errors(void) +{ + int i; + Actual_Socket s; + + /* + * This might be a fiddly business, because it's just possible + * that handling a pending error on one socket might cause + * others to be closed. (I can't think of any reason this might + * happen in current SSH implementation, but to maintain + * generality of this network layer I'll assume the worst.) + * + * So what we'll do is search the socket list for _one_ socket + * with a pending error, and then handle it, and then search + * the list again _from the beginning_. Repeat until we make a + * pass with no socket errors present. That way we are + * protected against the socket list changing under our feet. + */ + + do { + for (i = 0; (s = index234(sktree, i)) != NULL; i++) { + if (s->pending_error) { + /* + * An error has occurred on this socket. Pass it to the + * plug. + */ + plug_closing(s->plug, + winsock_error_string(s->pending_error), + s->pending_error, 0); + break; + } + } + } while (s); +} + +/* * Each socket abstraction contains a `void *' private field in * which the client can keep state. */ -void sk_set_private_ptr(Socket sock, void *ptr) +static void sk_tcp_set_private_ptr(Socket sock, void *ptr) { Actual_Socket s = (Actual_Socket) sock; s->private_ptr = ptr; } -void *sk_get_private_ptr(Socket sock) +static void *sk_tcp_get_private_ptr(Socket sock) { Actual_Socket s = (Actual_Socket) sock; return s->private_ptr; @@ -960,17 +1270,17 @@ void *sk_get_private_ptr(Socket sock) * if there's a problem. These functions extract an error message, * or return NULL if there's no problem. */ -char *sk_addr_error(SockAddr addr) +const char *sk_addr_error(SockAddr addr) { return addr->error; } -static char *sk_tcp_socket_error(Socket sock) +static const char *sk_tcp_socket_error(Socket sock) { Actual_Socket s = (Actual_Socket) sock; return s->error; } -void sk_set_frozen(Socket sock, int is_frozen) +static void sk_tcp_set_frozen(Socket sock, int is_frozen) { Actual_Socket s = (Actual_Socket) sock; if (s->frozen == is_frozen) @@ -978,7 +1288,7 @@ void sk_set_frozen(Socket sock, int is_frozen) s->frozen = is_frozen; if (!is_frozen && s->frozen_readable) { char c; - recv(s->s, &c, 1, MSG_PEEK); + p_recv(s->s, &c, 1, MSG_PEEK); } s->frozen_readable = 0; } @@ -999,3 +1309,13 @@ SOCKET next_socket(int *state) Actual_Socket s = index234(sktree, (*state)++); return s ? s->s : INVALID_SOCKET; } + +int net_service_lookup(char *service) +{ + struct servent *se; + se = p_getservbyname(service, NULL); + if (se != NULL) + return p_ntohs(se->s_port); + else + return 0; +}