diff options
| author | Garrett D'Amore <garrett@damore.org> | 2017-01-12 20:46:20 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2017-01-12 21:44:44 -0800 |
| commit | 2bb6a23037656474a90d869c5147b32bae1a2e40 (patch) | |
| tree | 90697aa3fba3186296a18decb32691a1a143b50a /src/platform/windows/win_net.c | |
| parent | 70348e2d4725c9ec43f51811d290c22c782058ac (diff) | |
| download | nng-2bb6a23037656474a90d869c5147b32bae1a2e40.tar.gz nng-2bb6a23037656474a90d869c5147b32bae1a2e40.tar.bz2 nng-2bb6a23037656474a90d869c5147b32bae1a2e40.zip | |
Initial swag at Win32. Much to do still.
Diffstat (limited to 'src/platform/windows/win_net.c')
| -rw-r--r-- | src/platform/windows/win_net.c | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/src/platform/windows/win_net.c b/src/platform/windows/win_net.c new file mode 100644 index 00000000..7c44c125 --- /dev/null +++ b/src/platform/windows/win_net.c @@ -0,0 +1,391 @@ +// +// Copyright 2017 Garrett D'Amore <garrett@damore.org> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include "core/nng_impl.h" + +#ifdef PLATFORM_WINDOWS + +static struct { + int wsa_err; + int nng_err; +} +nni_plat_wsa_errnos[] = { + { WSAECONNABORTED, NNG_ECONNABORTED }, + { WSAEINTR, NNG_EINTR }, + { WSAEFAULT, NNG_EFAULT }, + { WSAECONNRESET, NNG_ECONNRESET }, + { 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 }, + { WSAEACCESS, NNG_EPERM }, + { 0, 0 }, // MUST BE LAST +}; + + +static int +nni_plat_wsa_last_error(void) +{ + errnum = WSAGetLastError(); + if (errnum == 0) { + return (0); + } + for (i = 0; nni_plat_wsa_errnos[i].nng_err != 0; i++) { + if (errnum == nni_plat_wsa_errnos[i].wsa_err) { + return (nni_plat_errnos[i].nng_err); + } + } + // Other system errno. + return (NNG_ESYSERR + errnum); +} + + +static int +nni_plat_to_sockaddr(struct SOCKADDR_STORAGE *ss, const nni_sockaddr *sa) +{ + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + switch (sa->s_un.s_family) { + case NNG_AF_INET: + sin = (void *) ss; + memset(sin, 0, sizeof (*sin)); + sin->sin_family = PF_INET; + sin->sin_port = sa->s_un.s_in.sa_port; + sin->sin_addr.s_addr = sa->s_un.s_in.sa_addr; + return (sizeof (*sin)); + + case NNG_AF_INET6: + sin6 = (void *) ss; + memset(&sin6, 0, sizeof (sin6)); + sin6->sin6_family = PF_INET6; + sin6->sin6_port = sa->s_un.s_in6.sa_port; + memcpy(sin6->sin6_addr.s6_addr, sa->s_un.s_in6.sa_addr, 16); + return (sizeof (*sin6)); + } + return (-1); +} + + +static int +nni_plat_from_sockaddr(nni_sockaddr *sa, const struct sockaddr *ss) +{ + const struct sockaddr_in *sin; + const struct sockaddr_in6 *sin6; + + memset(sa, 0, sizeof (*sa)); + switch (ss->sa_family) { + case PF_INET: + sin = (const void *) ss; + sa->s_un.s_in.sa_family = NNG_AF_INET; + sa->s_un.s_in.sa_port = sin->sin_port; + sa->s_un.s_in.sa_addr = sin->sin_addr.s_addr; + return (0); + + case PF_INET6: + sin6 = (const void *) ss; + sa->s_un.s_in6.sa_family = NNG_AF_INET6; + sa->s_un.s_in6.sa_port = sin6->sin6_port; + memcpy(sa->s_un.s_in6.sa_addr, sin6->sin6_addr.s6_addr, 16); + return (0); + } + return (-1); +} + + +int +nni_plat_lookup_host(const char *host, nni_sockaddr *addr, int flags) +{ + ADDRINFO hint; + ADDIRINFO *ai; + + memset(&hint, 0, sizeof (hint)); + hint.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + hint.ai_family = PF_UNSPEC; + hint.ai_socktype = SOCK_STREAM; + hint.ai_protocol = IPPROTO_TCP; + if (flags & NNI_FLAG_IPV4ONLY) { + hint.ai_family = PF_INET; + } + + if (getaddrinfo(host, NULL, &hint, &ai) != 0) { + return (NNG_EADDRINVAL); + } + + if (nni_plat_from_sockaddr(addr, ai->ai_addr) < 0) { + freeaddrinfo(ai); + return (NNG_EADDRINVAL); + } + freeaddrinfo(ai); + return (0); +} + + +int +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; + + if (cnt > 4) { + return (NNG_EINVAL); + } + + for (i = 0; i < cnt; 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()); + } + + return (0); +} + + +int +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 nrecv; + + if (cnt > 4) { + return (NNG_EINVAL); + } + + for (i = 0; i < cnt; 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()); + } + if (nrecv > resid) { + nni_panic("readv says it read too much!"); + } + + resid -= nrecv; + while (nrecv) { + if (iov[i].len <= nrecv) { + nrecv -= iov[i].len; + i++; + cnt--; + } else { + iov[i].len -= nrecv; + iov[i].buf += nrecv; + nrecv = 0; + } + } + } + + return (0); +} + + +static void +nni_plat_tcp_setopts(int fd) +{ + int one; + + // Don't inherit the handle (CLOEXEC really). + SetHandleInformation(s->s, HANDLE_FLAG_INHERIT, 0); + + // Also disable Nagle. We are careful to group data with WSASend, + // and latency is king for most of our users. (Consider adding + // a method to enable this later.) + one = 1; + (void) setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof (one)); +} + + +void +nni_plat_tcp_init(nni_plat_tcpsock *s) +{ + s->s = INVALID_SOCKET; +} + + +void +nni_plat_tcp_fini(nni_plat_tcpsock *s) +{ + if (s->s != INVALID_SOCKET) { + (void) closesocket(s->s); + s->s = INVALID_SOCKET; + } +} + + +void +nni_plat_tcp_shutdown(nni_plat_tcpsock *s) +{ + if (s->s != INVALID_SOCKET) { + (void) shutdown(s->fd, SHUT_RDWR); + } +} + + +// nni_plat_tcp_listen creates a file descriptor bound to the given address. +// This basically does the equivalent of socket, bind, and listen. We have +// chosen a default value for the listen backlog of 128, which should be +// plenty. (If it isn't, then the accept thread can't get enough resources +// to keep up, and your clients are going to experience bad things. Normally +// the actual backlog should hover near 0 anyway.) +int +nni_plat_tcp_listen(nni_plat_tcpsock *s, const nni_sockaddr *addr) +{ + int len; + struct sockaddr_storage ss; + int rv; + ULONG one; + + 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()); + } + + nni_plat_tcp_setopts(s->s); + + // Make sure that we use the address exclusively. Windows lets + // others hijack us by default. + one = 1; + if (setsocket(s->s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, &one, + sizeof (one)) == SOCKET_ERROR) { + rv = nni_plat_wsa_last_error(); + (void) closesocket(s->s); + s->s = INVALID_SOCKET; + return (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); + } + + // 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); + } + + return (0); +} + + +// nni_plat_tcp_connect establishes an outbound connection. It the +// bind address is not null, then it will attempt to bind to the local +// address specified first. +int +nni_plat_tcp_connect(nni_plat_tcpsock *s, const nni_sockaddr *addr, + const nni_sockaddr *bindaddr) +{ + int len; + struct sockaddr_storage ss; + struct sockaddr_storage bss; + int rv; + + 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 (bindaddr != NULL) { + if (bindaddr->s_un.s_family != addr->s_un.s_family) { + (void) closesocket(s->s); + s->s = INVALID_SOCKET; + return (NNG_EINVAL); + } + 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(errno); + (void) closesocket(s->s); + s->s = INVALID_SOCKET; + return (rv); + } + } + + 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; + return (rv); + } + return (0); +} + + +int +nni_plat_tcp_accept(nni_plat_tcpsock *s, nni_plat_tcpsock *server) +{ + SOCKET fd; + int err; + + for (;;) { + fd = accept(server->s, NULL, NULL); + + if (fd == INVALID_SOCKET) { + err = WSAGetLastError(); + if (err == WSAECONNRESET || err == WSAEWOULDBLOCK) { + continue; + } + return (nni_plat_wsa_last_error()); + } else { + break; + } + } + + nni_plat_tcp_setopts(fd); + + s->s = fd; + return (0); +} + + +#endif // PLATFORM_WINDOWS |
