summaryrefslogtreecommitdiff
path: root/3rdparty/plibsys/src/psocket.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/plibsys/src/psocket.c')
-rw-r--r--3rdparty/plibsys/src/psocket.c1644
1 files changed, 1644 insertions, 0 deletions
diff --git a/3rdparty/plibsys/src/psocket.c b/3rdparty/plibsys/src/psocket.c
new file mode 100644
index 0000000..fb04379
--- /dev/null
+++ b/3rdparty/plibsys/src/psocket.c
@@ -0,0 +1,1644 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ * Some workarounds have been used from Glib (comments are kept)
+ *
+ * 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.
+ */
+
+#include "pmem.h"
+#include "psocket.h"
+#ifdef P_OS_SCO
+# include "ptimeprofiler.h"
+#endif
+#include "perror-private.h"
+#include "plibsys-private.h"
+#include "psysclose-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef P_OS_WIN
+# include <fcntl.h>
+# include <errno.h>
+# include <unistd.h>
+# include <signal.h>
+# ifdef P_OS_VMS
+# include <stropts.h>
+# endif
+#endif
+
+#ifndef P_OS_WIN
+# if defined (P_OS_BEOS) || defined (P_OS_MAC) || defined (P_OS_MAC9) || \
+ defined (P_OS_OS2) || defined (P_OS_AMIGA)
+# define P_SOCKET_USE_SELECT
+# include <sys/select.h>
+# include <sys/time.h>
+# else
+# define P_SOCKET_USE_POLL
+# include <sys/poll.h>
+# endif
+#endif
+
+/* On old Solaris systems SOMAXCONN is set to 5 */
+#define P_SOCKET_DEFAULT_BACKLOG 5
+
+struct PSocket_ {
+ PSocketFamily family;
+ PSocketProtocol protocol;
+ PSocketType type;
+ pint fd;
+ pint listen_backlog;
+ pint timeout;
+ puint blocking : 1;
+ puint keepalive : 1;
+ puint closed : 1;
+ puint connected : 1;
+ puint listening : 1;
+#ifdef P_OS_WIN
+ WSAEVENT events;
+#endif
+#ifdef P_OS_SCO
+ PTimeProfiler *timer;
+#endif
+};
+
+#ifndef SHUT_RD
+# define SHUT_RD 0
+#endif
+
+#ifndef SHUT_WR
+# define SHUT_WR 1
+#endif
+
+#ifndef SHUT_RDWR
+# define SHUT_RDWR 2
+#endif
+
+#ifdef MSG_NOSIGNAL
+# define P_SOCKET_DEFAULT_SEND_FLAGS MSG_NOSIGNAL
+#else
+# define P_SOCKET_DEFAULT_SEND_FLAGS 0
+#endif
+
+static pboolean pp_socket_set_fd_blocking (pint fd, pboolean blocking, PError **error);
+static pboolean pp_socket_check (const PSocket *socket, PError **error);
+static pboolean pp_socket_set_details_from_fd (PSocket *socket, PError **error);
+
+static pboolean
+pp_socket_set_fd_blocking (pint fd,
+ pboolean blocking,
+ PError **error)
+{
+#ifndef P_OS_WIN
+ pint32 arg;
+#else
+ pulong arg;
+#endif
+
+#ifndef P_OS_WIN
+# ifdef P_OS_VMS
+ arg = !blocking;
+# if (PLIBSYS_SIZEOF_VOID_P == 8)
+# pragma __pointer_size 32
+# endif
+ /* Explicit (void *) cast is necessary */
+ if (P_UNLIKELY (ioctl (fd, FIONBIO, (void *) &arg) < 0)) {
+# if (PLIBSYS_SIZEOF_VOID_P == 8)
+# pragma __pointer_size 64
+# endif
+# else
+ if (P_UNLIKELY ((arg = fcntl (fd, F_GETFL, NULL)) < 0)) {
+ P_WARNING ("PSocket::pp_socket_set_fd_blocking: fcntl() failed");
+ arg = 0;
+ }
+
+ arg = (!blocking) ? (arg | O_NONBLOCK) : (arg & ~O_NONBLOCK);
+
+ if (P_UNLIKELY (fcntl (fd, F_SETFL, arg) < 0)) {
+# endif
+#else
+ arg = !blocking;
+
+ if (P_UNLIKELY (ioctlsocket (fd, FIONBIO, &arg) == SOCKET_ERROR)) {
+#endif
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to set socket blocking flags");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static pboolean
+pp_socket_check (const PSocket *socket,
+ PError **error)
+{
+ if (P_UNLIKELY (socket->closed)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_NOT_AVAILABLE,
+ 0,
+ "Socket is already closed");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static pboolean
+pp_socket_set_details_from_fd (PSocket *socket,
+ PError **error)
+{
+#ifdef SO_DOMAIN
+ PSocketFamily family;
+#endif
+ struct sockaddr_storage address;
+ pint fd, value;
+ socklen_t addrlen, optlen;
+#ifdef P_OS_WIN
+ /* See comment below */
+ BOOL bool_val = FALSE;
+#else
+ pint bool_val;
+#endif
+
+ fd = socket->fd;
+ optlen = sizeof (value);
+
+ if (P_UNLIKELY (getsockopt (fd, SOL_SOCKET, SO_TYPE, (ppointer) &value, &optlen) != 0)) {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call getsockopt() to get socket info for fd");
+ return FALSE;
+ }
+
+ if (P_UNLIKELY (optlen != sizeof (value))) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Failed to get socket info for fd, bad option length");
+ return FALSE;
+ }
+
+ switch (value) {
+ case SOCK_STREAM:
+ socket->type = P_SOCKET_TYPE_STREAM;
+ break;
+
+ case SOCK_DGRAM:
+ socket->type = P_SOCKET_TYPE_DATAGRAM;
+ break;
+
+#ifdef SOCK_SEQPACKET
+ case SOCK_SEQPACKET:
+ socket->type = P_SOCKET_TYPE_SEQPACKET;
+ break;
+#endif
+
+ default:
+ socket->type = P_SOCKET_TYPE_UNKNOWN;
+ break;
+ }
+
+ addrlen = sizeof (address);
+
+ if (P_UNLIKELY (getsockname (fd, (struct sockaddr *) &address, &addrlen) != 0)) {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call getsockname() to get socket address info");
+ return FALSE;
+ }
+
+#ifdef SO_DOMAIN
+ if (!(addrlen > 0)) {
+ optlen = sizeof (family);
+
+ if (P_UNLIKELY (getsockopt (socket->fd,
+ SOL_SOCKET,
+ SO_DOMAIN,
+ (ppointer) &family,
+ &optlen) != 0)) {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call getsockopt() to get socket SO_DOMAIN option");
+ return FALSE;
+ }
+ }
+#endif
+
+ switch (address.ss_family) {
+ case P_SOCKET_FAMILY_INET:
+ socket->family = P_SOCKET_FAMILY_INET;
+ break;
+#ifdef AF_INET6
+ case P_SOCKET_FAMILY_INET6:
+ socket->family = P_SOCKET_FAMILY_INET6;
+ break;
+#endif
+ default:
+ socket->family = P_SOCKET_FAMILY_UNKNOWN;
+ break;
+ }
+
+#ifdef AF_INET6
+ if (socket->family == P_SOCKET_FAMILY_INET6 || socket->family == P_SOCKET_FAMILY_INET) {
+#else
+ if (socket->family == P_SOCKET_FAMILY_INET) {
+#endif
+ switch (socket->type) {
+ case P_SOCKET_TYPE_STREAM:
+ socket->protocol = P_SOCKET_PROTOCOL_TCP;
+ break;
+ case P_SOCKET_TYPE_DATAGRAM:
+ socket->protocol = P_SOCKET_PROTOCOL_UDP;
+ break;
+ case P_SOCKET_TYPE_SEQPACKET:
+ socket->protocol = P_SOCKET_PROTOCOL_SCTP;
+ break;
+ case P_SOCKET_TYPE_UNKNOWN:
+ break;
+ }
+ }
+
+ if (P_LIKELY (socket->family != P_SOCKET_FAMILY_UNKNOWN)) {
+ addrlen = sizeof (address);
+
+ if (getpeername (fd, (struct sockaddr *) &address, &addrlen) >= 0)
+ socket->connected = TRUE;
+ }
+
+ optlen = sizeof (bool_val);
+
+ if (getsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, (ppointer) &bool_val, &optlen) == 0) {
+#ifndef P_OS_WIN
+ /* Experimentation indicates that the SO_KEEPALIVE value is
+ * actually a char on Windows, even if documentation claims it
+ * to be a BOOL which is a typedef for int. */
+ if (optlen != sizeof (bool_val))
+ P_WARNING ("PSocket::pp_socket_set_details_from_fd: getsockopt() with SO_KEEPALIVE failed");
+#endif
+ socket->keepalive = !!bool_val;
+ } else
+ /* Can't read, maybe not supported, assume FALSE */
+ socket->keepalive = FALSE;
+
+ return TRUE;
+}
+
+pboolean
+p_socket_init_once (void)
+{
+#ifdef P_OS_WIN
+ WORD ver_req;
+ WSADATA wsa_data;
+
+ ver_req = MAKEWORD (2, 2);
+
+ if (P_UNLIKELY (WSAStartup (ver_req, &wsa_data) != 0))
+ return FALSE;
+
+ if (P_UNLIKELY (LOBYTE (wsa_data.wVersion) != 2 || HIBYTE (wsa_data.wVersion) != 2)) {
+ WSACleanup ();
+ return FALSE;
+ }
+#else
+# ifdef SIGPIPE
+ signal (SIGPIPE, SIG_IGN);
+# endif
+#endif
+ return TRUE;
+}
+
+void
+p_socket_close_once (void)
+{
+#ifdef P_OS_WIN
+ WSACleanup ();
+#endif
+}
+
+P_LIB_API PSocket *
+p_socket_new_from_fd (pint fd,
+ PError **error)
+{
+ PSocket *ret;
+#if !defined (P_OS_WIN) && defined (SO_NOSIGPIPE)
+ pint flags;
+#endif
+
+ if (P_UNLIKELY (fd < 0)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Unable to create socket from bad fd");
+ return NULL;
+ }
+
+ if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocket))) == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_NO_RESOURCES,
+ 0,
+ "Failed to allocate memory for socket");
+ return NULL;
+ }
+
+ ret->fd = fd;
+
+ if (P_UNLIKELY (pp_socket_set_details_from_fd (ret, error) == FALSE)) {
+ p_free (ret);
+ return NULL;
+ }
+
+ if (P_UNLIKELY (pp_socket_set_fd_blocking (ret->fd, FALSE, error) == FALSE)) {
+ p_free (ret);
+ return NULL;
+ }
+
+#if !defined (P_OS_WIN) && defined (SO_NOSIGPIPE)
+ flags = 1;
+
+ if (setsockopt (ret->fd, SOL_SOCKET, SO_NOSIGPIPE, &flags, sizeof (flags)) < 0)
+ P_WARNING ("PSocket::p_socket_new_from_fd: setsockopt() with SO_NOSIGPIPE failed");
+#endif
+
+ p_socket_set_listen_backlog (ret, P_SOCKET_DEFAULT_BACKLOG);
+
+ ret->timeout = 0;
+ ret->blocking = TRUE;
+
+#ifdef P_OS_SCO
+ if (P_UNLIKELY ((ret->timer = p_time_profiler_new ()) == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_NO_RESOURCES,
+ 0,
+ "Failed to allocate memory for internal timer");
+ p_free (ret);
+ return NULL;
+ }
+#endif
+
+#ifdef P_OS_WIN
+ if (P_UNLIKELY ((ret->events = WSACreateEvent ()) == WSA_INVALID_EVENT)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_FAILED,
+ (pint) p_error_get_last_net (),
+ "Failed to call WSACreateEvent() on socket");
+ p_free (ret);
+ return NULL;
+ }
+#endif
+
+ return ret;
+}
+
+P_LIB_API PSocket *
+p_socket_new (PSocketFamily family,
+ PSocketType type,
+ PSocketProtocol protocol,
+ PError **error)
+{
+ PSocket *ret;
+ pint native_type, fd;
+#ifndef P_OS_WIN
+ pint flags;
+#endif
+
+ if (P_UNLIKELY (family == P_SOCKET_FAMILY_UNKNOWN ||
+ type == P_SOCKET_TYPE_UNKNOWN ||
+ protocol == P_SOCKET_PROTOCOL_UNKNOWN)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input socket family, type or protocol");
+ return NULL;
+ }
+
+ switch (type) {
+ case P_SOCKET_TYPE_STREAM:
+ native_type = SOCK_STREAM;
+ break;
+
+ case P_SOCKET_TYPE_DATAGRAM:
+ native_type = SOCK_DGRAM;
+ break;
+
+#ifdef SOCK_SEQPACKET
+ case P_SOCKET_TYPE_SEQPACKET:
+ native_type = SOCK_SEQPACKET;
+ break;
+#endif
+
+ default:
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Unable to create socket with unknown family");
+ return NULL;
+ }
+
+ if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocket))) == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_NO_RESOURCES,
+ 0,
+ "Failed to allocate memory for socket");
+ return NULL;
+ }
+
+#ifdef P_OS_SCO
+ if (P_UNLIKELY ((ret->timer = p_time_profiler_new ()) == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_NO_RESOURCES,
+ 0,
+ "Failed to allocate memory for internal timer");
+ p_free (ret);
+ return NULL;
+ }
+#endif
+
+#ifdef SOCK_CLOEXEC
+ native_type |= SOCK_CLOEXEC;
+#endif
+ if (P_UNLIKELY ((fd = (pint) socket (family, native_type, protocol)) < 0)) {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call socket() to create socket");
+#ifdef P_OS_SCO
+ p_time_profiler_free (ret->timer);
+#endif
+ p_free (ret);
+ return NULL;
+ }
+
+#ifndef P_OS_WIN
+ flags = fcntl (fd, F_GETFD, 0);
+
+ if (P_LIKELY (flags != -1 && (flags & FD_CLOEXEC) == 0)) {
+ flags |= FD_CLOEXEC;
+
+ if (P_UNLIKELY (fcntl (fd, F_SETFD, flags) < 0))
+ P_WARNING ("PSocket::p_socket_new: fcntl() with FD_CLOEXEC failed");
+ }
+#endif
+
+ ret->fd = fd;
+
+#ifdef P_OS_WIN
+ ret->events = WSA_INVALID_EVENT;
+#endif
+
+ if (P_UNLIKELY (pp_socket_set_fd_blocking (ret->fd, FALSE, error) == FALSE)) {
+ p_socket_free (ret);
+ return NULL;
+ }
+
+#if !defined (P_OS_WIN) && defined (SO_NOSIGPIPE)
+ flags = 1;
+
+ if (setsockopt (ret->fd, SOL_SOCKET, SO_NOSIGPIPE, &flags, sizeof (flags)) < 0)
+ P_WARNING ("PSocket::p_socket_new: setsockopt() with SO_NOSIGPIPE failed");
+#endif
+
+ ret->timeout = 0;
+ ret->blocking = TRUE;
+ ret->family = family;
+ ret->protocol = protocol;
+ ret->type = type;
+
+ p_socket_set_listen_backlog (ret, P_SOCKET_DEFAULT_BACKLOG);
+
+#ifdef P_OS_WIN
+ if (P_UNLIKELY ((ret->events = WSACreateEvent ()) == WSA_INVALID_EVENT)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_FAILED,
+ (pint) p_error_get_last_net (),
+ "Failed to call WSACreateEvent() on socket");
+ p_socket_free (ret);
+ return NULL;
+ }
+#endif
+
+ return ret;
+}
+
+P_LIB_API pint
+p_socket_get_fd (const PSocket *socket)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return -1;
+
+ return socket->fd;
+}
+
+P_LIB_API PSocketFamily
+p_socket_get_family (const PSocket *socket)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return P_SOCKET_FAMILY_UNKNOWN;
+
+ return socket->family;
+}
+
+P_LIB_API PSocketType
+p_socket_get_type (const PSocket *socket)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return P_SOCKET_TYPE_UNKNOWN;
+
+ return socket->type;
+}
+
+P_LIB_API PSocketProtocol
+p_socket_get_protocol (const PSocket *socket)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return P_SOCKET_PROTOCOL_UNKNOWN;
+
+ return socket->protocol;
+}
+
+P_LIB_API pboolean
+p_socket_get_keepalive (const PSocket *socket)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return FALSE;
+
+ return socket->keepalive;
+}
+
+P_LIB_API pboolean
+p_socket_get_blocking (PSocket *socket)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return FALSE;
+
+ return socket->blocking;
+}
+
+P_LIB_API int
+p_socket_get_listen_backlog (const PSocket *socket)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return -1;
+
+ return socket->listen_backlog;
+}
+
+P_LIB_API pint
+p_socket_get_timeout (const PSocket *socket)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return -1;
+
+ return socket->timeout;
+}
+
+P_LIB_API PSocketAddress *
+p_socket_get_local_address (const PSocket *socket,
+ PError **error)
+{
+ struct sockaddr_storage buffer;
+ socklen_t len;
+ PSocketAddress *ret;
+
+ if (P_UNLIKELY (socket == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return NULL;
+ }
+
+ len = sizeof (buffer);
+
+ if (P_UNLIKELY (getsockname (socket->fd, (struct sockaddr *) &buffer, &len) < 0)) {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call getsockname() to get local socket address");
+ return NULL;
+ }
+
+ ret = p_socket_address_new_from_native (&buffer, (psize) len);
+
+ if (P_UNLIKELY (ret == NULL))
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_FAILED,
+ 0,
+ "Failed to create socket address from native structure");
+
+ return ret;
+}
+
+P_LIB_API PSocketAddress *
+p_socket_get_remote_address (const PSocket *socket,
+ PError **error)
+{
+ struct sockaddr_storage buffer;
+ socklen_t len;
+ PSocketAddress *ret;
+
+ if (P_UNLIKELY (socket == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return NULL;
+ }
+
+ len = sizeof (buffer);
+
+ if (P_UNLIKELY (getpeername (socket->fd, (struct sockaddr *) &buffer, &len) < 0)) {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call getpeername() to get remote socket address");
+ return NULL;
+ }
+
+#ifdef P_OS_SYLLABLE
+ /* Syllable has a bug with a wrong byte order for a TCP port,
+ * as it only supports IPv4 we can easily fix it here. */
+ ((struct sockaddr_in *) &buffer)->sin_port =
+ p_htons (((struct sockaddr_in *) &buffer)->sin_port);
+#endif
+
+ ret = p_socket_address_new_from_native (&buffer, (psize) len);
+
+ if (P_UNLIKELY (ret == NULL))
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_FAILED,
+ 0,
+ "Failed to create socket address from native structure");
+
+ return ret;
+}
+
+P_LIB_API pboolean
+p_socket_is_connected (const PSocket *socket)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return FALSE;
+
+ return socket->connected;
+}
+
+P_LIB_API pboolean
+p_socket_is_closed (const PSocket *socket)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return TRUE;
+
+ return socket->closed;
+}
+
+P_LIB_API pboolean
+p_socket_check_connect_result (PSocket *socket,
+ PError **error)
+{
+ socklen_t optlen;
+ pint val;
+
+ if (P_UNLIKELY (socket == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return FALSE;
+ }
+
+ optlen = sizeof (val);
+
+ if (P_UNLIKELY (getsockopt (socket->fd, SOL_SOCKET, SO_ERROR, (ppointer) &val, &optlen) < 0)) {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call getsockopt() to get connection status");
+ return FALSE;
+ }
+
+ if (P_UNLIKELY (val != 0))
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (val),
+ val,
+ "Error in socket layer");
+
+ socket->connected = (val == 0);
+
+ return (val == 0);
+}
+
+P_LIB_API void
+p_socket_set_keepalive (PSocket *socket,
+ pboolean keepalive)
+{
+#ifdef P_OS_WIN
+ pchar value;
+#else
+ pint value;
+#endif
+
+ if (P_UNLIKELY (socket == NULL))
+ return;
+
+ if (socket->keepalive == (puint) !!keepalive)
+ return;
+
+#ifdef P_OS_WIN
+ value = !! (pchar) keepalive;
+#else
+ value = !! (pint) keepalive;
+#endif
+ if (setsockopt (socket->fd, SOL_SOCKET, SO_KEEPALIVE, &value, sizeof (value)) < 0) {
+ P_WARNING ("PSocket::p_socket_set_keepalive: setsockopt() with SO_KEEPALIVE failed");
+ return;
+ }
+
+ socket->keepalive = !! (pint) keepalive;
+}
+
+P_LIB_API void
+p_socket_set_blocking (PSocket *socket,
+ pboolean blocking)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return;
+
+ socket->blocking = !! blocking;
+}
+
+P_LIB_API void
+p_socket_set_listen_backlog (PSocket *socket,
+ pint backlog)
+{
+ if (P_UNLIKELY (socket == NULL || socket->listening))
+ return;
+
+ socket->listen_backlog = backlog;
+}
+
+P_LIB_API void
+p_socket_set_timeout (PSocket *socket,
+ pint timeout)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return;
+
+ if (timeout < 0)
+ timeout = 0;
+
+ socket->timeout = timeout;
+}
+
+P_LIB_API pboolean
+p_socket_bind (const PSocket *socket,
+ PSocketAddress *address,
+ pboolean allow_reuse,
+ PError **error)
+{
+ struct sockaddr_storage addr;
+
+#ifdef SO_REUSEPORT
+ pboolean reuse_port;
+#endif
+
+#ifdef P_OS_WIN
+ pchar value;
+#else
+ pint value;
+#endif
+
+ if (P_UNLIKELY (socket == NULL || address == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return FALSE;
+ }
+
+ if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+ return FALSE;
+
+ /* Windows allows to reuse the same address even for an active TCP
+ * connection, that's why on Windows we should use SO_REUSEADDR only
+ * for UDP sockets, UNIX doesn't have such behavior
+ *
+ * Ignore errors here, the only likely error is "not supported", and
+ * this is a "best effort" thing mainly */
+
+#ifdef P_OS_WIN
+ value = !! (pchar) (allow_reuse && (socket->type == P_SOCKET_TYPE_DATAGRAM));
+#else
+ value = !! (pint) allow_reuse;
+#endif
+
+ if (setsockopt (socket->fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof (value)) < 0)
+ P_WARNING ("PSocket::p_socket_bind: setsockopt() with SO_REUSEADDR failed");
+
+#ifdef SO_REUSEPORT
+ reuse_port = allow_reuse && (socket->type == P_SOCKET_TYPE_DATAGRAM);
+
+# ifdef P_OS_WIN
+ value = !! (pchar) reuse_port;
+# else
+ value = !! (pint) reuse_port;
+# endif
+
+ if (setsockopt (socket->fd, SOL_SOCKET, SO_REUSEPORT, &value, sizeof (value)) < 0)
+ P_WARNING ("PSocket::p_socket_bind: setsockopt() with SO_REUSEPORT failed");
+#endif
+
+ if (P_UNLIKELY (p_socket_address_to_native (address, &addr, sizeof (addr)) == FALSE)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_FAILED,
+ 0,
+ "Failed to convert socket address to native structure");
+ return FALSE;
+ }
+
+ if (P_UNLIKELY (bind (socket->fd,
+ (struct sockaddr *) &addr,
+ (socklen_t) p_socket_address_get_native_size (address)) < 0)) {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call bind() on socket");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+P_LIB_API pboolean
+p_socket_connect (PSocket *socket,
+ PSocketAddress *address,
+ PError **error)
+{
+ struct sockaddr_storage buffer;
+ pint err_code;
+ pint conn_result;
+ PErrorIO sock_err;
+
+ if (P_UNLIKELY (socket == NULL || address == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return FALSE;
+ }
+
+ if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+ return FALSE;
+
+ if (P_UNLIKELY (p_socket_address_to_native (address, &buffer, sizeof (buffer)) == FALSE)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_FAILED,
+ 0,
+ "Failed to convert socket address to native structure");
+ return FALSE;
+ }
+
+#if !defined (P_OS_WIN) && defined (EINTR)
+ for (;;) {
+ conn_result = connect (socket->fd, (struct sockaddr *) &buffer,
+ (socklen_t) p_socket_address_get_native_size (address));
+
+ if (P_LIKELY (conn_result == 0))
+ break;
+
+ err_code = p_error_get_last_net ();
+
+ if (err_code == EINTR)
+ continue;
+ else
+ break;
+ }
+#else
+ conn_result = connect (socket->fd, (struct sockaddr *) &buffer,
+ (pint) p_socket_address_get_native_size (address));
+
+ if (conn_result != 0)
+ err_code = p_error_get_last_net ();
+#endif
+
+ if (conn_result == 0) {
+ socket->connected = TRUE;
+ return TRUE;
+ }
+
+ sock_err = p_error_get_io_from_system (err_code);
+
+ if (P_LIKELY (sock_err == P_ERROR_IO_WOULD_BLOCK || sock_err == P_ERROR_IO_IN_PROGRESS)) {
+ if (socket->blocking) {
+ if (p_socket_io_condition_wait (socket,
+ P_SOCKET_IO_CONDITION_POLLOUT,
+ error) == TRUE &&
+ p_socket_check_connect_result (socket, error) == TRUE) {
+ socket->connected = TRUE;
+ return TRUE;
+ }
+ } else
+ p_error_set_error_p (error,
+ (pint) sock_err,
+ err_code,
+ "Couldn't block non-blocking socket");
+ } else
+ p_error_set_error_p (error,
+ (pint) sock_err,
+ err_code,
+ "Failed to call connect() on socket");
+
+ return FALSE;
+}
+
+P_LIB_API pboolean
+p_socket_listen (PSocket *socket,
+ PError **error)
+{
+ if (P_UNLIKELY (socket == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return FALSE;
+ }
+
+ if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+ return FALSE;
+
+ if (P_UNLIKELY (listen (socket->fd, socket->listen_backlog) < 0)) {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call listen() on socket");
+ return FALSE;
+ }
+
+ socket->listening = TRUE;
+ return TRUE;
+}
+
+P_LIB_API PSocket *
+p_socket_accept (const PSocket *socket,
+ PError **error)
+{
+ PSocket *ret;
+ PErrorIO sock_err;
+ pint res;
+ pint err_code;
+#ifndef P_OS_WIN
+ pint flags;
+#endif
+
+ if (P_UNLIKELY (socket == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return NULL;
+ }
+
+ if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+ return NULL;
+
+ for (;;) {
+ if (socket->blocking &&
+ p_socket_io_condition_wait (socket,
+ P_SOCKET_IO_CONDITION_POLLIN,
+ error) == FALSE)
+ return NULL;
+
+ if ((res = (pint) accept (socket->fd, NULL, 0)) < 0) {
+ err_code = p_error_get_last_net ();
+#if !defined (P_OS_WIN) && defined (EINTR)
+ if (p_error_get_last_net () == EINTR)
+ continue;
+#endif
+ sock_err = p_error_get_io_from_system (err_code);
+
+ if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK)
+ continue;
+
+ p_error_set_error_p (error,
+ (pint) sock_err,
+ err_code,
+ "Failed to call accept() on socket");
+
+ return NULL;
+ }
+
+ break;
+ }
+
+#ifdef P_OS_WIN
+ /* The socket inherits the accepting sockets event mask and even object,
+ * we need to remove that */
+ WSAEventSelect (res, NULL, 0);
+#else
+ flags = fcntl (res, F_GETFD, 0);
+
+ if (P_LIKELY (flags != -1 && (flags & FD_CLOEXEC) == 0)) {
+ flags |= FD_CLOEXEC;
+
+ if (P_UNLIKELY (fcntl (res, F_SETFD, flags) < 0))
+ P_WARNING ("PSocket::p_socket_accept: fcntl() with FD_CLOEXEC failed");
+ }
+#endif
+
+ if (P_UNLIKELY ((ret = p_socket_new_from_fd (res, error)) == NULL)) {
+ if (P_UNLIKELY (p_sys_close (res) != 0))
+ P_WARNING ("PSocket::p_socket_accept: p_sys_close() failed");
+ } else
+ ret->protocol = socket->protocol;
+
+ return ret;
+}
+
+P_LIB_API pssize
+p_socket_receive (const PSocket *socket,
+ pchar *buffer,
+ psize buflen,
+ PError **error)
+{
+ PErrorIO sock_err;
+ pssize ret;
+ pint err_code;
+
+ if (P_UNLIKELY (socket == NULL || buffer == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return -1;
+ }
+
+ if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+ return -1;
+
+ for (;;) {
+ if (socket->blocking &&
+ p_socket_io_condition_wait (socket,
+ P_SOCKET_IO_CONDITION_POLLIN,
+ error) == FALSE)
+ return -1;
+
+ if ((ret = recv (socket->fd, buffer, (socklen_t) buflen, 0)) < 0) {
+ err_code = p_error_get_last_net ();
+
+#if !defined (P_OS_WIN) && defined (EINTR)
+ if (err_code == EINTR)
+ continue;
+#endif
+ sock_err = p_error_get_io_from_system (err_code);
+
+ if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK)
+ continue;
+
+ p_error_set_error_p (error,
+ (pint) sock_err,
+ err_code,
+ "Failed to call recv() on socket");
+
+ return -1;
+ }
+
+ break;
+ }
+
+ return ret;
+}
+
+P_LIB_API pssize
+p_socket_receive_from (const PSocket *socket,
+ PSocketAddress **address,
+ pchar *buffer,
+ psize buflen,
+ PError **error)
+{
+ PErrorIO sock_err;
+ struct sockaddr_storage sa;
+ socklen_t optlen;
+ pssize ret;
+ pint err_code;
+
+ if (P_UNLIKELY (socket == NULL || buffer == NULL || buflen == 0)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return -1;
+ }
+
+ if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+ return -1;
+
+ optlen = sizeof (sa);
+
+ for (;;) {
+ if (socket->blocking &&
+ p_socket_io_condition_wait (socket,
+ P_SOCKET_IO_CONDITION_POLLIN,
+ error) == FALSE)
+ return -1;
+
+ if ((ret = recvfrom (socket->fd,
+ buffer,
+ (socklen_t) buflen,
+ 0,
+ (struct sockaddr *) &sa,
+ &optlen)) < 0) {
+ err_code = p_error_get_last_net ();
+
+#if !defined (P_OS_WIN) && defined (EINTR)
+ if (err_code == EINTR)
+ continue;
+#endif
+ sock_err = p_error_get_io_from_system (err_code);
+
+ if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK)
+ continue;
+
+ p_error_set_error_p (error,
+ (pint) sock_err,
+ err_code,
+ "Failed to call recvfrom() on socket");
+
+ return -1;
+ }
+
+ break;
+ }
+
+ if (address != NULL)
+ *address = p_socket_address_new_from_native (&sa, optlen);
+
+ return ret;
+}
+
+P_LIB_API pssize
+p_socket_send (const PSocket *socket,
+ const pchar *buffer,
+ psize buflen,
+ PError **error)
+{
+ PErrorIO sock_err;
+ pssize ret;
+ pint err_code;
+
+ if (P_UNLIKELY (socket == NULL || buffer == NULL || buflen == 0)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return -1;
+ }
+
+ if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+ return -1;
+
+ for (;;) {
+ if (socket->blocking &&
+ p_socket_io_condition_wait (socket,
+ P_SOCKET_IO_CONDITION_POLLOUT,
+ error) == FALSE)
+ return -1;
+
+ if ((ret = send (socket->fd,
+ buffer,
+ (socklen_t) buflen,
+ P_SOCKET_DEFAULT_SEND_FLAGS)) < 0) {
+ err_code = p_error_get_last_net ();
+
+#if !defined (P_OS_WIN) && defined (EINTR)
+ if (err_code == EINTR)
+ continue;
+#endif
+ sock_err = p_error_get_io_from_system (err_code);
+
+ if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK)
+ continue;
+
+ p_error_set_error_p (error,
+ (pint) sock_err,
+ err_code,
+ "Failed to call send() on socket");
+
+ return -1;
+ }
+
+ break;
+ }
+
+ return ret;
+}
+
+P_LIB_API pssize
+p_socket_send_to (const PSocket *socket,
+ PSocketAddress *address,
+ const pchar *buffer,
+ psize buflen,
+ PError **error)
+{
+ PErrorIO sock_err;
+ struct sockaddr_storage sa;
+ socklen_t optlen;
+ pssize ret;
+ pint err_code;
+
+ if (!socket || !address || !buffer) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return -1;
+ }
+
+ if (!pp_socket_check (socket, error))
+ return -1;
+
+ if (!p_socket_address_to_native (address, &sa, sizeof (sa))) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_FAILED,
+ 0,
+ "Failed to convert socket address to native structure");
+ return -1;
+ }
+
+ optlen = (socklen_t) p_socket_address_get_native_size (address);
+
+ for (;;) {
+ if (socket->blocking &&
+ p_socket_io_condition_wait (socket, P_SOCKET_IO_CONDITION_POLLOUT, error) == FALSE)
+ return -1;
+
+ if ((ret = sendto (socket->fd,
+ buffer,
+ (socklen_t) buflen,
+ 0,
+ (struct sockaddr *) &sa,
+ optlen)) < 0) {
+ err_code = p_error_get_last_net ();
+
+#if !defined (P_OS_WIN) && defined (EINTR)
+ if (err_code == EINTR)
+ continue;
+#endif
+ sock_err = p_error_get_io_from_system (err_code);
+
+ if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK)
+ continue;
+
+ p_error_set_error_p (error,
+ (pint) sock_err,
+ err_code,
+ "Failed to call sendto() on socket");
+
+ return -1;
+ }
+
+ break;
+ }
+
+ return ret;
+}
+
+P_LIB_API pboolean
+p_socket_close (PSocket *socket,
+ PError **error)
+{
+ pint err_code;
+
+ if (P_UNLIKELY (socket == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return FALSE;
+ }
+
+ if (socket->closed)
+ return TRUE;
+
+ if (P_LIKELY (p_sys_close (socket->fd) == 0)) {
+ socket->connected = FALSE;
+ socket->closed = TRUE;
+ socket->listening = FALSE;
+ socket->fd = -1;
+
+ return TRUE;
+ } else {
+ err_code = p_error_get_last_net ();
+
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (err_code),
+ err_code,
+ "Failed to close socket");
+
+ return FALSE;
+ }
+}
+
+P_LIB_API pboolean
+p_socket_shutdown (PSocket *socket,
+ pboolean shutdown_read,
+ pboolean shutdown_write,
+ PError **error)
+{
+ pint how;
+
+ if (P_UNLIKELY (socket == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return FALSE;
+ }
+
+ if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+ return FALSE;
+
+ if (P_UNLIKELY (shutdown_read == FALSE && shutdown_write == FALSE))
+ return TRUE;
+
+#ifndef P_OS_WIN
+ if (shutdown_read == TRUE && shutdown_write == TRUE)
+ how = SHUT_RDWR;
+ else if (shutdown_read == TRUE)
+ how = SHUT_RD;
+ else
+ how = SHUT_WR;
+#else
+ if (shutdown_read == TRUE && shutdown_write == TRUE)
+ how = SD_BOTH;
+ else if (shutdown_read == TRUE)
+ how = SD_RECEIVE;
+ else
+ how = SD_SEND;
+#endif
+
+ if (P_UNLIKELY (shutdown (socket->fd, how) != 0)) {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call shutdown() on socket");
+ return FALSE;
+ }
+
+ if (shutdown_read == TRUE && shutdown_write == TRUE)
+ socket->connected = FALSE;
+
+ return TRUE;
+}
+
+P_LIB_API void
+p_socket_free (PSocket *socket)
+{
+ if (P_UNLIKELY (socket == NULL))
+ return;
+
+#ifdef P_OS_WIN
+ if (P_LIKELY (socket->events != WSA_INVALID_EVENT))
+ WSACloseEvent (socket->events);
+#endif
+
+ p_socket_close (socket, NULL);
+
+#ifdef P_OS_SCO
+ if (P_LIKELY (socket->timer != NULL))
+ p_time_profiler_free (socket->timer);
+#endif
+
+ p_free (socket);
+}
+
+P_LIB_API pboolean
+p_socket_set_buffer_size (const PSocket *socket,
+ PSocketDirection dir,
+ psize size,
+ PError **error)
+{
+ pint optname;
+ pint optval;
+
+ if (P_UNLIKELY (socket == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return FALSE;
+ }
+
+ if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+ return FALSE;
+
+ optname = (dir == P_SOCKET_DIRECTION_RCV) ? SO_RCVBUF : SO_SNDBUF;
+ optval = (pint) size;
+
+ if (P_UNLIKELY (setsockopt (socket->fd,
+ SOL_SOCKET,
+ optname,
+ (pconstpointer) &optval,
+ sizeof (optval)) != 0)) {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call setsockopt() on socket to set buffer size");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+P_LIB_API pboolean
+p_socket_io_condition_wait (const PSocket *socket,
+ PSocketIOCondition condition,
+ PError **error)
+{
+#if defined (P_OS_WIN)
+ long network_events;
+ pint evret;
+ pint timeout;
+
+ if (P_UNLIKELY (socket == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return FALSE;
+ }
+
+ if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+ return FALSE;
+
+ timeout = socket->timeout > 0 ? socket->timeout : WSA_INFINITE;
+
+ if (condition == P_SOCKET_IO_CONDITION_POLLIN)
+ network_events = FD_READ | FD_ACCEPT;
+ else
+ network_events = FD_WRITE | FD_CONNECT;
+
+ WSAResetEvent (socket->events);
+ WSAEventSelect (socket->fd, socket->events, network_events);
+
+ evret = WSAWaitForMultipleEvents (1, (const HANDLE *) &socket->events, TRUE, timeout, FALSE);
+
+ if (evret == WSA_WAIT_EVENT_0)
+ return TRUE;
+ else if (evret == WSA_WAIT_TIMEOUT) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_TIMED_OUT,
+ (pint) p_error_get_last_net (),
+ "Timed out while waiting socket condition");
+ return FALSE;
+ } else {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call WSAWaitForMultipleEvents() on socket");
+ return FALSE;
+ }
+#elif defined (P_SOCKET_USE_POLL)
+ struct pollfd pfd;
+ pint evret;
+ pint timeout;
+
+ if (P_UNLIKELY (socket == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return FALSE;
+ }
+
+ if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+ return FALSE;
+
+ timeout = socket->timeout > 0 ? socket->timeout : -1;
+
+ pfd.fd = socket->fd;
+ pfd.revents = 0;
+
+ if (condition == P_SOCKET_IO_CONDITION_POLLIN)
+ pfd.events = POLLIN;
+ else
+ pfd.events = POLLOUT;
+
+# ifdef P_OS_SCO
+ p_time_profiler_reset (socket->timer);
+# endif
+
+ while (TRUE) {
+ evret = poll (&pfd, 1, timeout);
+
+# ifdef EINTR
+ if (evret == -1 && p_error_get_last_net () == EINTR) {
+# ifdef P_OS_SCO
+ if (timeout < 0 ||
+ (p_time_profiler_elapsed_usecs (socket->timer) / 1000) < (puint64) timeout)
+ continue;
+ else
+ evret = 0;
+# else
+ continue;
+# endif
+ }
+# endif
+
+ if (evret == 1)
+ return TRUE;
+ else if (evret == 0) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_TIMED_OUT,
+ (pint) p_error_get_last_net (),
+ "Timed out while waiting socket condition");
+ return FALSE;
+ } else {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call poll() on socket");
+ return FALSE;
+ }
+ }
+#else
+ fd_set fds;
+ struct timeval tv;
+ struct timeval * ptv;
+ pint evret;
+
+ if (P_UNLIKELY (socket == NULL)) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_INVALID_ARGUMENT,
+ 0,
+ "Invalid input argument");
+ return FALSE;
+ }
+
+ if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+ return FALSE;
+
+ if (socket->timeout > 0)
+ ptv = &tv;
+ else
+ ptv = NULL;
+
+ while (TRUE) {
+ FD_ZERO (&fds);
+ FD_SET (socket->fd, &fds);
+
+ if (socket->timeout > 0) {
+ tv.tv_sec = socket->timeout / 1000;
+ tv.tv_usec = (socket->timeout % 1000) * 1000;
+ }
+
+ if (condition == P_SOCKET_IO_CONDITION_POLLIN)
+ evret = select (socket->fd + 1, &fds, NULL, NULL, ptv);
+ else
+ evret = select (socket->fd + 1, NULL, &fds, NULL, ptv);
+
+#ifdef EINTR
+ if (evret == -1 && p_error_get_last_net () == EINTR)
+ continue;
+#endif
+
+ if (evret == 1)
+ return TRUE;
+ else if (evret == 0) {
+ p_error_set_error_p (error,
+ (pint) P_ERROR_IO_TIMED_OUT,
+ (pint) p_error_get_last_net (),
+ "Timed out while waiting socket condition");
+ return FALSE;
+ } else {
+ p_error_set_error_p (error,
+ (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+ (pint) p_error_get_last_net (),
+ "Failed to call select() on socket");
+ return FALSE;
+ }
+ }
+#endif
+}