aboutsummaryrefslogtreecommitdiff
path: root/src/platform/windows/win_net.c
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2017-01-14 13:00:55 -0800
committerGarrett D'Amore <garrett@damore.org>2017-01-14 13:00:55 -0800
commitf4ce5a285167e7656037096f77f04ab80a010453 (patch)
tree48a9746e154872e4a01c9dee465aed716af278be /src/platform/windows/win_net.c
parentb639e4d3643b8245b77bc8707a3a864221fad195 (diff)
downloadnng-f4ce5a285167e7656037096f77f04ab80a010453.tar.gz
nng-f4ce5a285167e7656037096f77f04ab80a010453.tar.bz2
nng-f4ce5a285167e7656037096f77f04ab80a010453.zip
Windows TCP now working.
There are lots of changes here, mostly stuff we did in support of Windows TCP. However, there are some bugs that were fixed, and we added some new error codes, and generalized the handling of some failures during accept. Windows IPC (NamedPipes) is still missing.
Diffstat (limited to 'src/platform/windows/win_net.c')
-rw-r--r--src/platform/windows/win_net.c358
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);
}