summaryrefslogtreecommitdiffstats
path: root/src/foundation/linux/SocketImpl.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/foundation/linux/SocketImpl.h')
-rw-r--r--src/foundation/linux/SocketImpl.h424
1 files changed, 424 insertions, 0 deletions
diff --git a/src/foundation/linux/SocketImpl.h b/src/foundation/linux/SocketImpl.h
new file mode 100644
index 0000000..4c3fcb7
--- /dev/null
+++ b/src/foundation/linux/SocketImpl.h
@@ -0,0 +1,424 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef FOUNDATION_LINUX_SOCKET_IMPL_H
+#define FOUNDATION_LINUX_SOCKET_IMPL_H
+#pragma once
+
+/*=========================================================================*\
+* BSD include files
+\*=========================================================================*/
+/* error codes */
+#include <errno.h>
+/* close function */
+#include <unistd.h>
+/* fnctnl function and associated constants */
+#include <fcntl.h>
+/* struct sockaddr */
+#include <sys/types.h>
+/* socket function */
+#include <sys/socket.h>
+/* struct timeval */
+#include <sys/time.h>
+/* gethostbyname and gethostbyaddr functions */
+#include <netdb.h>
+/* sigpipe handling */
+#include <signal.h>
+/* IP stuff*/
+#include <netinet/in.h>
+#include <arpa/inet.h>
+/* TCP options (nagle algorithm disable) */
+#include <netinet/tcp.h>
+
+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 t_socket;
+ typedef t_socket *p_socket;
+ typedef struct sockaddr SA;
+
+#define SOCKET_INVALID (-1)
+
+#define WAITFD_R 1
+#define WAITFD_W 2
+#define WAITFD_C (WAITFD_R | WAITFD_W)
+
+ int socket_waitfd(p_socket ps, int sw, QT3DSU32 tm)
+ {
+ int ret;
+ fd_set rfds, wfds, *rp, *wp;
+ struct timeval tv, *tp = NULL;
+ if (tm == 0)
+ return IO_TIMEOUT; /* optimize timeout == 0 case */
+ if (tm < QT3DS_MAX_U32) {
+ // shoule consider max timeout specially by setting tp = NULL
+ // or you get invalid argument errno = 22
+ tv.tv_sec = (int)(tm / 1000);
+ QT3DSU32 leftover = tm % 1000;
+ tv.tv_usec = (int)(leftover * 100000);
+ tp = &tv;
+ }
+ do {
+ /* must set bits within loop, because select may have modifed them */
+ rp = wp = NULL;
+ 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;
+ }
+ ret = select(*ps + 1, rp, wp, NULL, tp);
+ } while (ret == -1 && errno == EINTR);
+ if (ret == -1)
+ return errno;
+ if (ret == 0)
+ return IO_TIMEOUT;
+ if (sw == WAITFD_C && FD_ISSET(*ps, &rfds))
+ return IO_CLOSED;
+ return IO_DONE;
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Initializes module
+ \*-------------------------------------------------------------------------*/
+ int socket_open(void)
+ {
+ /* instals a handler to ignore sigpipe or it will crash us */
+ signal(SIGPIPE, SIG_IGN);
+ return 1;
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Close module
+ \*-------------------------------------------------------------------------*/
+ int socket_close(void) { return 1; }
+
+ /*-------------------------------------------------------------------------*\
+ * Put socket into blocking mode
+ \*-------------------------------------------------------------------------*/
+ void socket_setblocking(p_socket ps)
+ {
+ int flags = fcntl(*ps, F_GETFL, 0);
+ flags &= (~(O_NONBLOCK));
+ fcntl(*ps, F_SETFL, flags);
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Put socket into non-blocking mode
+ \*-------------------------------------------------------------------------*/
+ void socket_setnonblocking(p_socket ps)
+ {
+ int flags = fcntl(*ps, F_GETFL, 0);
+ flags |= O_NONBLOCK;
+ fcntl(*ps, F_SETFL, flags);
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Close and inutilize socket
+ \*-------------------------------------------------------------------------*/
+ void socket_destroy(p_socket ps)
+ {
+ if (*ps != SOCKET_INVALID) {
+ socket_setblocking(ps);
+ close(*ps);
+ *ps = SOCKET_INVALID;
+ }
+ }
+ /*-------------------------------------------------------------------------*\
+ *
+ \*-------------------------------------------------------------------------*/
+ void socket_shutdown(p_socket ps, int how)
+ {
+ socket_setblocking(ps);
+ shutdown(*ps, how);
+ socket_setnonblocking(ps);
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Creates and sets up a socket
+ \*-------------------------------------------------------------------------*/
+ 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 errno;
+ }
+
+ /*-------------------------------------------------------------------------*\
+ *
+ \*-------------------------------------------------------------------------*/
+ int socket_listen(p_socket ps, int backlog)
+ {
+ int err = IO_DONE;
+ socket_setblocking(ps);
+ if (listen(*ps, backlog))
+ err = errno;
+ socket_setnonblocking(ps);
+ 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 = errno;
+ socket_setnonblocking(ps);
+ return err;
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Connects or returns error message
+ \*-------------------------------------------------------------------------*/
+ int socket_connect(p_socket ps, SA *addr, socklen_t len, QT3DSU32 tm)
+ {
+ int err;
+ /* avoid calling on closed sockets */
+ if (*ps == SOCKET_INVALID)
+ return IO_CLOSED;
+ /* call connect until done or failed without being interrupted */
+ do
+ if (connect(*ps, addr, len) == 0)
+ return IO_DONE;
+ while ((err = errno) == EINTR);
+ /* if connection failed immediately, return error code */
+ if (err != EINPROGRESS && err != EAGAIN)
+ return err;
+ /* zero timeout case optimization */
+ if (tm == 0)
+ return IO_TIMEOUT;
+ /* wait until we have the result of the connection attempt or timeout */
+ err = socket_waitfd(ps, WAITFD_C, tm);
+ if (err == IO_CLOSED) {
+ if (recv(*ps, (char *)&err, 0, 0) == 0)
+ return IO_DONE;
+ else
+ return errno;
+ } else
+ return err;
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Accept with timeout
+ \*-------------------------------------------------------------------------*/
+ 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;
+ if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID)
+ return IO_DONE;
+ err = errno;
+ if (err == EINTR)
+ continue;
+ if (err != EAGAIN && err != ECONNABORTED)
+ return err;
+ if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE)
+ return err;
+ }
+ /* can't reach here */
+ return IO_UNKNOWN;
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Send with timeout
+ \*-------------------------------------------------------------------------*/
+ int socket_send(p_socket ps, const char *data, size_t count, size_t *sent, QT3DSU32 tm)
+ {
+ 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 (;;) {
+ long put = (long)send(*ps, data, count, 0);
+ /* if we sent anything, we are done */
+ if (put > 0) {
+ *sent = put;
+ return IO_DONE;
+ }
+ err = errno;
+ /* send can't really return 0, but EPIPE means the connection was closed */
+ if (put == 0 || err == EPIPE)
+ return IO_CLOSED;
+ /* we call was interrupted, just try again */
+ if (err == EINTR)
+ continue;
+ /* if failed fatal reason, report error */
+ if (err != EAGAIN)
+ return err;
+ /* wait until we can send something or we timeout */
+ if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE)
+ return err;
+ }
+ /* can't reach here */
+ return IO_UNKNOWN;
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Receive with timeout
+ \*-------------------------------------------------------------------------*/
+ int socket_recv(p_socket ps, char *data, size_t count, size_t *got, QT3DSU32 tm)
+ {
+ int err;
+ *got = 0;
+ if (*ps == SOCKET_INVALID)
+ return IO_CLOSED;
+ for (;;) {
+ long taken = (long)recv(*ps, data, count, 0);
+ if (taken > 0) {
+ *got = taken;
+ return IO_DONE;
+ }
+ err = errno;
+ if (taken == 0)
+ return IO_CLOSED;
+ if (err == EINTR)
+ continue;
+ if (err != EAGAIN)
+ return err;
+ if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE)
+ return err;
+ }
+ 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 if (h_errno)
+ return h_errno;
+ else if (errno)
+ return errno;
+ else
+ return IO_UNKNOWN;
+ }
+
+ int socket_gethostbyname(const char *addr, struct hostent **hp)
+ {
+ *hp = gethostbyname(addr);
+ if (*hp)
+ return IO_DONE;
+ else if (h_errno)
+ return h_errno;
+ else if (errno)
+ return errno;
+ else
+ return IO_UNKNOWN;
+ }
+
+ /*-------------------------------------------------------------------------*\
+ * Error translation functions
+ * Make sure important error messages are standard
+ \*-------------------------------------------------------------------------*/
+ const char *socket_hoststrerror(int err)
+ {
+ if (err <= 0)
+ return io_strerror(err);
+ switch (err) {
+ case HOST_NOT_FOUND:
+ return "host not found";
+ default:
+ return hstrerror(err);
+ }
+ }
+
+ const char *socket_strerror(int err)
+ {
+ if (err <= 0)
+ return io_strerror(err);
+ switch (err) {
+ case EADDRINUSE:
+ return "address already in use";
+ case EISCONN:
+ return "already connected";
+ case EACCES:
+ return "permission denied";
+ case ECONNREFUSED:
+ return "connection refused";
+ case ECONNABORTED:
+ return "closed";
+ case ECONNRESET:
+ return "closed";
+ case ETIMEDOUT:
+ return "timeout";
+ default:
+ return strerror(errno);
+ }
+ }
+ }
+}
+}
+
+#endif