diff options
Diffstat (limited to 'src/platform/windows/win_net.c')
| -rw-r--r-- | src/platform/windows/win_net.c | 358 |
1 files changed, 259 insertions, 99 deletions
diff --git a/src/platform/windows/win_net.c b/src/platform/windows/win_net.c index 3bd34b78..c7a7849d 100644 --- a/src/platform/windows/win_net.c +++ b/src/platform/windows/win_net.c @@ -11,46 +11,104 @@ #ifdef PLATFORM_WINDOWS +#include <stdio.h> + +// Windows has infinite numbers of error codes it seems. static struct { int wsa_err; int nng_err; } nni_plat_wsa_errnos[] = { - { WSAECONNABORTED, NNG_ECLOSED }, - { WSAEINTR, NNG_EINTR }, + { WSA_INVALID_HANDLE, NNG_ECLOSED }, + { WSA_NOT_ENOUGH_MEMORY, NNG_ENOMEM }, + { WSA_INVALID_PARAMETER, NNG_EINVAL }, + { WSA_OPERATION_ABORTED, NNG_ECLOSED }, + { WSA_IO_INCOMPLETE, NNG_EAGAIN }, + + { WSAEINTR, NNG_EINTR }, + { WSAEBADF, NNG_ECLOSED }, + { WSAEACCES, NNG_EPERM }, + { WSAEFAULT, NNG_ESYSERR + WSAEFAULT }, + { WSAEWOULDBLOCK, NNG_EAGAIN }, + { WSAEINPROGRESS, NNG_EAGAIN }, + { WSAEALREADY, NNG_ESYSERR + WSAEALREADY }, + { WSAENOTSOCK, NNG_ECLOSED }, + { WSAEMSGSIZE, NNG_EMSGSIZE }, + { WSAEPROTOTYPE, NNG_ESYSERR + WSAEPROTOTYPE }, + { WSAENOPROTOOPT, NNG_ENOTSUP }, + { WSAEPROTONOSUPPORT, NNG_ENOTSUP }, + { WSAEPROTONOSUPPORT, NNG_ENOTSUP }, + { WSAEADDRINUSE, NNG_EADDRINUSE }, + { WSAEADDRNOTAVAIL, NNG_EADDRINVAL }, + { WSAENETDOWN, NNG_EUNREACHABLE }, + { WSAENETUNREACH, NNG_EUNREACHABLE }, + { WSAECONNABORTED, NNG_ETIMEDOUT }, + { WSAECONNRESET, NNG_ECLOSED }, + { WSAENOBUFS, NNG_ENOMEM }, + { WSAEISCONN, NNG_ESYSERR + WSAEISCONN }, + { WSAENOTCONN, NNG_ECLOSED }, + { WSAESHUTDOWN, NNG_ECLOSED }, + { WSAETOOMANYREFS, NNG_ESYSERR + WSAETOOMANYREFS }, + { WSAETIMEDOUT, NNG_ETIMEDOUT }, + { WSAECONNREFUSED, NNG_ECONNREFUSED }, + { WSAELOOP, NNG_ESYSERR + WSAELOOP }, + { WSAENAMETOOLONG, NNG_ESYSERR + WSAENAMETOOLONG }, + { WSAEHOSTDOWN, NNG_EUNREACHABLE }, + { WSAEHOSTUNREACH, NNG_EUNREACHABLE }, + { WSAENOTEMPTY, NNG_ESYSERR + WSAENOTEMPTY }, + { WSAEPROCLIM, NNG_ESYSERR + WSAEPROCLIM }, + { WSAEUSERS, NNG_ESYSERR + WSAEUSERS }, + { WSAEDQUOT, NNG_ESYSERR + WSAEDQUOT }, + { WSAESTALE, NNG_ESYSERR + WSAESTALE }, + { WSAEREMOTE, NNG_ESYSERR + WSAEREMOTE }, + { WSASYSNOTREADY, NNG_ESYSERR + WSASYSNOTREADY }, + { WSAVERNOTSUPPORTED, NNG_ENOTSUP }, + { WSANOTINITIALISED, NNG_ESYSERR + WSANOTINITIALISED }, + { WSAEDISCON, NNG_ECLOSED }, + { WSAENOMORE, NNG_ESYSERR + WSAENOMORE }, + { WSAECANCELLED, NNG_ESYSERR + WSAECANCELLED }, + { WSAEINVALIDPROVIDER, NNG_ESYSERR + WSAEINVALIDPROVIDER }, + { WSAEPROVIDERFAILEDINIT, NNG_ESYSERR + WSAEPROVIDERFAILEDINIT }, + { WSASYSCALLFAILURE, NNG_ESYSERR + WSASYSCALLFAILURE }, + { WSASERVICE_NOT_FOUND, NNG_ESYSERR + WSASERVICE_NOT_FOUND }, + { WSATYPE_NOT_FOUND, NNG_ESYSERR + WSATYPE_NOT_FOUND }, + { WSA_E_CANCELLED, NNG_ESYSERR + WSA_E_CANCELLED }, + { WSAEREFUSED, NNG_ESYSERR + WSAEREFUSED }, + { WSAHOST_NOT_FOUND, NNG_EADDRINVAL }, + { WSATRY_AGAIN, NNG_EAGAIN }, + { WSANO_RECOVERY, NNG_ESYSERR + WSANO_RECOVERY }, + { WSANO_DATA, NNG_EADDRINVAL }, + // Eliding all the QoS related errors. + +#if 0 // REVIEW THESE!!! - { WSAECONNRESET, NNG_ECONNREFUSED }, - { WSAEMSGSIZE, NNG_EINVAL }, - { WSAENETDOWN, NNG_EUNREACHABLE }, - { WSAENETRESET, NNG_ECLOSED }, - { WSAENOBUFS, NNG_ENOMEM }, - { WSAESHUTDOWN, NNG_ECLOSED }, - { WSAEWOULDBLOCK, NNG_EAGAIN }, - { WSAEBADF, NNG_ECLOSED }, - { WSA_INVALID_HANDLE, NNG_ECLOSED }, - { WSA_NOT_ENOUGH_MEMORY, NNG_ENOMEM }, - { WSA_INVALID_PARAMETER, NNG_EINVAL }, - { WSAEACCES, NNG_EPERM }, - { 0, 0 }, // MUST BE LAST + { ERROR_BROKEN_PIPE, NNG_ECLOSED }, + { ERROR_CONNECTION_REFUSED, NNG_ECONNREFUSED }, + { ERROR_NOT_CONNECTED, NNG_ECLOSED }, + { ERROR_PIPE_NOT_CONNECTED, NNG_ECLOSED }, + { ERROR_NO_DATA, NNG_ECLOSED }, +#endif + // Must be Last!! + { 0, 0 }, }; static int -nni_plat_wsa_last_error(void) +nni_winsock_error(int werr) { - int errnum = WSAGetLastError(); int i; - if (errnum == 0) { + if (werr == 0) { return (0); } + for (i = 0; nni_plat_wsa_errnos[i].nng_err != 0; i++) { - if (errnum == nni_plat_wsa_errnos[i].wsa_err) { + if (werr == nni_plat_wsa_errnos[i].wsa_err) { return (nni_plat_wsa_errnos[i].nng_err); } } // Other system errno. - return (NNG_ESYSERR + errnum); + return (NNG_ESYSERR + werr); } @@ -140,22 +198,53 @@ nni_plat_tcp_send(nni_plat_tcpsock *s, nni_iov *iovs, int cnt) { WSABUF iov[4]; // We never have more than 3 at present int i; - DWORD sent = 0; int rv; + DWORD offset; + DWORD nsent; + DWORD resid; + DWORD flags; + WSAOVERLAPPED *olp = &s->send_olpd; if (cnt > 4) { return (NNG_EINVAL); } - for (i = 0; i < cnt; i++) { + for (i = 0, resid = 0; i < cnt; resid += iov[i].len, i++) { iov[i].buf = iovs[i].iov_buf; iov[i].len = iovs[i].iov_len; } - rv = WSASend(s->s, iov, cnt, &sent, 0, NULL, NULL); - if (rv != 0) { - // XXX: CONVERT WSAGetLastError code. - return (nni_plat_wsa_last_error()); + i = 0; + while (resid) { + flags = 0; + rv = WSASend(s->s, &iov[i], cnt, &nsent, flags, olp, NULL); + if (rv == SOCKET_ERROR) { + if ((rv = WSAGetLastError()) != WSA_IO_PENDING) { + return (nni_winsock_error(rv)); + } + flags = 0; + if (!WSAGetOverlappedResult(s->s, olp, &nsent, + TRUE, &flags)) { + return (nni_winsock_error(WSAGetLastError())); + } + } + + if (nsent > resid) { + nni_panic("WSASend says it sent too much"); + } + + resid -= nsent; + while (nsent) { + if (iov[i].len <= nsent) { + nsent -= iov[i].len; + i++; + cnt--; + } else { + iov[i].len -= nsent; + iov[i].buf += nsent; + nsent = 0; + } + } } return (0); @@ -167,29 +256,39 @@ nni_plat_tcp_recv(nni_plat_tcpsock *s, nni_iov *iovs, int cnt) { WSABUF iov[4]; // We never have more than 3 at present int i; - int offset; - int resid = 0; int rv; + DWORD offset; + DWORD resid; DWORD nrecv; + DWORD flags; + WSAOVERLAPPED *olp = &s->recv_olpd; if (cnt > 4) { return (NNG_EINVAL); } - for (i = 0; i < cnt; i++) { + for (i = 0, resid = 0; i < cnt; resid += iov[i].len, i++) { iov[i].buf = iovs[i].iov_buf; iov[i].len = iovs[i].iov_len; - resid += iov[i].len; } i = 0; while (resid) { - rv = WSARecv(s->s, iov, cnt, &nrecv, 0, NULL, NULL); - if (rv != 0) { - return (nni_plat_wsa_last_error()); + flags = 0; + rv = WSARecv(s->s, &iov[i], cnt, &nrecv, &flags, olp, NULL); + if (rv == SOCKET_ERROR) { + if ((rv = WSAGetLastError()) != WSA_IO_PENDING) { + return (nni_winsock_error(rv)); + } + flags = 0; + if (!WSAGetOverlappedResult(s->s, olp, &nrecv, + TRUE, &flags)) { + return (nni_winsock_error(WSAGetLastError())); + } } + if (nrecv > resid) { - nni_panic("readv says it read too much!"); + nni_panic("WSARecv says it read too much!"); } resid -= nrecv; @@ -227,19 +326,74 @@ nni_plat_tcp_setopts(SOCKET fd) } -void +int nni_plat_tcp_init(nni_plat_tcpsock *s) { s->s = INVALID_SOCKET; + return (0); +} + + +static int +nni_plat_tcp_open(nni_plat_tcpsock *s) +{ + int rv; + DWORD nbytes; + GUID guid1 = WSAID_CONNECTEX; + GUID guid2 = WSAID_ACCEPTEX; + + ZeroMemory(s, sizeof (*s)); + s->s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_NO_HANDLE_INHERIT|WSA_FLAG_OVERLAPPED); + if (s->s == INVALID_SOCKET) { + rv = WSAGetLastError(); + return (nni_winsock_error(rv)); + } + + if (WSAIoctl(s->s, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid1, sizeof (guid1), &s->connectex, sizeof (s->connectex), + &nbytes, NULL, NULL) == SOCKET_ERROR) { + nni_panic("failed lookup for ConnectEx function"); + } + if (WSAIoctl(s->s, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid2, sizeof (guid2), &s->acceptex, sizeof (s->acceptex), + &nbytes, NULL, NULL) == SOCKET_ERROR) { + nni_panic("failed lookup for AcceptEx function"); + } + + nni_plat_tcp_setopts(s->s); + + return (0); +} + + +static void +nni_plat_tcp_close(nni_plat_tcpsock *s) +{ + SOCKET fd; + + if ((fd = s->s) != INVALID_SOCKET) { + s->s = INVALID_SOCKET; + (void) shutdown(fd, SD_BOTH); + (void) CancelIoEx((HANDLE) fd, &s->conn_olpd); + (void) CancelIoEx((HANDLE) fd, &s->recv_olpd); + (void) CancelIoEx((HANDLE) fd, &s->send_olpd); + (void) closesocket(fd); + } } void nni_plat_tcp_fini(nni_plat_tcpsock *s) { - if (s->s != INVALID_SOCKET) { - (void) closesocket(s->s); + SOCKET fd; + + if ((fd = s->s) != INVALID_SOCKET) { s->s = INVALID_SOCKET; + (void) CancelIoEx((HANDLE) fd, &s->conn_olpd); + (void) CancelIoEx((HANDLE) fd, &s->recv_olpd); + (void) CancelIoEx((HANDLE) fd, &s->send_olpd); + (void) closesocket(fd); } } @@ -247,9 +401,7 @@ nni_plat_tcp_fini(nni_plat_tcpsock *s) void nni_plat_tcp_shutdown(nni_plat_tcpsock *s) { - if (s->s != INVALID_SOCKET) { - (void) shutdown(s->s, SD_BOTH); - } + nni_plat_tcp_close(s); } @@ -264,46 +416,39 @@ nni_plat_tcp_listen(nni_plat_tcpsock *s, const nni_sockaddr *addr) { int len; SOCKADDR_STORAGE ss; + ULONG yes; int rv; - BOOL yes; len = nni_plat_to_sockaddr(&ss, addr); if (len < 0) { return (NNG_EADDRINVAL); } - s->s = WSASocket(ss.ss_family, SOCK_STREAM, 0, NULL, 0, - WSA_FLAG_NO_HANDLE_INHERIT); - if (s->s == INVALID_SOCKET) { - return (nni_plat_wsa_last_error()); + if ((rv = nni_plat_tcp_open(s)) != 0) { + return (rv); } - nni_plat_tcp_setopts(s->s); - // Make sure that we use the address exclusively. Windows lets // others hijack us by default. yes = 1; if (setsockopt(s->s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &yes, sizeof (yes)) == SOCKET_ERROR) { - rv = nni_plat_wsa_last_error(); - (void) closesocket(s->s); - s->s = INVALID_SOCKET; - return (rv); + rv = WSAGetLastError(); + nni_plat_tcp_close(s); + return (nni_winsock_error(rv)); } if (bind(s->s, (struct sockaddr *) &ss, len) != 0) { - rv = nni_plat_wsa_last_error(); - (void) closesocket(s->s); - s->s = INVALID_SOCKET; - return (rv); + rv = WSAGetLastError(); + nni_plat_tcp_close(s); + return (nni_winsock_error(rv)); } // Listen -- 128 depth is probably sufficient. If it isn't, other // bad things are going to happen. if (listen(s->s, 128) != 0) { - rv = nni_plat_wsa_last_error(); - (void) closesocket(s->s); - s->s = INVALID_SOCKET; - return (rv); + rv = WSAGetLastError(); + nni_plat_tcp_close(s); + return (nni_winsock_error(rv)); } return (0); @@ -320,6 +465,10 @@ nni_plat_tcp_connect(nni_plat_tcpsock *s, const nni_sockaddr *addr, int len; SOCKADDR_STORAGE ss; SOCKADDR_STORAGE bss; + WSAOVERLAPPED *olp = &s->conn_olpd; + BOOL ok; + DWORD nbytes; + DWORD flags; int rv; len = nni_plat_to_sockaddr(&ss, addr); @@ -327,39 +476,44 @@ nni_plat_tcp_connect(nni_plat_tcpsock *s, const nni_sockaddr *addr, return (NNG_EADDRINVAL); } - s->s = WSASocket(ss.ss_family, SOCK_STREAM, 0, NULL, 0, - WSA_FLAG_NO_HANDLE_INHERIT); - if (s->s == INVALID_SOCKET) { - return (nni_plat_wsa_last_error()); - } - if (bindaddr != NULL) { if (bindaddr->s_un.s_family != addr->s_un.s_family) { - (void) closesocket(s->s); - s->s = INVALID_SOCKET; - return (NNG_EINVAL); + return (NNG_EADDRINVAL); } if (nni_plat_to_sockaddr(&bss, bindaddr) < 0) { - (void) closesocket(s->s); - s->s = INVALID_SOCKET; return (NNG_EADDRINVAL); } - if (bind(s->s, (struct sockaddr *) &bss, len) < 0) { - rv = nni_plat_wsa_last_error(); - (void) closesocket(s->s); - s->s = INVALID_SOCKET; - return (rv); - } + } else { + ZeroMemory(&bss, sizeof (bss)); + bss.ss_family = ss.ss_family; } - nni_plat_tcp_setopts(s->s); - - if (connect(s->s, (struct sockaddr *) &ss, len) != 0) { - rv = nni_plat_wsa_last_error(); - (void) closesocket(s->s); - s->s = INVALID_SOCKET; + if ((rv = nni_plat_tcp_open(s)) != 0) { return (rv); } + + // ConnectEx must always be bound first. + if (bind(s->s, (struct sockaddr *) &bss, len) < 0) { + rv = WSAGetLastError(); + nni_plat_tcp_close(s); + return (nni_winsock_error(rv)); + } + + if (s->connectex(s->s, (struct sockaddr *) &ss, len, NULL, 0, NULL, + olp)) { + // Immediate completion? + return (0); + } + if ((rv = WSAGetLastError()) != ERROR_IO_PENDING) { + nni_plat_tcp_close(s); + return (nni_winsock_error(rv)); + } + nbytes = flags = 0; + if (!WSAGetOverlappedResult(s->s, olp, &nbytes, TRUE, &flags)) { + rv = WSAGetLastError(); + nni_plat_tcp_close(s); + return (nni_winsock_error(rv)); + } return (0); } @@ -367,26 +521,32 @@ nni_plat_tcp_connect(nni_plat_tcpsock *s, const nni_sockaddr *addr, int nni_plat_tcp_accept(nni_plat_tcpsock *s, nni_plat_tcpsock *server) { - SOCKET fd; - int err; - - for (;;) { - fd = accept(server->s, NULL, NULL); + DWORD nbytes; + DWORD flags; + WSAOVERLAPPED *olp = &s->conn_olpd; + char ainfo[512]; + int rv; - if (fd == INVALID_SOCKET) { - err = WSAGetLastError(); - if ((err == WSAECONNRESET) || (err == WSAEWOULDBLOCK)) { - continue; - } - return (nni_plat_wsa_last_error()); - } else { - break; - } + if ((rv = nni_plat_tcp_open(s)) != 0) { + return (rv); } - nni_plat_tcp_setopts(fd); - - s->s = fd; + // 256 > (sizeof (SOCKADDR_STORAGE) + 16) + nbytes = 0; + if (s->acceptex(server->s, s->s, ainfo, 0, 256, 256, &nbytes, olp)) { + // Immediate completion? + return (0); + } + if ((rv = WSAGetLastError()) != ERROR_IO_PENDING) { + nni_plat_tcp_close(s); + return (nni_winsock_error(rv)); + } + nbytes = flags = 0; + if (!WSAGetOverlappedResult(server->s, olp, &nbytes, TRUE, &flags)) { + rv = WSAGetLastError(); + nni_plat_tcp_close(s); + return (nni_winsock_error(rv)); + } return (0); } |
