summaryrefslogtreecommitdiffstats
path: root/src/foundation/windows/SocketImpl.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/foundation/windows/SocketImpl.h')
-rw-r--r--src/foundation/windows/SocketImpl.h506
1 files changed, 506 insertions, 0 deletions
diff --git a/src/foundation/windows/SocketImpl.h b/src/foundation/windows/SocketImpl.h
new file mode 100644
index 0000000..f72203b
--- /dev/null
+++ b/src/foundation/windows/SocketImpl.h
@@ -0,0 +1,506 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+LuaSocket 3.0 license
+Copyright � 2004-2013 Diego Nehab
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef FOUNDATION_WINDOWS_SOCKET_IMPL_H
+#define FOUNDATION_WINDOWS_SOCKET_IMPL_H
+#pragma once
+#include <winsock.h>
+#pragma warning(disable : 4127)
+#pragma warning(disable : 4702)
+
+namespace qt3ds {
+namespace foundation {
+ namespace socketimpl {
+
+ // Functions take from lua socket implementation. Note that it has an MIT license.
+
+ enum {
+ IO_DONE = 0, /* operation completed successfully */
+ IO_TIMEOUT = -1, /* operation timed out */
+ IO_CLOSED = -2, /* the connection has been closed */
+ IO_UNKNOWN = -3
+ };
+
+ /*-------------------------------------------------------------------------*\
+ * I/O error strings
+ \*-------------------------------------------------------------------------*/
+ const char *io_strerror(int err)
+ {
+ switch (err) {
+ case IO_DONE:
+ return NULL;
+ case IO_CLOSED:
+ return "closed";
+ case IO_TIMEOUT:
+ return "timeout";
+ default:
+ return "unknown error";
+ }
+ }
+
+ typedef int socklen_t;
+ typedef SOCKET t_socket;
+ typedef t_socket *p_socket;
+ typedef struct sockaddr SA;
+
+#define SOCKET_INVALID (INVALID_SOCKET)
+
+ static const char *wstrerror(int err)
+ {
+ switch (err) {
+ case WSAEINTR:
+ return "Interrupted function call";
+ case WSAEACCES:
+ return "Permission denied";
+ case WSAEFAULT:
+ return "Bad address";
+ case WSAEINVAL:
+ return "Invalid argument";
+ case WSAEMFILE:
+ return "Too many open files";
+ case WSAEWOULDBLOCK:
+ return "Resource temporarily unavailable";
+ case WSAEINPROGRESS:
+ return "Operation now in progress";
+ case WSAEALREADY:
+ return "Operation already in progress";
+ case WSAENOTSOCK:
+ return "Socket operation on nonsocket";
+ case WSAEDESTADDRREQ:
+ return "Destination address required";
+ case WSAEMSGSIZE:
+ return "Message too long";
+ case WSAEPROTOTYPE:
+ return "Protocol wrong type for socket";
+ case WSAENOPROTOOPT:
+ return "Bad protocol option";
+ case WSAEPROTONOSUPPORT:
+ return "Protocol not supported";
+ case WSAESOCKTNOSUPPORT:
+ return "Socket type not supported";
+ case WSAEOPNOTSUPP:
+ return "Operation not supported";
+ case WSAEPFNOSUPPORT:
+ return "Protocol family not supported";
+ case WSAEAFNOSUPPORT:
+ return "Address family not supported by protocol family";
+ case WSAEADDRINUSE:
+ return "Address already in use";
+ case WSAEADDRNOTAVAIL:
+ return "Cannot assign requested address";
+ case WSAENETDOWN:
+ return "Network is down";
+ case WSAENETUNREACH:
+ return "Network is unreachable";
+ case WSAENETRESET:
+ return "Network dropped connection on reset";
+ case WSAECONNABORTED:
+ return "Software caused connection abort";
+ case WSAECONNRESET:
+ return "Connection reset by peer";
+ case WSAENOBUFS:
+ return "No buffer space available";
+ case WSAEISCONN:
+ return "Socket is already connected";
+ case WSAENOTCONN:
+ return "Socket is not connected";
+ case WSAESHUTDOWN:
+ return "Cannot send after socket shutdown";
+ case WSAETIMEDOUT:
+ return "Connection timed out";
+ case WSAECONNREFUSED:
+ return "Connection refused";
+ case WSAEHOSTDOWN:
+ return "Host is down";
+ case WSAEHOSTUNREACH:
+ return "No route to host";
+ case WSAEPROCLIM:
+ return "Too many processes";
+ case WSASYSNOTREADY:
+ return "Network subsystem is unavailable";
+ case WSAVERNOTSUPPORTED:
+ return "Winsock.dll version out of range";
+ case WSANOTINITIALISED:
+ return "Successful WSAStartup not yet performed";
+ case WSAEDISCON:
+ return "Graceful shutdown in progress";
+ case WSAHOST_NOT_FOUND:
+ return "Host not found";
+ case WSATRY_AGAIN:
+ return "Nonauthoritative host not found";
+ case WSANO_RECOVERY:
+ return "Nonrecoverable name lookup error";
+ case WSANO_DATA:
+ return "Valid name, no data record of requested type";
+ default:
+ return "Unknown error";
+ }
+ }
+
+ const char *socket_strerror(int err)
+ {
+ switch (err) {
+ case WSAEADDRINUSE:
+ return "address already in use";
+ case WSAECONNREFUSED:
+ return "connection refused";
+ case WSAEISCONN:
+ return "already connected";
+ case WSAEACCES:
+ return "permission denied";
+ case WSAECONNABORTED:
+ return "closed";
+ case WSAECONNRESET:
+ return "closed";
+ case WSAETIMEDOUT:
+ return "timeout";
+ default:
+ return wstrerror(err);
+ }
+ }
+
+ int socket_open(void)
+ {
+ WSADATA wsaData;
+ WORD wVersionRequested = MAKEWORD(2, 0);
+ int err = WSAStartup(wVersionRequested, &wsaData);
+ if (err != 0)
+ return 0;
+ if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0)
+ && (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) {
+ WSACleanup();
+ return 0;
+ }
+ return 1;
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Close module
+ \*-------------------------------------------------------------------------*/
+ int socket_close(void)
+ {
+ WSACleanup();
+ return 1;
+ }
+
+/*-------------------------------------------------------------------------*\
+* Wait for readable/writable/connected socket with timeout
+\*-------------------------------------------------------------------------*/
+#define WAITFD_R 1
+#define WAITFD_W 2
+#define WAITFD_E 4
+#define WAITFD_C (WAITFD_E | WAITFD_W)
+
+ static int socket_waitfd(p_socket ps, int sw, QT3DSU32 timeoutMilliseconds)
+ {
+ int ret;
+ fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL;
+ struct timeval tv, *tp = NULL;
+ if (timeoutMilliseconds == 0)
+ return IO_TIMEOUT; /* optimize timeout == 0 case */
+ if (sw & WAITFD_R) {
+ FD_ZERO(&rfds);
+ FD_SET(*ps, &rfds);
+ rp = &rfds;
+ }
+ if (sw & WAITFD_W) {
+ FD_ZERO(&wfds);
+ FD_SET(*ps, &wfds);
+ wp = &wfds;
+ }
+ if (sw & WAITFD_C) {
+ FD_ZERO(&efds);
+ FD_SET(*ps, &efds);
+ ep = &efds;
+ }
+ if (timeoutMilliseconds >= 0.0) {
+ tv.tv_sec = (int)(timeoutMilliseconds / 1000);
+ QT3DSU32 leftover = timeoutMilliseconds % 1000;
+ tv.tv_usec = (int)(leftover * 100000);
+ tp = &tv;
+ }
+ ret = select(0, rp, wp, ep, tp);
+ if (ret == -1)
+ return WSAGetLastError();
+ if (ret == 0)
+ return IO_TIMEOUT;
+ if (sw == WAITFD_C && FD_ISSET(*ps, &efds))
+ return IO_CLOSED;
+ return IO_DONE;
+ }
+
+ static int socket_send(p_socket ps, const char *data, size_t count, size_t *sent,
+ QT3DSU32 timeoutMilliseconds)
+ {
+ int err;
+ *sent = 0;
+ /* avoid making system calls on closed sockets */
+ if (*ps == SOCKET_INVALID)
+ return IO_CLOSED;
+ /* loop until we send something or we give up on error */
+ for (;;) {
+ /* try to send something */
+ int put = send(*ps, data, (int)count, 0);
+ /* if we sent something, we are done */
+ if (put > 0) {
+ *sent = put;
+ return IO_DONE;
+ }
+ /* deal with failure */
+ err = WSAGetLastError();
+ /* we can only proceed if there was no serious error */
+ if (err != WSAEWOULDBLOCK)
+ return err;
+ /* avoid busy wait */
+ if ((err = socket_waitfd(ps, WAITFD_W, timeoutMilliseconds)) != IO_DONE)
+ return err;
+ }
+ /* can't reach here */
+ return IO_UNKNOWN;
+ }
+ /*-------------------------------------------------------------------------*\
+ * Receive with timeout
+ \*-------------------------------------------------------------------------*/
+ static int socket_recv(p_socket ps, char *data, size_t count, size_t *got,
+ QT3DSU32 timeoutMilliseconds)
+ {
+ int err;
+ *got = 0;
+ if (*ps == SOCKET_INVALID)
+ return IO_CLOSED;
+ for (;;) {
+ int taken = recv(*ps, data, (int)count, 0);
+ if (taken > 0) {
+ *got = taken;
+ return IO_DONE;
+ }
+ if (taken == 0)
+ return IO_CLOSED;
+ err = WSAGetLastError();
+ if (err != WSAEWOULDBLOCK)
+ return err;
+ if ((err = socket_waitfd(ps, WAITFD_R, timeoutMilliseconds)) != IO_DONE)
+ return err;
+ }
+ return IO_UNKNOWN;
+ }
+
+ void socket_setblocking(p_socket ps)
+ {
+ u_long argp = 0;
+ ioctlsocket(*ps, FIONBIO, &argp);
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Put socket into non-blocking mode
+ \*-------------------------------------------------------------------------*/
+ void socket_setnonblocking(p_socket ps)
+ {
+ u_long argp = 1;
+ ioctlsocket(*ps, FIONBIO, &argp);
+ }
+
+ int socket_listen(p_socket ps, int backlog)
+ {
+ int err = IO_DONE;
+ socket_setblocking(ps);
+ if (listen(*ps, backlog) < 0)
+ err = WSAGetLastError();
+ socket_setnonblocking(ps);
+ return err;
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Accept with timeout
+ \*-------------------------------------------------------------------------*/
+ static int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, QT3DSU32 tm)
+ {
+ SA daddr;
+ socklen_t dlen = sizeof(daddr);
+ if (*ps == SOCKET_INVALID)
+ return IO_CLOSED;
+ if (!addr)
+ addr = &daddr;
+ if (!len)
+ len = &dlen;
+ for (;;) {
+ int err;
+ /* try to get client socket */
+ if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID)
+ return IO_DONE;
+ /* find out why we failed */
+ err = WSAGetLastError();
+ /* if we failed because there was no connectoin, keep trying */
+ if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED)
+ return err;
+ /* call select to avoid busy wait */
+ if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE)
+ return err;
+ }
+ /* can't reach here */
+ return IO_UNKNOWN;
+ }
+
+ int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp)
+ {
+ *hp = gethostbyaddr(addr, len, AF_INET);
+ if (*hp)
+ return IO_DONE;
+ else
+ return WSAGetLastError();
+ }
+
+ int socket_gethostbyname(const char *addr, struct hostent **hp)
+ {
+ *hp = gethostbyname(addr);
+ if (*hp)
+ return IO_DONE;
+ else
+ return WSAGetLastError();
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Error translation functions
+ \*-------------------------------------------------------------------------*/
+ const char *socket_hoststrerror(int err)
+ {
+ if (err <= 0)
+ return io_strerror(err);
+ switch (err) {
+ case WSAHOST_NOT_FOUND:
+ return "host not found";
+ default:
+ return wstrerror(err);
+ }
+ }
+ /*-------------------------------------------------------------------------*\
+ * Close and inutilize socket
+ \*-------------------------------------------------------------------------*/
+ void socket_destroy(p_socket ps)
+ {
+ if (*ps != SOCKET_INVALID) {
+ socket_setblocking(ps); /* close can take a long time on WIN32 */
+ closesocket(*ps);
+ *ps = SOCKET_INVALID;
+ }
+ }
+
+ /*-------------------------------------------------------------------------*\
+ *
+ \*-------------------------------------------------------------------------*/
+ void socket_shutdown(p_socket ps, int how)
+ {
+ socket_setblocking(ps);
+ shutdown(*ps, how);
+ }
+
+ int socket_create(p_socket ps, int domain, int type, int protocol)
+ {
+ *ps = socket(domain, type, protocol);
+ if (*ps != SOCKET_INVALID)
+ return IO_DONE;
+ else
+ return WSAGetLastError();
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Connects or returns error message
+ \*-------------------------------------------------------------------------*/
+ int socket_connect(p_socket ps, SA *addr, socklen_t len, QT3DSU32 tm)
+ {
+ int err;
+ /* don't call on closed socket */
+ if (*ps == SOCKET_INVALID)
+ return IO_CLOSED;
+ /* ask system to connect */
+ if (connect(*ps, addr, len) == 0)
+ return IO_DONE;
+ /* make sure the system is trying to connect */
+ err = WSAGetLastError();
+ if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS)
+ return err;
+ /* zero timeout case optimization */
+ if (tm == 0)
+ return IO_TIMEOUT;
+ /* we wait until something happens */
+ err = socket_waitfd(ps, WAITFD_C, tm);
+ if (err == IO_CLOSED) {
+ int len = sizeof(err);
+ /* give windows time to set the error (yes, disgusting) */
+ Sleep(10);
+ /* find out why we failed */
+ getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &len);
+ /* we KNOW there was an error. if 'why' is 0, we will return
+ * "unknown error", but it's not really our fault */
+ return err > 0 ? err : IO_UNKNOWN;
+ } else
+ return err;
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Binds or returns error message
+ \*-------------------------------------------------------------------------*/
+ int socket_bind(p_socket ps, SA *addr, socklen_t len)
+ {
+ int err = IO_DONE;
+ socket_setblocking(ps);
+ if (bind(*ps, addr, len) < 0)
+ err = WSAGetLastError();
+ socket_setnonblocking(ps);
+ return err;
+ }
+ }
+}
+}
+
+#endif \ No newline at end of file