summaryrefslogtreecommitdiff
path: root/src/libs/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/network')
-rw-r--r--src/libs/network/FILES26
-rw-r--r--src/libs/network/Makeinfo14
-rw-r--r--src/libs/network/bytesex.h96
-rw-r--r--src/libs/network/connect/Makeinfo2
-rw-r--r--src/libs/network/connect/connect.c490
-rw-r--r--src/libs/network/connect/connect.h111
-rw-r--r--src/libs/network/connect/listen.c456
-rw-r--r--src/libs/network/connect/listen.h106
-rw-r--r--src/libs/network/connect/resolve.c211
-rw-r--r--src/libs/network/connect/resolve.h109
-rw-r--r--src/libs/network/netmanager/Makeinfo11
-rw-r--r--src/libs/network/netmanager/ndesc.c211
-rw-r--r--src/libs/network/netmanager/ndesc.h82
-rw-r--r--src/libs/network/netmanager/ndindex.ci103
-rw-r--r--src/libs/network/netmanager/netmanager.h48
-rw-r--r--src/libs/network/netmanager/netmanager_bsd.c223
-rw-r--r--src/libs/network/netmanager/netmanager_bsd.h26
-rw-r--r--src/libs/network/netmanager/netmanager_common.ci58
-rw-r--r--src/libs/network/netmanager/netmanager_win.c464
-rw-r--r--src/libs/network/netmanager/netmanager_win.h35
-rw-r--r--src/libs/network/netport.c91
-rw-r--r--src/libs/network/netport.h43
-rw-r--r--src/libs/network/network.h27
-rw-r--r--src/libs/network/network_bsd.c30
-rw-r--r--src/libs/network/network_win.c75
-rw-r--r--src/libs/network/socket/Makeinfo11
-rw-r--r--src/libs/network/socket/socket.c61
-rw-r--r--src/libs/network/socket/socket.h99
-rw-r--r--src/libs/network/socket/socket_bsd.c283
-rw-r--r--src/libs/network/socket/socket_bsd.h33
-rw-r--r--src/libs/network/socket/socket_win.c314
-rw-r--r--src/libs/network/socket/socket_win.h34
-rw-r--r--src/libs/network/wspiapiwrap.c34
-rw-r--r--src/libs/network/wspiapiwrap.h33
34 files changed, 4050 insertions, 0 deletions
diff --git a/src/libs/network/FILES b/src/libs/network/FILES
new file mode 100644
index 0000000..ddcd685
--- /dev/null
+++ b/src/libs/network/FILES
@@ -0,0 +1,26 @@
+In libs/network/:
+netport.{c,h} Portability definitions.
+bytesex.h Functions for endianness conversions.
+network.h Functions for initialising the network subsystem.
+network_bsd.c Network subsystem functions for BSD sockets.
+network_win.c Network subsystem functions for Winsock sockets.
+wspiapiwrap.{c,h} Hack to make sure <wspiapi.h> is #included in exactly
+ one file.
+
+In libs/network/netmanager/:
+ndesc.{c,h} Defines network descriptors.
+netmanager.h Handles callbacks for network activity.
+netmanager_bsd.{c,h} NetManager for systems with BSD sockets.
+netmanager_win.{c,h} NetManager for Winsock systems.
+
+In libs/network/socket/:
+socket.{c,h} Platform-independant socket layer.
+socket_bsd.{c,h} Socket operations for BSD sockets.
+socket_win.{c,h} Socket operations for Winsock sockets.
+
+In libs/network/connect/:
+connect.{c,h} Routines for establishing outgoing connections.
+listen.{c,h} Routines for receiving incoming connections.
+resolve.{c,h} Routines for hostname lookups.
+
+
diff --git a/src/libs/network/Makeinfo b/src/libs/network/Makeinfo
new file mode 100644
index 0000000..40171fa
--- /dev/null
+++ b/src/libs/network/Makeinfo
@@ -0,0 +1,14 @@
+uqm_SUBDIRS="connect netmanager socket"
+uqm_CFILES="netport.c"
+uqm_HFILES="bytesex.h netport.h network.h"
+
+if [ -n "$uqm_USE_WINSOCK" ]; then
+ uqm_CFILES="$uqm_CFILES network_win.c"
+ if [ -n "$MACRO___MINGW32__" ]; then
+ uqm_CFILES="$uqm_CFILES wspiapiwrap.c"
+ uqm_HFILES="$uqm_HFILES wspiapiwrap.h"
+ fi
+else
+ uqm_CFILES="$uqm_CFILES network_bsd.c"
+fi
+
diff --git a/src/libs/network/bytesex.h b/src/libs/network/bytesex.h
new file mode 100644
index 0000000..4ee89d7
--- /dev/null
+++ b/src/libs/network/bytesex.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// Routines for changing the endianness of values.
+// I'm not using ntohs() etc. as those would require include files that may
+// have conflicting definitions. This is a problem on Windows, where these
+// functions are in winsock2.h, which includes windows.h, which includes
+// pretty much Microsoft's complete collection of .h files.
+
+#ifndef LIBS_NETWORK_BYTESEX_H_
+#define LIBS_NETWORK_BYTESEX_H_
+
+#include "port.h"
+ // for inline
+#include "endian_uqm.h"
+ // for WORDS_BIGENDIAN
+#include "types.h"
+
+static inline uint16
+swapBytes16(uint16 x) {
+ return (x << 8) | (x >> 8);
+}
+
+static inline uint32
+swapBytes32(uint32 x) {
+ return (x << 24)
+ | ((x & 0x0000ff00) << 8)
+ | ((x & 0x00ff0000) >> 8)
+ | (x >> 24);
+}
+
+#ifdef WORDS_BIGENDIAN
+// Already in network order.
+
+static inline uint16
+hton16(uint16 x) {
+ return x;
+}
+
+static inline uint32
+hton32(uint32 x) {
+ return x;
+}
+
+static inline uint16
+ntoh16(uint16 x) {
+ return x;
+}
+
+static inline uint32
+ntoh32(uint32 x) {
+ return x;
+}
+
+#else /* !defined(WORDS_BIGENDIAN) */
+// Need to swap bytes
+
+static inline uint16
+hton16(uint16 x) {
+ return swapBytes16(x);
+}
+
+static inline uint32
+hton32(uint32 x) {
+ return swapBytes32(x);
+}
+
+static inline uint16
+ntoh16(uint16 x) {
+ return swapBytes16(x);
+}
+
+static inline uint32
+ntoh32(uint32 x) {
+ return swapBytes32(x);
+}
+
+#endif /* defined(WORDS_BIGENDIAN) */
+
+#endif /* LIBS_NETWORK_BYTESEX_H_ */
+
diff --git a/src/libs/network/connect/Makeinfo b/src/libs/network/connect/Makeinfo
new file mode 100644
index 0000000..5595270
--- /dev/null
+++ b/src/libs/network/connect/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="connect.c listen.c resolve.c"
+uqm_HFILES="connect.h listen.h resolve.h"
diff --git a/src/libs/network/connect/connect.c b/src/libs/network/connect/connect.c
new file mode 100644
index 0000000..4599b18
--- /dev/null
+++ b/src/libs/network/connect/connect.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+
+#define CONNECT_INTERNAL
+#define SOCKET_INTERNAL
+#include "connect.h"
+
+#include "resolve.h"
+#include "libs/alarm.h"
+#include "../socket/socket.h"
+#include "libs/misc.h"
+#include "libs/log.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef USE_WINSOCK
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include "../wspiapiwrap.h"
+#else
+# include <netdb.h>
+#endif
+
+#define DEBUG_CONNECT_REF
+#ifdef DEBUG_CONNECT_REF
+# include "types.h"
+#endif
+
+
+static void connectHostNext(ConnectState *connectState);
+static void doConnectCallback(ConnectState *connectState, NetDescriptor *nd,
+ const struct sockaddr *addr, socklen_t addrLen);
+static void doConnectErrorCallback(ConnectState *connectState,
+ const ConnectError *error);
+
+
+static ConnectState *
+ConnectState_alloc(void) {
+ return (ConnectState *) malloc(sizeof (ConnectState));
+}
+
+static void
+ConnectState_free(ConnectState *connectState) {
+ free(connectState);
+}
+
+static void
+ConnectState_delete(ConnectState *connectState) {
+ assert(connectState->nd == NULL);
+ assert(connectState->alarm == NULL);
+ assert(connectState->info == NULL);
+ assert(connectState->infoPtr == NULL);
+ ConnectState_free(connectState);
+}
+
+void
+ConnectState_incRef(ConnectState *connectState) {
+ assert(connectState->refCount < REFCOUNT_MAX);
+ connectState->refCount++;
+#ifdef DEBUG_CONNECT_REF
+ log_add(log_Debug, "ConnectState %08" PRIxPTR ": ref++ (%d)",
+ (uintptr_t) connectState, connectState->refCount);
+#endif
+}
+
+bool
+ConnectState_decRef(ConnectState *connectState) {
+ assert(connectState->refCount > 0);
+ connectState->refCount--;
+#ifdef DEBUG_CONNECT_REF
+ log_add(log_Debug, "ConnectState %08" PRIxPTR ": ref-- (%d)",
+ (uintptr_t) connectState, connectState->refCount);
+#endif
+ if (connectState->refCount == 0) {
+ ConnectState_delete(connectState);
+ return true;
+ }
+ return false;
+}
+
+// decrements ref count by 1
+void
+ConnectState_close(ConnectState *connectState) {
+ if (connectState->resolveState != NULL) {
+ Resolve_close(connectState->resolveState);
+ connectState->resolveState = NULL;
+ }
+ if (connectState->alarm != NULL) {
+ Alarm_remove(connectState->alarm);
+ connectState->alarm = NULL;
+ }
+ if (connectState->nd != NULL) {
+ NetDescriptor_close(connectState->nd);
+ connectState->nd = NULL;
+ }
+ if (connectState->info != NULL) {
+ freeaddrinfo(connectState->info);
+ connectState->info = NULL;
+ connectState->infoPtr = NULL;
+ }
+ connectState->state = Connect_closed;
+ ConnectState_decRef(connectState);
+}
+
+void
+ConnectState_setExtra(ConnectState *connectState, void *extra) {
+ connectState->extra = extra;
+}
+
+void *
+ConnectState_getExtra(ConnectState *connectState) {
+ return connectState->extra;
+}
+
+static void
+connectCallback(NetDescriptor *nd) {
+ // Called by the NetManager when a connection has been established.
+ ConnectState *connectState =
+ (ConnectState *) NetDescriptor_getExtra(nd);
+ int err;
+
+ if (connectState->alarm != NULL) {
+ Alarm_remove(connectState->alarm);
+ connectState->alarm = NULL;
+ }
+
+ if (connectState->state == Connect_closed) {
+ // The connection attempt has been aborted.
+#ifdef DEBUG
+ log_add(log_Debug, "Connection attempt was aborted.");
+#endif
+ ConnectState_decRef(connectState);
+ return;
+ }
+
+ if (Socket_getError(NetDescriptor_getSocket(nd), &err) == -1) {
+ log_add(log_Fatal, "Socket_getError() failed: %s.",
+ strerror(errno));
+ explode();
+ }
+ if (err != 0) {
+#ifdef DEBUG
+ log_add(log_Debug, "connect() failed: %s.", strerror(err));
+#endif
+ NetDescriptor_close(nd);
+ connectState->nd = NULL;
+ connectState->infoPtr = connectState->infoPtr->ai_next;
+ connectHostNext(connectState);
+ return;
+ }
+
+#ifdef DEBUG
+ log_add(log_Debug, "Connection established.");
+#endif
+
+ // Notify the higher layer.
+ connectState->nd = NULL;
+ // The callback function takes over ownership of the
+ // NetDescriptor.
+ NetDescriptor_setWriteCallback(nd, NULL);
+ // Note that connectState->info and connectState->infoPtr are cleaned up
+ // when ConnectState_close() is called by the callback function.
+
+ ConnectState_incRef(connectState);
+ doConnectCallback(connectState, nd, connectState->infoPtr->ai_addr,
+ connectState->infoPtr->ai_addrlen);
+ {
+ // The callback called should release the last reference to
+ // the connectState, by calling ConnectState_close().
+ bool released = ConnectState_decRef(connectState);
+ assert(released);
+ (void) released; // In case assert() evaluates to nothing.
+ }
+}
+
+static void
+connectTimeoutCallback(ConnectState *connectState) {
+ connectState->alarm = NULL;
+
+ NetDescriptor_close(connectState->nd);
+ connectState->nd = NULL;
+
+ connectState->infoPtr = connectState->infoPtr->ai_next;
+ connectHostNext(connectState);
+}
+
+static void
+setConnectTimeout(ConnectState *connectState) {
+ assert(connectState->alarm == NULL);
+
+ connectState->alarm =
+ Alarm_addRelativeMs(connectState->flags.timeout,
+ (AlarmCallback) connectTimeoutCallback, connectState);
+}
+
+// Try connecting to the next address.
+static Socket *
+tryConnectHostNext(ConnectState *connectState) {
+ struct addrinfo *info;
+ Socket *sock;
+ int connectResult;
+
+ assert(connectState->nd == NULL);
+
+ info = connectState->infoPtr;
+
+ sock = Socket_openNative(info->ai_family, info->ai_socktype,
+ info->ai_protocol);
+ if (sock == Socket_noSocket) {
+ int savedErrno = errno;
+ log_add(log_Error, "socket() failed: %s.", strerror(errno));
+ errno = savedErrno;
+ return Socket_noSocket;
+ }
+
+ if (Socket_setNonBlocking(sock) == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Could not make socket non-blocking: %s.",
+ strerror(errno));
+ errno = savedErrno;
+ return Socket_noSocket;
+ }
+
+ (void) Socket_setReuseAddr(sock);
+ // Ignore errors; it's not a big deal.
+ (void) Socket_setInlineOOB(sock);
+ // Ignore errors; it's not a big deal as the other party is not
+ // not supposed to send any OOB data.
+ (void) Socket_setKeepAlive(sock);
+ // Ignore errors; it's not a big deal.
+
+ connectResult = Socket_connect(sock, info->ai_addr, info->ai_addrlen);
+ if (connectResult == 0) {
+ // Connection has already succeeded.
+ // We just wait for the writability callback anyhow, so that
+ // we can use one code path.
+ return sock;
+ }
+
+ switch (errno) {
+ case EINPROGRESS:
+ // Connection in progress; wait for the write callback.
+ return sock;
+ }
+
+ // Connection failed immediately. This is just for one of the addresses,
+ // so this does not have to be final.
+ // Note that as the socket is non-blocking, most failed connection
+ // errors will usually not be reported immediately.
+ {
+ int savedErrno = errno;
+ Socket_close(sock);
+#ifdef DEBUG
+ log_add(log_Debug, "connect() immediately failed for one address: "
+ "%s.", strerror(errno));
+ // TODO: add the address in the status message.
+#endif
+ errno = savedErrno;
+ }
+ return Socket_noSocket;
+}
+
+static void
+connectRetryCallback(ConnectState *connectState) {
+ connectState->alarm = NULL;
+
+ connectState->infoPtr = connectState->info;
+ connectHostNext(connectState);
+}
+
+static void
+setConnectRetryAlarm(ConnectState *connectState) {
+ assert(connectState->alarm == NULL);
+ assert(connectState->flags.retryDelayMs != Connect_noRetry);
+
+ connectState->alarm =
+ Alarm_addRelativeMs(connectState->flags.retryDelayMs,
+ (AlarmCallback) connectRetryCallback, connectState);
+}
+
+static void
+connectHostReportAllFailed(ConnectState *connectState) {
+ // Could not connect to any host.
+ ConnectError error;
+ freeaddrinfo(connectState->info);
+ connectState->info = NULL;
+ connectState->infoPtr = NULL;
+ connectState->state = Connect_closed;
+ error.state = Connect_connecting;
+ error.err = ETIMEDOUT;
+ // No errno code is exactly suitable. We have been unable
+ // to connect to any host, but the reasons may vary
+ // (unreachable, refused, ...).
+ // ETIMEDOUT is the least specific portable errno code that
+ // seems appropriate.
+ doConnectErrorCallback(connectState, &error);
+}
+
+static void
+connectHostNext(ConnectState *connectState) {
+ Socket *sock;
+
+ while (connectState->infoPtr != NULL) {
+ sock = tryConnectHostNext(connectState);
+
+ if (sock != Socket_noSocket) {
+ // Connection succeeded or connection in progress
+ connectState->nd =
+ NetDescriptor_new(sock, (void *) connectState);
+ if (connectState->nd == NULL) {
+ ConnectError error;
+ int savedErrno = errno;
+
+ log_add(log_Error, "NetDescriptor_new() failed: %s.",
+ strerror(errno));
+ Socket_close(sock);
+ freeaddrinfo(connectState->info);
+ connectState->info = NULL;
+ connectState->infoPtr = NULL;
+ connectState->state = Connect_closed;
+ error.state = Connect_connecting;
+ error.err = savedErrno;
+ doConnectErrorCallback(connectState, &error);
+ return;
+ }
+
+ NetDescriptor_setWriteCallback(connectState->nd, connectCallback);
+ setConnectTimeout(connectState);
+ return;
+ }
+
+ connectState->infoPtr = connectState->infoPtr->ai_next;
+ }
+
+ // Connect failed to all addresses.
+
+ if (connectState->flags.retryDelayMs == Connect_noRetry) {
+ connectHostReportAllFailed(connectState);
+ return;
+ }
+
+ setConnectRetryAlarm(connectState);
+}
+
+static void
+connectHostResolveCallback(ResolveState *resolveState,
+ struct addrinfo *info) {
+ ConnectState *connectState =
+ (ConnectState *) ResolveState_getExtra(resolveState);
+
+ connectState->state = Connect_connecting;
+
+ Resolve_close(resolveState);
+ connectState->resolveState = NULL;
+
+ if (connectState->flags.familyPrefer != PF_UNSPEC) {
+ // Reorganise the 'info' list to put the structures of the
+ // prefered family in front.
+ struct addrinfo *preferred;
+ struct addrinfo **preferredEnd;
+ struct addrinfo *rest;
+ struct addrinfo **restEnd;
+ splitAddrInfoOnFamily(info, connectState->flags.familyPrefer,
+ &preferred, &preferredEnd, &rest, &restEnd);
+ info = preferred;
+ *preferredEnd = rest;
+ }
+
+ connectState->info = info;
+ connectState->infoPtr = info;
+
+ connectHostNext(connectState);
+}
+
+static void
+connectHostResolveErrorCallback(ResolveState *resolveState,
+ const ResolveError *resolveError) {
+ ConnectState *connectState =
+ (ConnectState *) ResolveState_getExtra(resolveState);
+ ConnectError connectError;
+
+ assert(resolveError->gaiRes != 0);
+
+ Resolve_close(resolveState);
+ connectState->resolveState = NULL;
+
+ connectError.state = Connect_resolving;
+ connectError.resolveError = resolveError;
+ connectError.err = resolveError->err;
+ doConnectErrorCallback(connectState, &connectError);
+}
+
+ConnectState *
+connectHostByName(const char *host, const char *service, Protocol proto,
+ const ConnectFlags *flags, ConnectConnectCallback connectCallback,
+ ConnectErrorCallback errorCallback, void *extra) {
+ struct addrinfo hints;
+ ConnectState *connectState;
+ ResolveFlags resolveFlags;
+ // Structure is empty (for now).
+
+ assert(flags->familyDemand == PF_inet ||
+ flags->familyDemand == PF_inet6 ||
+ flags->familyDemand == PF_unspec);
+ assert(flags->familyPrefer == PF_inet ||
+ flags->familyPrefer == PF_inet6 ||
+ flags->familyPrefer == PF_unspec);
+ assert(proto == IPProto_tcp || proto == IPProto_udp);
+
+ memset(&hints, '\0', sizeof hints);
+ hints.ai_family = protocolFamilyTranslation[flags->familyDemand];
+ hints.ai_protocol = protocolTranslation[proto];
+
+ if (proto == IPProto_tcp) {
+ hints.ai_socktype = SOCK_STREAM;
+ } else {
+ assert(proto == IPProto_udp);
+ hints.ai_socktype = SOCK_DGRAM;
+ }
+ hints.ai_flags = 0;
+
+ connectState = ConnectState_alloc();
+ connectState->refCount = 1;
+#ifdef DEBUG_CONNECT_REF
+ log_add(log_Debug, "ConnectState %08" PRIxPTR ": ref=1 (%d)",
+ (uintptr_t) connectState, connectState->refCount);
+#endif
+ connectState->state = Connect_resolving;
+ connectState->flags = *flags;
+ connectState->connectCallback = connectCallback;
+ connectState->errorCallback = errorCallback;
+ connectState->extra = extra;
+ connectState->info = NULL;
+ connectState->infoPtr = NULL;
+ connectState->nd = NULL;
+ connectState->alarm = NULL;
+
+ connectState->resolveState = getaddrinfoAsync(
+ host, service, &hints, &resolveFlags,
+ (ResolveCallback) connectHostResolveCallback,
+ (ResolveErrorCallback) connectHostResolveErrorCallback,
+ (ResolveCallbackArg) connectState);
+
+ return connectState;
+}
+
+// NB: The callback function becomes the owner of nd
+static void
+doConnectCallback(ConnectState *connectState, NetDescriptor *nd,
+ const struct sockaddr *addr, socklen_t addrLen) {
+ assert(connectState->connectCallback != NULL);
+
+ ConnectState_incRef(connectState);
+ // No need to increment nd as the callback function takes over ownership.
+ (*connectState->connectCallback)(connectState, nd, addr, addrLen);
+ ConnectState_decRef(connectState);
+}
+
+static void
+doConnectErrorCallback(ConnectState *connectState,
+ const ConnectError *error) {
+ assert(connectState->errorCallback != NULL);
+
+ ConnectState_incRef(connectState);
+ (*connectState->errorCallback)(connectState, error);
+ ConnectState_decRef(connectState);
+}
+
+
+
diff --git a/src/libs/network/connect/connect.h b/src/libs/network/connect/connect.h
new file mode 100644
index 0000000..77d7a44
--- /dev/null
+++ b/src/libs/network/connect/connect.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_CONNECT_CONNECT_H_
+#define LIBS_NETWORK_CONNECT_CONNECT_H_
+
+
+typedef struct ConnectFlags ConnectFlags;
+typedef struct ConnectError ConnectError;
+typedef struct ConnectState ConnectState;
+
+typedef enum {
+ Connect_closed,
+ Connect_resolving,
+ Connect_connecting
+} ConnectStateState;
+
+
+#include "../netmanager/netmanager.h"
+#include "../socket/socket.h"
+#include "resolve.h"
+
+
+// For connectHost()
+struct ConnectFlags {
+ ProtocolFamily familyDemand;
+ // Only accept a protocol family of this type.
+ // One of PF_inet, PF_inet6, or PF_unspec.
+ ProtocolFamily familyPrefer;
+ // Prefer a protocol family of this type.
+ // One of PF_inet, PF_inet6, or PF_unspec.
+ int timeout;
+ /* Number of milliseconds before timing out a connection attempt.
+ * Note that if a host has multiple addresses, a connect to that
+ * host will have this timeout *per address*. */
+ int retryDelayMs;
+ /* Retry connecting this many ms after connecting to the last
+ * address for the specified host fails. Set to Connect_noRetry
+ * to give up after one try. */
+#define Connect_noRetry -1
+};
+
+struct ConnectError {
+ ConnectStateState state;
+ // State where the error occured.
+ int err;
+ // errno value. Not relevant if state == resolving unless
+ // gaiRes == EAI_SYSTEM.
+ const ResolveError *resolveError;
+ // Only relevant if state == resolving.
+};
+
+typedef void (*ConnectConnectCallback)(ConnectState *connectState,
+ NetDescriptor *nd, const struct sockaddr *addr, socklen_t addrLen);
+typedef void (*ConnectErrorCallback)(ConnectState *connectState,
+ const ConnectError *error);
+
+#ifdef CONNECT_INTERNAL
+
+#include "libs/alarm.h"
+
+struct ConnectState {
+ RefCount refCount;
+
+ ConnectStateState state;
+ ConnectFlags flags;
+
+ ConnectConnectCallback connectCallback;
+ ConnectErrorCallback errorCallback;
+ void *extra;
+
+ struct addrinfo *info;
+ struct addrinfo *infoPtr;
+
+ ResolveState *resolveState;
+
+ NetDescriptor *nd;
+ Alarm *alarm;
+ // Used for both the timeout for a connection attempt
+ // and to retry after all addresses have been tried.
+};
+#endif /* CONNECT_INTERNAL */
+
+ConnectState *connectHostByName(const char *host, const char *service,
+ Protocol proto, const ConnectFlags *flags,
+ ConnectConnectCallback connectCallback,
+ ConnectErrorCallback errorCallback, void *extra);
+void ConnectState_incRef(ConnectState *connectState);
+bool ConnectState_decRef(ConnectState *connectState);
+void ConnectState_close(ConnectState *connectState);
+void ConnectState_setExtra(ConnectState *connectState, void *extra);
+void *ConnectState_getExtra(ConnectState *connectState);
+
+#endif /* LIBS_NETWORK_CONNECT_CONNECT_H_ */
+
+
diff --git a/src/libs/network/connect/listen.c b/src/libs/network/connect/listen.c
new file mode 100644
index 0000000..4a7a65c
--- /dev/null
+++ b/src/libs/network/connect/listen.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+#include "../netport.h"
+
+#define LISTEN_INTERNAL
+#define SOCKET_INTERNAL
+#include "listen.h"
+
+#include "resolve.h"
+#include "../socket/socket.h"
+#include "../netmanager/netmanager.h"
+#include "libs/misc.h"
+#include "libs/log.h"
+
+#include <assert.h>
+#include <errno.h>
+#ifdef USE_WINSOCK
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include "../wspiapiwrap.h"
+#else
+# include <netdb.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#define DEBUG_LISTEN_REF
+#ifdef DEBUG_LISTEN_REF
+# include "types.h"
+#endif
+
+
+static void acceptCallback(NetDescriptor *nd);
+static void doListenErrorCallback(ListenState *listenState,
+ const ListenError *error);
+
+
+static ListenState *
+ListenState_alloc(void) {
+ return (ListenState *) malloc(sizeof (ListenState));
+}
+
+static void
+ListenState_free(ListenState *listenState) {
+ free(listenState);
+}
+
+static void
+ListenState_delete(ListenState *listenState) {
+ assert(listenState->nds == NULL);
+ ListenState_free(listenState);
+}
+
+void
+ListenState_incRef(ListenState *listenState) {
+ assert(listenState->refCount < REFCOUNT_MAX);
+ listenState->refCount++;
+#ifdef DEBUG_LISTEN_REF
+ log_add(log_Debug, "ListenState %08" PRIxPTR ": ref++ (%d)",
+ (uintptr_t) listenState, listenState->refCount);
+#endif
+}
+
+bool
+ListenState_decRef(ListenState *listenState) {
+ assert(listenState->refCount > 0);
+ listenState->refCount--;
+#ifdef DEBUG_LISTEN_REF
+ log_add(log_Debug, "ListenState %08" PRIxPTR ": ref-- (%d)",
+ (uintptr_t) listenState, listenState->refCount);
+#endif
+ if (listenState->refCount == 0) {
+ ListenState_delete(listenState);
+ return true;
+ }
+ return false;
+}
+
+// Decrements ref count byh 1
+void
+ListenState_close(ListenState *listenState) {
+ if (listenState->resolveState != NULL) {
+ Resolve_close(listenState->resolveState);
+ listenState->resolveState = NULL;
+ }
+ if (listenState->nds != NULL) {
+ while (listenState->numNd > 0) {
+ listenState->numNd--;
+ NetDescriptor_close(listenState->nds[listenState->numNd]);
+ }
+ free(listenState->nds);
+ listenState->nds = NULL;
+ }
+ listenState->state = Listen_closed;
+ ListenState_decRef(listenState);
+}
+
+void
+ListenState_setExtra(ListenState *listenState, void *extra) {
+ listenState->extra = extra;
+}
+
+void *
+ListenState_getExtra(ListenState *listenState) {
+ return listenState->extra;
+}
+
+static NetDescriptor *
+listenPortSingle(struct ListenState *listenState, struct addrinfo *info) {
+ Socket *sock;
+ int bindResult;
+ int listenResult;
+ NetDescriptor *nd;
+
+ sock = Socket_openNative(info->ai_family, info->ai_socktype,
+ info->ai_protocol);
+ if (sock == Socket_noSocket) {
+ int savedErrno = errno;
+ log_add(log_Error, "socket() failed: %s.", strerror(errno));
+ errno = savedErrno;
+ return NULL;
+ }
+
+ (void) Socket_setReuseAddr(sock);
+ // Ignore errors; it's not a big deal.
+ if (Socket_setNonBlocking(sock) == -1) {
+ int savedErrno = errno;
+ // Error message is already printed.
+ Socket_close(sock);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ bindResult = Socket_bind(sock, info->ai_addr, info->ai_addrlen);
+ if (bindResult == -1) {
+ int savedErrno = errno;
+ if (errno == EADDRINUSE) {
+#ifdef DEBUG
+ log_add(log_Warning, "bind() failed: %s.", strerror(errno));
+#endif
+ } else
+ log_add(log_Error, "bind() failed: %s.", strerror(errno));
+ Socket_close(sock);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ listenResult = Socket_listen(sock, listenState->flags.backlog);
+ if (listenResult == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "listen() failed: %s.", strerror(errno));
+ Socket_close(sock);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ nd = NetDescriptor_new(sock, (void *) listenState);
+ if (nd == NULL) {
+ int savedErrno = errno;
+ log_add(log_Error, "NetDescriptor_new() failed: %s.",
+ strerror(errno));
+ Socket_close(sock);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ NetDescriptor_setReadCallback(nd, acceptCallback);
+
+ return nd;
+}
+
+static void
+listenPortMulti(struct ListenState *listenState, struct addrinfo *info) {
+ struct addrinfo *addrPtr;
+ size_t addrCount;
+ size_t addrOkCount;
+ NetDescriptor **nds;
+
+ // Count how many addresses we've got.
+ addrCount = 0;
+ for (addrPtr = info; addrPtr != NULL; addrPtr = addrPtr->ai_next)
+ addrCount++;
+
+ // This is where we intend to store the file descriptors of the
+ // listening sockets.
+ nds = malloc(addrCount * sizeof listenState->nds[0]);
+
+ // Bind to each address.
+ addrOkCount = 0;
+ for (addrPtr = info; addrPtr != NULL; addrPtr = addrPtr->ai_next) {
+ NetDescriptor *nd;
+ nd = listenPortSingle(listenState, addrPtr);
+ if (nd == NULL) {
+ // Failed. On to the next.
+ // An error message is already printed for serious errors.
+ // If the address is already in use, we here also print
+ // a message when we are not already listening on one of
+ // the other addresses.
+ // This is because on some IPv6 capabable systems (like Linux),
+ // IPv6 sockets also handle IPv4 connections, which means
+ // that a separate IPv4 socket won't be able to bind to the
+ // port.
+ // BUG: if the IPv4 socket is in the list before the
+ // IPv6 socket, it will be the IPv6 which will fail to bind,
+ // so only IPv4 connections will be handled, as v4 sockets can't
+ // accept v6 connections.
+ // In practice, on Linux, I haven't seen it happen, but
+ // it's a real possibility.
+ if (errno == EADDRINUSE && addrOkCount == 0) {
+ log_add(log_Error, "Error while preparing a network socket "
+ "for incoming connections: %s", strerror(errno));
+ }
+ continue;
+ }
+
+ nds[addrOkCount] = nd;
+ addrOkCount++;
+ }
+
+ freeaddrinfo(info);
+
+ listenState->nds =
+ realloc(nds, addrOkCount * sizeof listenState->nds[0]);
+ listenState->numNd = addrOkCount;
+
+ if (addrOkCount == 0) {
+ // Could not listen on any port.
+ ListenError error;
+ error.state = Listen_listening;
+ error.err = EIO;
+ // Nothing better to offer.
+ doListenErrorCallback(listenState, &error);
+ return;
+ }
+}
+
+static void
+listenPortResolveCallback(ResolveState *resolveState,
+ struct addrinfo *result) {
+ ListenState *listenState =
+ (ListenState *) ResolveState_getExtra(resolveState);
+ Resolve_close(listenState->resolveState);
+ listenState->resolveState = NULL;
+ listenState->state = Listen_listening;
+ listenPortMulti(listenState, result);
+}
+
+static void
+listenPortResolveErrorCallback(ResolveState *resolveState,
+ const ResolveError *resolveError) {
+ ListenState *listenState =
+ (ListenState *) ResolveState_getExtra(resolveState);
+ ListenError listenError;
+
+ assert(resolveError->gaiRes != 0);
+
+ listenError.state = Listen_resolving;
+ listenError.resolveError = resolveError;
+ listenError.err = resolveError->err;
+ doListenErrorCallback(listenState, &listenError);
+}
+
+// 'proto' is one of IPProto_tcp or IPProto_udp.
+ListenState *
+listenPort(const char *service, Protocol proto, const ListenFlags *flags,
+ ListenConnectCallback connectCallback,
+ ListenErrorCallback errorCallback, void *extra) {
+ struct addrinfo hints;
+ ListenState *listenState;
+ ResolveFlags resolveFlags;
+ // Structure is empty (for now).
+
+ assert(flags->familyDemand == PF_inet ||
+ flags->familyDemand == PF_inet6 ||
+ flags->familyDemand == PF_unspec);
+ assert(flags->familyPrefer == PF_inet ||
+ flags->familyPrefer == PF_inet6 ||
+ flags->familyPrefer == PF_unspec);
+ assert(proto == IPProto_tcp || proto == IPProto_udp);
+
+ // Acquire a list of addresses to bind to.
+ memset(&hints, '\0', sizeof hints);
+ hints.ai_family = protocolFamilyTranslation[flags->familyDemand];
+ hints.ai_protocol = protocolTranslation[proto];
+
+ if (proto == IPProto_tcp) {
+ hints.ai_socktype = SOCK_STREAM;
+ } else {
+ assert(proto == IPProto_udp);
+ hints.ai_socktype = SOCK_DGRAM;
+ }
+ hints.ai_flags = AI_PASSIVE;
+
+ listenState = ListenState_alloc();
+ listenState->refCount = 1;
+#ifdef DEBUG_LISTEN_REF
+ log_add(log_Debug, "ListenState %08" PRIxPTR ": ref=1 (%d)",
+ (uintptr_t) listenState, listenState->refCount);
+#endif
+ listenState->state = Listen_resolving;
+ listenState->flags = *flags;
+ listenState->connectCallback = connectCallback;
+ listenState->errorCallback = errorCallback;
+ listenState->extra = extra;
+ listenState->nds = NULL;
+ listenState->numNd = 0;
+
+ listenState->resolveState = getaddrinfoAsync(NULL, service, &hints,
+ &resolveFlags, listenPortResolveCallback,
+ listenPortResolveErrorCallback,
+ (ResolveCallbackArg) listenState);
+
+ return listenState;
+}
+
+// NB: The callback function becomes the owner of newNd.
+static void
+doListenConnectCallback(ListenState *listenState, NetDescriptor *listenNd,
+ NetDescriptor *newNd,
+ const struct sockaddr *addr, SOCKLEN_T addrLen) {
+ assert(listenState->connectCallback != NULL);
+
+ ListenState_incRef(listenState);
+ // No need to increment listenNd, as there's guaranteed to be one
+ // reference from listenState. And no need to increment newNd,
+ // as the callback function takes over ownership.
+ (*listenState->connectCallback)(listenState, listenNd, newNd,
+ addr, (socklen_t) addrLen);
+ ListenState_decRef(listenState);
+}
+
+static void
+doListenErrorCallback(ListenState *listenState, const ListenError *error) {
+ assert(listenState->errorCallback != NULL);
+
+ ListenState_incRef(listenState);
+ (*listenState->errorCallback)(listenState, error);
+ ListenState_decRef(listenState);
+}
+
+static void
+acceptSingleConnection(ListenState *listenState, NetDescriptor *nd) {
+ Socket *sock;
+ Socket *acceptResult;
+ struct sockaddr_storage addr;
+ socklen_t addrLen;
+ NetDescriptor *newNd;
+
+ sock = NetDescriptor_getSocket(nd);
+ addrLen = sizeof (addr);
+ acceptResult = Socket_accept(sock, (struct sockaddr *) &addr, &addrLen);
+ if (acceptResult == Socket_noSocket) {
+ switch (errno) {
+ case EWOULDBLOCK:
+ case ECONNABORTED:
+ // Nothing serious. Keep listening.
+ return;
+ case EMFILE:
+ case ENFILE:
+ case ENOBUFS:
+ case ENOMEM:
+#ifdef ENOSR
+ case ENOSR:
+#endif
+ // Serious problems, but future connections may still
+ // be possible.
+ log_add(log_Warning, "accept() reported '%s'",
+ strerror(errno));
+ return;
+ default:
+ // Should not happen.
+ log_add(log_Fatal, "Internal error: accept() reported "
+ "'%s'", strerror(errno));
+ explode();
+ }
+ }
+
+ (void) Socket_setReuseAddr(acceptResult);
+ // Ignore errors; it's not a big deal.
+ if (Socket_setNonBlocking(acceptResult) == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Could not make socket non-blocking: %s.",
+ strerror(errno));
+ Socket_close(acceptResult);
+ errno = savedErrno;
+ return;
+ }
+ (void) Socket_setInlineOOB(acceptResult);
+ // Ignore errors; it's not a big deal as the other
+ // party is not not supposed to send any OOB data.
+ (void) Socket_setKeepAlive(sock);
+ // Ignore errors; it's not a big deal.
+
+#ifdef DEBUG
+ {
+ char hostname[NI_MAXHOST];
+ int gniRes;
+
+ gniRes = getnameinfo((struct sockaddr *) &addr, addrLen,
+ hostname, sizeof hostname, NULL, 0, 0);
+ if (gniRes != 0) {
+ log_add(log_Error, "Error while performing hostname "
+ "lookup for incoming connection: %s",
+ (gniRes == EAI_SYSTEM) ? strerror(errno) :
+ gai_strerror(gniRes));
+ } else {
+ log_add(log_Debug, "Accepted incoming connection from '%s'.",
+ hostname);
+ }
+ }
+#endif
+
+ newNd = NetDescriptor_new(acceptResult, NULL);
+ if (newNd == NULL) {
+ int savedErrno = errno;
+ log_add(log_Error, "NetDescriptor_new() failed: %s.",
+ strerror(errno));
+ Socket_close(acceptResult);
+ errno = savedErrno;
+ return;
+ }
+
+ doListenConnectCallback(listenState, nd, newNd,
+ (struct sockaddr *) &addr, addrLen);
+ // NB: newNd is now handed over to the callback function, and should
+ // no longer be referenced from here.
+}
+
+// Called when select() has indicated readability on a listening socket,
+// i.e. when a connection is in the queue.
+static void
+acceptCallback(NetDescriptor *nd) {
+ ListenState *listenState = (ListenState *) NetDescriptor_getExtra(nd);
+
+ acceptSingleConnection(listenState, nd);
+}
+
+
diff --git a/src/libs/network/connect/listen.h b/src/libs/network/connect/listen.h
new file mode 100644
index 0000000..e44ef53
--- /dev/null
+++ b/src/libs/network/connect/listen.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_CONNECT_LISTEN_H_
+#define LIBS_NETWORK_CONNECT_LISTEN_H_
+
+typedef struct ListenFlags ListenFlags;
+typedef enum {
+ Listen_closed,
+ Listen_resolving,
+ Listen_listening
+} ListenStateState;
+typedef struct ListenError ListenError;
+typedef struct ListenState ListenState;
+
+#include "port.h"
+
+#ifdef USE_WINSOCK
+// I do not want to include winsock2.h, because of possible conflicts with
+// code that includes this file.
+// Note that listen.c itself includes winsock2.h; SOCKLEN_T is used there
+// only where necessary to keep the API consistent.
+# define SOCKLEN_T size_t
+struct sockaddr;
+#else
+# include <netinet/in.h>
+# define SOCKLEN_T socklen_t
+#endif
+
+#include "resolve.h"
+#include "../socket/socket.h"
+
+#include "../netmanager/netmanager.h"
+
+// For listenPort()
+struct ListenFlags {
+ ProtocolFamily familyDemand;
+ // Only accept a protocol family of this type.
+ // One of PF_inet, PF_inet6, or PF_unspec.
+ ProtocolFamily familyPrefer;
+ // Prefer a protocol family of this type.
+ // One of PF_inet, PF_inet6, or PF_unspec.
+ int backlog;
+ // As the 2rd parameter to listen();
+};
+
+struct ListenError {
+ ListenStateState state;
+ // State where the error occured.
+ int err;
+ // errno value. Not relevant if state == resolving unless
+ // gaiRes == EAI_SYSTEM.
+ const ResolveError *resolveError;
+ // Only relevant if state == resolving.
+};
+
+typedef void (*ListenConnectCallback)(ListenState *listenState,
+ NetDescriptor *listenNd, NetDescriptor *newNd,
+ const struct sockaddr *addr, SOCKLEN_T addrLen);
+typedef void (*ListenErrorCallback)(ListenState *listenState,
+ const ListenError *error);
+
+#ifdef LISTEN_INTERNAL
+struct ListenState {
+ RefCount refCount;
+
+ ListenStateState state;
+ ListenFlags flags;
+
+ ListenConnectCallback connectCallback;
+ ListenErrorCallback errorCallback;
+ void *extra;
+
+ ResolveState *resolveState;
+ NetDescriptor **nds;
+ size_t numNd;
+ // INV: (numNd == NULL) == (nds == NULL)
+};
+#endif /* defined(LISTEN_INTERNAL) */
+
+ListenState *listenPort(const char *service, Protocol proto,
+ const ListenFlags *flags, ListenConnectCallback connectCallback,
+ ListenErrorCallback errorCallback, void *extra);
+void ListenState_close(ListenState *listenState);
+void ListenState_incRef(ListenState *listenState);
+bool ListenState_decRef(ListenState *listenState);
+void ListenState_setExtra(ListenState *listenState, void *extra);
+void *ListenState_getExtra(ListenState *listenState);
+
+#endif /* LIBS_NETWORK_CONNECT_LISTEN_H_ */
+
diff --git a/src/libs/network/connect/resolve.c b/src/libs/network/connect/resolve.c
new file mode 100644
index 0000000..646e437
--- /dev/null
+++ b/src/libs/network/connect/resolve.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define RESOLVE_INTERNAL
+#include "resolve.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#define DEBUG_RESOLVE_REF
+#ifdef DEBUG_RESOLVE_REF
+# include "types.h"
+# include "libs/log.h"
+# include <string.h>
+#endif
+
+static ResolveState *
+ResolveState_new(void) {
+ return (ResolveState *) malloc(sizeof (ResolveState));
+}
+
+static void
+ResolveState_free(ResolveState *resolveState) {
+ free(resolveState);
+}
+
+static void
+ResolveState_delete(ResolveState *resolveState) {
+ assert(resolveState->callbackID == CallbackID_invalid);
+ ResolveState_free(resolveState);
+}
+
+void
+ResolveState_incRef(ResolveState *resolveState) {
+ assert(resolveState->refCount < REFCOUNT_MAX);
+ resolveState->refCount++;
+#ifdef DEBUG_RESOLVE_REF
+ log_add(log_Debug, "ResolveState %08" PRIxPTR ": ref++ (%d)",
+ (uintptr_t) resolveState, resolveState->refCount);
+#endif
+}
+
+bool
+ResolveState_decRef(ResolveState *resolveState) {
+ assert(resolveState->refCount > 0);
+ resolveState->refCount--;
+#ifdef DEBUG_RESOLVE_REF
+ log_add(log_Debug, "ResolveState %08" PRIxPTR ": ref-- (%d)",
+ (uintptr_t) resolveState, resolveState->refCount);
+#endif
+ if (resolveState->refCount == 0) {
+ ResolveState_delete(resolveState);
+ return true;
+ }
+ return false;
+}
+
+void
+ResolveState_setExtra(ResolveState *resolveState, void *extra) {
+ resolveState->extra = extra;
+}
+
+void *
+ResolveState_getExtra(ResolveState *resolveState) {
+ return resolveState->extra;
+}
+
+static void
+doResolveCallback(ResolveState *resolveState) {
+ ResolveState_incRef(resolveState);
+ (*resolveState->callback)(resolveState, resolveState->result);
+ {
+ bool released = ResolveState_decRef(resolveState);
+ assert(released);
+ (void) released; // In case assert() evaluates to nothing.
+ }
+}
+
+static void
+doResolveErrorCallback(ResolveState *resolveState) {
+ ResolveState_incRef(resolveState);
+ resolveState->errorCallback(resolveState, &resolveState->error);
+ {
+ bool released = ResolveState_decRef(resolveState);
+ assert(released);
+ (void) released; // In case assert() evaluates to nothing.
+ }
+}
+
+static void
+resolveCallback(ResolveState *resolveState) {
+ resolveState->callbackID = CallbackID_invalid;
+ if (resolveState->error.gaiRes == 0) {
+ // Successful lookup.
+ doResolveCallback(resolveState);
+ } else {
+ // Lookup failed.
+ doResolveErrorCallback(resolveState);
+ }
+}
+
+// Function that does getaddrinfo() and calls the callback function when
+// the result is available.
+// TODO: actually make this function asynchronous. Right now it just calls
+// getaddrinfo() (which blocks) and then schedules the callback.
+ResolveState *
+getaddrinfoAsync(const char *node, const char *service,
+ const struct addrinfo *hints, ResolveFlags *flags,
+ ResolveCallback callback, ResolveErrorCallback errorCallback,
+ ResolveCallbackArg extra) {
+ ResolveState *resolveState;
+
+ resolveState = ResolveState_new();
+ resolveState->refCount = 1;
+#ifdef DEBUG_RESOLVE_REF
+ log_add(log_Debug, "ResolveState %08" PRIxPTR ": ref=1 (%d)",
+ (uintptr_t) resolveState, resolveState->refCount);
+#endif
+ resolveState->state = Resolve_resolving;
+ resolveState->flags = *flags;
+ resolveState->callback = callback;
+ resolveState->errorCallback = errorCallback;
+ resolveState->extra = extra;
+ resolveState->result = NULL;
+
+ resolveState->error.gaiRes =
+ getaddrinfo(node, service, hints, &resolveState->result);
+ resolveState->error.err = errno;
+
+ resolveState->callbackID = Callback_add(
+ (CallbackFunction) resolveCallback, (CallbackArg) resolveState);
+
+ return resolveState;
+}
+
+void
+Resolve_close(ResolveState *resolveState) {
+ if (resolveState->callbackID != CallbackID_invalid) {
+ Callback_remove(resolveState->callbackID);
+ resolveState->callbackID = CallbackID_invalid;
+ }
+ resolveState->state = Resolve_closed;
+ ResolveState_decRef(resolveState);
+}
+
+// Split an addrinfo list into two separate lists, one with structures with
+// the specified value for the ai_family field, the other with the other
+// structures. The order of entries in the resulting lists will remain
+// the same as in the original list.
+// info - the original list
+// family - the family for the first list
+// selected - pointer to where the list of selected structures should be
+// stored
+// selectedEnd - pointer to where the end of 'selected' should be stored
+// rest - pointer to where the list of not-selected structures should be
+// stored
+// restEnd - pointer to where the end of 'rest' should be stored
+//
+// Yes, it is allowed to modify the ai_next field of addrinfo structures.
+// Or at least, it's not disallowed by RFC 3493, and the following
+// text from that RFC seems to suggest it should be possible:
+// ] The freeaddrinfo() function must support the freeing of arbitrary
+// ] sublists of an addrinfo list originally returned by getaddrinfo().
+void
+splitAddrInfoOnFamily(struct addrinfo *info, int family,
+ struct addrinfo **selected, struct addrinfo ***selectedEnd,
+ struct addrinfo **rest, struct addrinfo ***restEnd) {
+ struct addrinfo *selectedFirst;
+ struct addrinfo **selectedNext;
+ struct addrinfo *restFirst;
+ struct addrinfo **restNext;
+
+ selectedNext = &selectedFirst;
+ restNext = &restFirst;
+ while (info != NULL) {
+ if (info->ai_family == family) {
+ *selectedNext = info;
+ selectedNext = &(*selectedNext)->ai_next;
+ } else {
+ *restNext = info;
+ restNext = &(*restNext)->ai_next;
+ }
+ info = info->ai_next;
+ }
+ *selectedNext = NULL;
+ *restNext = NULL;
+
+ // Fill in the result parameters.
+ *selected = selectedFirst;
+ *selectedEnd = selectedNext;
+ *rest = restFirst;
+ *restEnd = restNext;
+}
+
+
diff --git a/src/libs/network/connect/resolve.h b/src/libs/network/connect/resolve.h
new file mode 100644
index 0000000..509c3b9
--- /dev/null
+++ b/src/libs/network/connect/resolve.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_CONNECT_RESOLVE_H_
+#define LIBS_NETWORK_CONNECT_RESOLVE_H_
+
+
+typedef struct ResolveFlags ResolveFlags;
+typedef struct ResolveError ResolveError;
+typedef enum {
+ Resolve_closed,
+ Resolve_resolving
+} ResolveStateState;
+typedef struct ResolveState ResolveState;
+
+#include "port.h"
+#include "../netport.h"
+
+// For addrinfo.
+#ifdef USE_WINSOCK
+// Not including <winsock2.h> because of possible conflicts with files
+// including this file.
+struct addrinfo;
+#else
+# include <netdb.h>
+#endif
+
+#include "libs/callback.h"
+#include "../netmanager/netmanager.h"
+
+
+struct ResolveFlags {
+ // Nothing yet.
+
+ int dummy; // empty struct declarations are not allowed by C'99.
+};
+
+struct ResolveError {
+ int gaiRes;
+ int err;
+ // errno value. Only relevant if gaiRes == EAI_SYSTEM.
+};
+
+typedef void *ResolveCallbackArg;
+typedef void (*ResolveCallback)(ResolveState *resolveState,
+ struct addrinfo *result);
+ // The receiver of the callback is owner of 'result' and
+ // should call freeaddrinfo().
+typedef void (*ResolveErrorCallback)(ResolveState *resolveState,
+ const ResolveError *error);
+
+#ifdef RESOLVE_INTERNAL
+#ifdef USE_WINSOCK
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include "../wspiapiwrap.h"
+#else /* !defined(USE_WINSOCK) */
+# include <netdb.h>
+#endif /* !defined(USE_WINSOCK) */
+
+struct ResolveState {
+ RefCount refCount;
+
+ ResolveStateState state;
+ ResolveFlags flags;
+
+ ResolveCallback callback;
+ ResolveErrorCallback errorCallback;
+ void *extra;
+
+ CallbackID callbackID;
+ ResolveError error;
+ struct addrinfo *result;
+};
+#endif /* RESOLVE_INTERNAL */
+
+
+void ResolveState_incRef(ResolveState *resolveState);
+bool ResolveState_decRef(ResolveState *resolveState);
+void ResolveState_setExtra(ResolveState *resolveState, void *extra);
+void *ResolveState_getExtra(ResolveState *resolveState);
+ResolveState *getaddrinfoAsync(const char *node, const char *service,
+ const struct addrinfo *hints, ResolveFlags *flags,
+ ResolveCallback callback, ResolveErrorCallback errorCallback,
+ ResolveCallbackArg extra);
+void Resolve_close(ResolveState *resolveState);
+
+void splitAddrInfoOnFamily(struct addrinfo *info, int family,
+ struct addrinfo **selected, struct addrinfo ***selectedEnd,
+ struct addrinfo **rest, struct addrinfo ***restEnd);
+
+
+#endif /* LIBS_NETWORK_CONNECT_RESOLVE_H_ */
+
diff --git a/src/libs/network/netmanager/Makeinfo b/src/libs/network/netmanager/Makeinfo
new file mode 100644
index 0000000..ddf28b3
--- /dev/null
+++ b/src/libs/network/netmanager/Makeinfo
@@ -0,0 +1,11 @@
+uqm_CFILES="ndesc.c"
+uqm_HFILES="ndesc.h netmanager.h"
+
+if [ -n "$uqm_USE_WINSOCK" ]; then
+ uqm_CFILES="$uqm_CFILES netmanager_win.c"
+ uqm_HFILES="$uqm_HFILES netmanager_win.h"
+else
+ uqm_CFILES="$uqm_CFILES netmanager_bsd.c"
+ uqm_HFILES="$uqm_HFILES netmanager_bsd.h"
+fi
+
diff --git a/src/libs/network/netmanager/ndesc.c b/src/libs/network/netmanager/ndesc.c
new file mode 100644
index 0000000..e407ff9
--- /dev/null
+++ b/src/libs/network/netmanager/ndesc.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NETDESCRIPTOR_INTERNAL
+#include "ndesc.h"
+
+#include "netmanager.h"
+#include "libs/callback.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#undef DEBUG_NETDESCRIPTOR_REF
+#ifdef DEBUG_NETDESCRIPTOR_REF
+# include "libs/log.h"
+# include <inttypes.h>
+#endif
+
+
+static NetDescriptor *
+NetDescriptor_alloc(void) {
+ return malloc(sizeof (NetDescriptor));
+}
+
+static void
+NetDescriptor_free(NetDescriptor *nd) {
+ free(nd);
+}
+
+// Sets the ref count to 1.
+NetDescriptor *
+NetDescriptor_new(Socket *socket, void *extra) {
+ NetDescriptor *nd;
+
+ nd = NetDescriptor_alloc();
+ nd->refCount = 1;
+#ifdef DEBUG_NETDESCRIPTOR_REF
+ log_add(log_Debug, "NetDescriptor %08" PRIxPTR ": ref=1 (%d)",
+ (uintptr_t) nd, nd->refCount);
+#endif
+
+ nd->flags.closed = false;
+ nd->readCallback = NULL;
+ nd->writeCallback = NULL;
+ nd->exceptionCallback = NULL;
+ nd->closeCallback = NULL;
+ nd->socket = socket;
+ nd->smd = NULL;
+ nd->extra = extra;
+
+ if (NetManager_addDesc(nd) == -1) {
+ int savedErrno = errno;
+ NetDescriptor_free(nd);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ return nd;
+}
+
+static void
+NetDescriptor_delete(NetDescriptor *nd) {
+ assert(nd->socket == Socket_noSocket);
+ assert(nd->smd == NULL);
+
+ NetDescriptor_free(nd);
+}
+
+// Called from the callback handler.
+static void
+NetDescriptor_closeCallback(NetDescriptor *nd) {
+ if (nd->closeCallback != NULL) {
+ // The check is necessary because the close callback may have
+ // been removed before it is triggered.
+ (*nd->closeCallback)(nd);
+ }
+ NetDescriptor_decRef(nd);
+}
+
+void
+NetDescriptor_close(NetDescriptor *nd) {
+ assert(!nd->flags.closed);
+ assert(nd->socket != Socket_noSocket);
+
+ NetManager_removeDesc(nd);
+ (void) Socket_close(nd->socket);
+ nd->socket = Socket_noSocket;
+ nd->flags.closed = true;
+ if (nd->closeCallback != NULL) {
+ // Keep one reference around until the close callback has been
+ // called.
+ (void) Callback_add(
+ (CallbackFunction) NetDescriptor_closeCallback,
+ (CallbackArg) nd);
+ } else
+ NetDescriptor_decRef(nd);
+}
+
+void
+NetDescriptor_incRef(NetDescriptor *nd) {
+ assert(nd->refCount < REFCOUNT_MAX);
+ nd->refCount++;
+#ifdef DEBUG_NETDESCRIPTOR_REF
+ log_add(log_Debug, "NetDescriptor %08" PRIxPTR ": ref++ (%d)",
+ (uintptr_t) nd, nd->refCount);
+#endif
+}
+
+// returns true iff the ref counter has reached 0.
+bool
+NetDescriptor_decRef(NetDescriptor *nd) {
+ assert(nd->refCount > 0);
+ nd->refCount--;
+#ifdef DEBUG_NETDESCRIPTOR_REF
+ log_add(log_Debug, "NetDescriptor %08" PRIxPTR ": ref-- (%d)",
+ (uintptr_t) nd, nd->refCount);
+#endif
+ if (nd->refCount == 0) {
+ NetDescriptor_delete(nd);
+ return true;
+ }
+ return false;
+}
+
+// The socket will no longer be managed by the NetManager.
+void
+NetDescriptor_detach(NetDescriptor *nd) {
+ NetManager_removeDesc(nd);
+ nd->socket = Socket_noSocket;
+ nd->flags.closed = true;
+ NetDescriptor_decRef(nd);
+}
+
+Socket *
+NetDescriptor_getSocket(NetDescriptor *nd) {
+ return nd->socket;
+}
+
+void
+NetDescriptor_setExtra(NetDescriptor *nd, void *extra) {
+ nd->extra = extra;
+}
+
+void *
+NetDescriptor_getExtra(const NetDescriptor *nd) {
+ return nd->extra;
+}
+
+void
+NetDescriptor_setReadCallback(NetDescriptor *nd,
+ NetDescriptor_ReadCallback callback) {
+ nd->readCallback = callback;
+ if (!nd->flags.closed) {
+ if (nd->readCallback != NULL) {
+ NetManager_activateReadCallback(nd);
+ } else
+ NetManager_deactivateReadCallback(nd);
+ }
+}
+
+void
+NetDescriptor_setWriteCallback(NetDescriptor *nd,
+ NetDescriptor_WriteCallback callback) {
+ nd->writeCallback = callback;
+ if (!nd->flags.closed) {
+ if (nd->writeCallback != NULL) {
+ NetManager_activateWriteCallback(nd);
+ } else
+ NetManager_deactivateWriteCallback(nd);
+ }
+}
+
+void
+NetDescriptor_setExceptionCallback(NetDescriptor *nd,
+ NetDescriptor_ExceptionCallback callback) {
+ nd->exceptionCallback = callback;
+ if (!nd->flags.closed) {
+ if (nd->exceptionCallback != NULL) {
+ NetManager_activateExceptionCallback(nd);
+ } else
+ NetManager_deactivateExceptionCallback(nd);
+ }
+}
+
+// The close callback is called as a result of a socket being closed, either
+// because of a local command or a remote disconnect.
+// The close callback will only be scheduled when this happens. The
+// callback will not be called until the Callback_process() is called.
+void
+NetDescriptor_setCloseCallback(NetDescriptor *nd,
+ NetDescriptor_CloseCallback callback) {
+ nd->closeCallback = callback;
+}
+
+
diff --git a/src/libs/network/netmanager/ndesc.h b/src/libs/network/netmanager/ndesc.h
new file mode 100644
index 0000000..8834db9
--- /dev/null
+++ b/src/libs/network/netmanager/ndesc.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_NETMANAGER_NDESC_H_
+#define LIBS_NETWORK_NETMANAGER_NDESC_H_
+
+#include "types.h"
+
+
+typedef struct NetDescriptor NetDescriptor;
+
+typedef void (*NetDescriptor_ReadCallback)(NetDescriptor *nd);
+typedef void (*NetDescriptor_WriteCallback)(NetDescriptor *nd);
+typedef void (*NetDescriptor_ExceptionCallback)(NetDescriptor *nd);
+typedef void (*NetDescriptor_CloseCallback)(NetDescriptor *nd);
+
+typedef uint32 RefCount;
+#define REFCOUNT_MAX UINT32_MAX
+
+#include "../socket/socket.h"
+#include "netmanager.h"
+
+#ifdef NETDESCRIPTOR_INTERNAL
+// All created NetDescriptors are registered to the NetManager.
+// They are unregisted when the NetDescriptor is closed.
+// On creation the ref count is set to 1. On close it is decremented by 1.
+struct NetDescriptor {
+ struct {
+ bool closed: 1;
+ } flags;
+
+ RefCount refCount;
+
+ NetDescriptor_ReadCallback readCallback;
+ NetDescriptor_WriteCallback writeCallback;
+ NetDescriptor_ExceptionCallback exceptionCallback;
+ NetDescriptor_CloseCallback closeCallback;
+
+ Socket *socket;
+ SocketManagementData *smd;
+
+ // Extra state-dependant information for the user.
+ void *extra;
+};
+#endif
+
+NetDescriptor *NetDescriptor_new(Socket *socket, void *extra);
+void NetDescriptor_close(NetDescriptor *nd);
+void NetDescriptor_incRef(NetDescriptor *nd);
+bool NetDescriptor_decRef(NetDescriptor *nd);
+void NetDescriptor_detach(NetDescriptor *nd);
+Socket *NetDescriptor_getSocket(NetDescriptor *nd);
+void NetDescriptor_setExtra(NetDescriptor *nd, void *extra);
+void *NetDescriptor_getExtra(const NetDescriptor *nd);
+void NetDescriptor_setReadCallback(NetDescriptor *nd,
+ NetDescriptor_ReadCallback callback);
+void NetDescriptor_setWriteCallback(NetDescriptor *nd,
+ NetDescriptor_WriteCallback callback);
+void NetDescriptor_setExceptionCallback(NetDescriptor *nd,
+ NetDescriptor_ExceptionCallback callback);
+void NetDescriptor_setCloseCallback(NetDescriptor *nd,
+ NetDescriptor_CloseCallback callback);
+
+
+#endif /* LIBS_NETWORK_NETMANAGER_NDESC_H_ */
+
+
diff --git a/src/libs/network/netmanager/ndindex.ci b/src/libs/network/netmanager/ndindex.ci
new file mode 100644
index 0000000..f922d8b
--- /dev/null
+++ b/src/libs/network/netmanager/ndindex.ci
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// This file is part of netmanager_bsd.c, from where it is #included.
+// Only used for BSD sockets.
+
+// This file provides a mapping of Sockets to NetDescriptors.
+
+
+static NetDescriptor *netDescriptors[FD_SETSIZE];
+ // INV: flags.closed is not set for entries in netDescriptors.
+static size_t maxND;
+ // One past the largest used ND in netDescriptors, as used in
+ // the first argument of select();
+
+
+static inline void
+NDIndex_init(void) {
+ size_t i;
+ size_t numND = sizeof (netDescriptors) / sizeof (netDescriptors[0]);
+
+ for (i = 0; i < numND; i++)
+ netDescriptors[i] = NULL;
+}
+
+static inline void
+NDIndex_uninit(void) {
+ // Nothing to do.
+}
+
+static inline int
+NDIndex_registerNDWithSocket(Socket *sock, NetDescriptor *nd) {
+ if ((unsigned int) sock->fd >= FD_SETSIZE) {
+ errno = EMFILE;
+ return -1;
+ }
+
+ netDescriptors[sock->fd] = nd;
+
+ if ((size_t) sock->fd >= maxND)
+ maxND = (size_t) sock->fd + 1;
+
+ return 0;
+}
+
+static inline void
+NDIndex_unregisterNDForSocket(Socket *sock) {
+ NetDescriptor **last;
+
+ netDescriptors[sock->fd] = NULL;
+
+ last = &netDescriptors[sock->fd];
+
+ if ((size_t) sock->fd + 1 == maxND) {
+ do {
+ maxND--;
+ if (last == &netDescriptors[0])
+ break;
+ last--;
+ } while (*last == NULL);
+ }
+}
+
+static inline NetDescriptor *
+NDIndex_getNDForSocket(Socket *sock) {
+ assert((size_t) sock->fd < maxND);
+ return netDescriptors[sock->fd];
+}
+
+static inline NetDescriptor *
+NDIndex_getNDForSocketFd(int fd) {
+ assert((size_t) fd < maxND);
+ return netDescriptors[fd];
+}
+
+static inline bool
+NDIndex_socketRegistered(Socket *sock) {
+ return ((size_t) sock->fd < maxND)
+ && (NDIndex_getNDForSocket(sock) != NULL);
+}
+
+// Get the first argument to be used in select().
+static inline size_t
+NDIndex_getSelectNumND(void) {
+ return maxND;
+}
+
+
diff --git a/src/libs/network/netmanager/netmanager.h b/src/libs/network/netmanager/netmanager.h
new file mode 100644
index 0000000..42be572
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_NETMANAGER_NETMANAGER_H_
+#define LIBS_NETWORK_NETMANAGER_NETMANAGER_H_
+
+#include "port.h"
+#include "types.h"
+
+#ifdef USE_WINSOCK
+# include "netmanager_win.h"
+#else
+# include "netmanager_bsd.h"
+#endif
+
+#include "ndesc.h"
+
+void NetManager_init(void);
+void NetManager_uninit(void);
+int NetManager_process(uint32 *timeoutMs);
+
+// Only for internal use by the NetManager:
+int NetManager_addDesc(NetDescriptor *nd);
+void NetManager_removeDesc(NetDescriptor *nd);
+void NetManager_activateReadCallback(NetDescriptor *nd);
+void NetManager_deactivateReadCallback(NetDescriptor *nd);
+void NetManager_activateWriteCallback(NetDescriptor *nd);
+void NetManager_deactivateWriteCallback(NetDescriptor *nd);
+void NetManager_activateExceptionCallback(NetDescriptor *nd);
+void NetManager_deactivateExceptionCallback(NetDescriptor *nd);
+
+#endif /* LIBS_NETWORK_NETMANAGER_NETMANAGER_H_ */
+
diff --git a/src/libs/network/netmanager/netmanager_bsd.c b/src/libs/network/netmanager/netmanager_bsd.c
new file mode 100644
index 0000000..29159f8
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_bsd.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define SOCKET_INTERNAL
+#define NETDESCRIPTOR_INTERNAL
+#include "netmanager_bsd.h"
+#include "ndesc.h"
+#include "../socket/socket.h"
+
+#include "ndesc.h"
+#include "types.h"
+#include "libs/log.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "netmanager_common.ci"
+#include "ndindex.ci"
+
+
+// INV: The following sets only contain sockets present in the netDescriptor
+// array.
+static fd_set readSet;
+static fd_set writeSet;
+static fd_set exceptionSet;
+
+
+void
+NetManager_init(void) {
+ NDIndex_init();
+
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+ FD_ZERO(&exceptionSet);
+}
+
+void
+NetManager_uninit(void) {
+ NDIndex_uninit();
+}
+
+// Register the NetDescriptor with the NetManager.
+int
+NetManager_addDesc(NetDescriptor *nd) {
+ int fd;
+ assert(nd->socket != Socket_noSocket);
+ assert(!NDIndex_socketRegistered(nd->socket));
+
+ if (NDIndex_registerNDWithSocket(nd->socket, nd) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ fd = nd->socket->fd;
+ if (nd->readCallback != NULL)
+ FD_SET(fd, &readSet);
+ if (nd->writeCallback != NULL)
+ FD_SET(fd, &writeSet);
+ if (nd->exceptionCallback != NULL)
+ FD_SET(fd, &exceptionSet);
+ return 0;
+}
+
+void
+NetManager_removeDesc(NetDescriptor *nd) {
+ int fd;
+
+ assert(nd->socket != Socket_noSocket);
+ assert(NDIndex_getNDForSocket(nd->socket) == nd);
+
+ fd = nd->socket->fd;
+ FD_CLR(fd, &readSet);
+ FD_CLR(fd, &writeSet);
+ FD_CLR(fd, &exceptionSet);
+
+ NDIndex_unregisterNDForSocket(nd->socket);
+}
+
+void
+NetManager_activateReadCallback(NetDescriptor *nd) {
+ FD_SET(nd->socket->fd, &readSet);
+}
+
+void
+NetManager_deactivateReadCallback(NetDescriptor *nd) {
+ FD_CLR(nd->socket->fd, &readSet);
+}
+
+void
+NetManager_activateWriteCallback(NetDescriptor *nd) {
+ FD_SET(nd->socket->fd, &writeSet);
+}
+
+void
+NetManager_deactivateWriteCallback(NetDescriptor *nd) {
+ FD_CLR(nd->socket->fd, &writeSet);
+}
+
+void
+NetManager_activateExceptionCallback(NetDescriptor *nd) {
+ FD_SET(nd->socket->fd, &exceptionSet);
+}
+
+void
+NetManager_deactivateExceptionCallback(NetDescriptor *nd) {
+ FD_CLR(nd->socket->fd, &exceptionSet);
+}
+
+// This function may be called again from inside a callback function
+// triggered by this function. BUG: This may result in callbacks being
+// called multiple times.
+// This function should however not be called from multiple threads at once.
+int
+NetManager_process(uint32 *timeoutMs) {
+ struct timeval timeout;
+ size_t i;
+ int selectResult;
+ fd_set newReadSet;
+ fd_set newWriteSet;
+ fd_set newExceptionSet;
+ bool bitSet;
+
+ timeout.tv_sec = *timeoutMs / 1000;
+ timeout.tv_usec = (*timeoutMs % 1000) * 1000;
+
+ // Structure assignment:
+ newReadSet = readSet;
+ newWriteSet = writeSet;
+ newExceptionSet = exceptionSet;
+
+ do {
+ selectResult = select(NDIndex_getSelectNumND(),
+ &newReadSet, &newWriteSet, &newExceptionSet, &timeout);
+ // BUG: If select() is restarted because of EINTR, the timeout
+ // may start over. (Linux changes 'timeout' to the time left,
+ // but most other platforms don't.)
+ } while (selectResult == -1 && errno == EINTR);
+ if (selectResult == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "select() failed: %s.", strerror(errno));
+ errno = savedErrno;
+ *timeoutMs = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000);
+ // XXX: rounding microseconds down. Is that the correct
+ // thing to do?
+ return -1;
+ }
+
+ for (i = 0; i < maxND; i++) {
+ NetDescriptor *nd;
+
+ if (selectResult == 0) {
+ // No more bits set in the fd_sets
+ break;
+ }
+
+ nd = NDIndex_getNDForSocketFd(i);
+ if (nd == NULL)
+ continue;
+
+ bitSet = false;
+ // Is one of the bits in the fd_sets set?
+
+ // A callback may cause a NetDescriptor to be closed. The deletion
+ // of the structure will be scheduled, but will still be
+ // available at least until this function returns.
+
+ if (FD_ISSET(i, &newExceptionSet))
+ {
+ bool closed;
+ bitSet = true;
+ closed = NetManager_doExceptionCallback(nd);
+ if (closed)
+ goto next;
+ }
+
+ if (FD_ISSET(i, &newWriteSet))
+ {
+ bool closed;
+ bitSet = true;
+ closed = NetManager_doWriteCallback(nd);
+ if (closed)
+ goto next;
+ }
+
+ if (FD_ISSET(i, &newReadSet))
+ {
+ bool closed;
+ bitSet = true;
+ closed = NetManager_doReadCallback(nd);
+ if (closed)
+ goto next;
+ }
+
+next:
+ if (bitSet)
+ selectResult--;
+ }
+
+ *timeoutMs = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000);
+ // XXX: rounding microseconds down. Is that the correct
+ // thing to do?
+ return 0;
+}
+
+
+
diff --git a/src/libs/network/netmanager/netmanager_bsd.h b/src/libs/network/netmanager/netmanager_bsd.h
new file mode 100644
index 0000000..b94dd69
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_bsd.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_NETMANAGER_NETMANAGER_BSD_H_
+#define LIBS_NETWORK_NETMANAGER_NETMANAGER_BSD_H_
+
+typedef struct SocketManagementDataBsd SocketManagementDataBsd;
+typedef SocketManagementDataBsd SocketManagementData;
+
+#endif /* LIBS_NETWORK_NETMANAGER_NETMANAGER_BSD_H_ */
+
diff --git a/src/libs/network/netmanager/netmanager_common.ci b/src/libs/network/netmanager/netmanager_common.ci
new file mode 100644
index 0000000..058e739
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_common.ci
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// This file is part of netmanager_bsd.ci and netmanager_win.ci,
+// from where it is #included.
+
+static bool
+NetManager_doReadCallback(NetDescriptor *nd) {
+ bool closed;
+
+ assert(nd->readCallback != NULL);
+ NetDescriptor_incRef(nd);
+ (*nd->readCallback)(nd);
+ closed = nd->flags.closed;
+ NetDescriptor_decRef(nd);
+ return closed;
+}
+
+static bool
+NetManager_doWriteCallback(NetDescriptor *nd) {
+ bool closed;
+
+ assert(nd->writeCallback != NULL);
+ NetDescriptor_incRef(nd);
+ (*nd->writeCallback)(nd);
+ closed = nd->flags.closed;
+ NetDescriptor_decRef(nd);
+ return closed;
+}
+
+static bool
+NetManager_doExceptionCallback(NetDescriptor *nd) {
+ bool closed;
+
+ assert(nd->exceptionCallback != NULL);
+ NetDescriptor_incRef(nd);
+ (*nd->exceptionCallback)(nd);
+ closed = nd->flags.closed;
+ NetDescriptor_decRef(nd);
+ return closed;
+}
+
+
diff --git a/src/libs/network/netmanager/netmanager_win.c b/src/libs/network/netmanager/netmanager_win.c
new file mode 100644
index 0000000..f146732
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_win.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+#include "../netport.h"
+
+#define NETMANAGER_INTERNAL
+#define NETDESCRIPTOR_INTERNAL
+#define SOCKET_INTERNAL
+#include "netmanager_win.h"
+#include "../socket/socket.h"
+
+#include "ndesc.h"
+#include "types.h"
+#include "libs/misc.h"
+#include "libs/log.h"
+
+#include <assert.h>
+#include <winsock2.h>
+
+#include "netmanager_common.ci"
+
+int closeWSAEvent(WSAEVENT event);
+
+
+// The elements of the following arrays with the same index belong to
+// eachother.
+#define MAX_SOCKETS WSA_MAXIMUM_WAIT_EVENTS
+ // We cannot have more sockets than we can have events, as
+ // all may need an event at some point.
+static NetDescriptor *netDescriptors[MAX_SOCKETS];
+static WSAEVENT events[WSA_MAXIMUM_WAIT_EVENTS];
+static size_t numSockets;
+static size_t numActiveEvents;
+// For each NetDescriptor registered with the NetManager, an event
+// is created. Only the first numActiveEvents will be processed though.
+// Inv: numActiveEvents <= numSockets
+// Inv: for all i: 0 <= i < numActiveEvents: events[i] is active
+// (events[i] being active also means netDescriptor[i]->smd->eventMask
+// != 0)
+// Inv: for all i: 0 <= i < numSockets: netDescriptor[i]->smd->index == i
+
+void
+NetManager_init(void) {
+ numActiveEvents = 0;
+}
+
+void
+NetManager_uninit(void) {
+ assert(numActiveEvents == 0);
+}
+
+static inline SocketManagementDataWin *
+SocketManagementData_alloc(void) {
+ return malloc(sizeof (SocketManagementDataWin));
+}
+
+static inline void
+SocketManagementData_free(SocketManagementDataWin *smd) {
+ free(smd);
+}
+
+// XXX: This function should be moved to some file with generic network
+// functions.
+int
+closeWSAEvent(WSAEVENT event) {
+ for (;;) {
+ int error;
+
+ if (WSACloseEvent(event))
+ break;
+
+ error = WSAGetLastError();
+ if (error != WSAEINPROGRESS) {
+ log_add(log_Error,
+ "WSACloseEvent() failed with error code %d.", error);
+ errno = winsockErrorToErrno(error);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+// Register the NetDescriptor with the NetManager.
+int
+NetManager_addDesc(NetDescriptor *nd) {
+ long eventMask = 0;
+ WSAEVENT event;
+
+ if (numSockets >= WSA_MAXIMUM_WAIT_EVENTS) {
+ errno = EMFILE;
+ return -1;
+ }
+
+ if (nd->readCallback != NULL)
+ eventMask |= FD_READ | FD_ACCEPT;
+
+ if (nd->writeCallback != NULL)
+ eventMask |= FD_WRITE /* | FD_CONNECT */;
+
+ if (nd->exceptionCallback != NULL)
+ eventMask |= FD_OOB;
+
+ eventMask |= FD_CLOSE;
+
+ event = WSACreateEvent();
+ if (event == WSA_INVALID_EVENT) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ nd->smd = SocketManagementData_alloc();
+ if (eventMask != 0) {
+ // XXX: This guard is now always true, because of FD_CLOSE.
+ // This means that numActiveEvents will always be equal to
+ // numEvents.
+ // Once I'm convinced this is the right way to go,
+ // I can remove some unnecessary code.
+ if (WSAEventSelect(nd->socket->sock, event, eventMask) ==
+ SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ int closeStatus = closeWSAEvent(event);
+ if (closeStatus == -1) {
+ log_add(log_Fatal, "closeWSAEvent() failed: %s.",
+ strerror(errno));
+ explode();
+ }
+ SocketManagementData_free(nd->smd);
+ errno = savedErrno;
+ return -1;
+ }
+
+ // Move existing socket for which there exists no event, so
+ // so that all sockets for which there exists an event are at
+ // the front of the array of netdescriptors.
+ if (numActiveEvents < numSockets) {
+ netDescriptors[numSockets] = netDescriptors[numActiveEvents];
+ netDescriptors[numSockets]->smd->index = numSockets;
+ }
+
+ nd->smd->index = numActiveEvents;
+ numActiveEvents++;
+ } else {
+ nd->smd->index = numSockets;
+ }
+ nd->smd->eventMask = eventMask;
+
+ netDescriptors[nd->smd->index] = nd;
+ events[nd->smd->index] = event;
+ numSockets++;
+
+ return 0;
+}
+
+void
+NetManager_removeDesc(NetDescriptor *nd) {
+ assert(nd->smd != NULL);
+ assert(nd->smd->index < numSockets);
+ assert(nd == netDescriptors[nd->smd->index]);
+
+ {
+ int closeStatus = closeWSAEvent(events[nd->smd->index]);
+ if (closeStatus == -1)
+ explode();
+ }
+
+ if (nd->smd->index < numActiveEvents) {
+ size_t index = nd->smd->index;
+ if (index + 1 != numActiveEvents) {
+ // Keep the list of active events consecutive by filling
+ // the new hole with the last active event.
+ events[index] = events[numActiveEvents - 1];
+ netDescriptors[index] = netDescriptors[numActiveEvents - 1];
+ netDescriptors[index]->smd->index = index;
+ }
+ numActiveEvents--;
+ }
+
+ SocketManagementData_free(nd->smd);
+ nd->smd = NULL;
+
+ numSockets--;
+}
+
+static void
+swapSockets(int index1, int index2) {
+ NetDescriptor *tempNd;
+ WSAEVENT tempEvent;
+
+ tempNd = netDescriptors[index2];
+ tempEvent = events[index2];
+
+ netDescriptors[index2] = netDescriptors[index1];
+ events[index2] = events[index1];
+ netDescriptors[index2]->smd->index = index2;
+
+ netDescriptors[index1] = tempNd;
+ events[index1] = tempEvent;
+ netDescriptors[index1]->smd->index = index1;
+}
+
+static int
+NetManager_updateEvent(NetDescriptor *nd) {
+ assert(nd == netDescriptors[nd->smd->index]);
+
+ if (WSAEventSelect(nd->socket->sock,
+ events[nd->smd->index], nd->smd->eventMask) == SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ int closeStatus = closeWSAEvent(events[nd->smd->index]);
+ if (closeStatus == -1) {
+ log_add(log_Fatal, "closeWSAEvent() failed: %s.",
+ strerror(errno));
+ explode();
+ }
+ errno = savedErrno;
+ return -1;
+ }
+
+ if (nd->smd->eventMask != 0) {
+ // There are some events that we are interested in.
+ if (nd->smd->index >= numActiveEvents) {
+ // Event was not yet active.
+ if (nd->smd->index != numActiveEvents) {
+ // Need to keep the active nds and events in the front of
+ // their arrays.
+ swapSockets(nd->smd->index, numActiveEvents);
+ }
+ numActiveEvents++;
+ }
+ } else {
+ // There are no events that we are interested in.
+ if (nd->smd->index < numActiveEvents) {
+ // Event was active.
+ if (nd->smd->index != numActiveEvents - 1) {
+ // Need to keep the active nds and events in the front of
+ // their arrays.
+ swapSockets(nd->smd->index, numActiveEvents - 1);
+ }
+ }
+ numActiveEvents--;
+ }
+
+ return 0;
+}
+
+static void
+activateSomeCallback(NetDescriptor *nd, long eventMask) {
+ nd->smd->eventMask |= eventMask;
+ {
+ int status = NetManager_updateEvent(nd);
+ if (status == -1) {
+ log_add(log_Fatal, "NetManager_updateEvent() failed: %s.",
+ strerror(errno));
+ explode();
+ // TODO: better error handling.
+ }
+ }
+}
+
+static void
+deactivateSomeCallback(NetDescriptor *nd, long eventMask) {
+ nd->smd->eventMask &= ~eventMask;
+ {
+ int status = NetManager_updateEvent(nd);
+ if (status == -1) {
+ log_add(log_Fatal, "NetManager_updateEvent() failed: %s.",
+ strerror(errno));
+ explode();
+ // TODO: better error handling
+ }
+ }
+}
+
+void
+NetManager_activateReadCallback(NetDescriptor *nd) {
+ activateSomeCallback(nd, FD_READ | FD_ACCEPT);
+}
+
+void
+NetManager_deactivateReadCallback(NetDescriptor *nd) {
+ deactivateSomeCallback(nd, FD_READ | FD_ACCEPT);
+}
+
+void
+NetManager_activateWriteCallback(NetDescriptor *nd) {
+ activateSomeCallback(nd, FD_WRITE /* | FD_CONNECT */);
+}
+
+void
+NetManager_deactivateWriteCallback(NetDescriptor *nd) {
+ deactivateSomeCallback(nd, FD_WRITE /* | FD_CONNECT */);
+}
+
+void
+NetManager_activateExceptionCallback(NetDescriptor *nd) {
+ activateSomeCallback(nd, FD_OOB);
+}
+
+void
+NetManager_deactivateExceptionCallback(NetDescriptor *nd) {
+ deactivateSomeCallback(nd, FD_OOB);
+}
+
+static inline int
+NetManager_processEvent(size_t index) {
+ WSANETWORKEVENTS networkEvents;
+ int enumRes;
+
+ enumRes = WSAEnumNetworkEvents(netDescriptors[index]->socket->sock,
+ events[index], &networkEvents);
+ if (enumRes == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ if (networkEvents.lNetworkEvents & FD_READ) {
+ bool closed;
+ if (networkEvents.iErrorCode[FD_READ_BIT] != 0) {
+ // No special handling is required; the callback function
+ // will try to do a recv() and will get the error then.
+ }
+
+ closed = NetManager_doReadCallback(netDescriptors[index]);
+ if (closed)
+ goto closed;
+ }
+ if (networkEvents.lNetworkEvents & FD_WRITE) {
+ bool closed;
+ if (networkEvents.iErrorCode[FD_WRITE_BIT] != 0) {
+ // No special handling is required; the callback function
+ // will try to do a send() and will get the error then.
+ }
+
+ closed = NetManager_doWriteCallback(netDescriptors[index]);
+ if (closed)
+ goto closed;
+ }
+ if (networkEvents.lNetworkEvents & FD_OOB) {
+ bool closed;
+ if (networkEvents.iErrorCode[FD_OOB_BIT] != 0) {
+ // No special handling is required; the callback function
+ // will get the error then when it tries to do a recv().
+ }
+
+ closed = NetManager_doExceptionCallback(netDescriptors[index]);
+ if (closed)
+ goto closed;
+ }
+ if (networkEvents.lNetworkEvents & FD_ACCEPT) {
+ // There is no specific accept callback (because the BSD sockets
+ // don't work with specific notification for accept); we use
+ // the read callback instead.
+ bool closed;
+ if (networkEvents.iErrorCode[FD_READ_BIT] != 0) {
+ // No special handling is required; the callback function
+ // will try to do an accept() and will get the error then.
+ }
+
+ closed = NetManager_doReadCallback(netDescriptors[index]);
+ if (closed)
+ goto closed;
+ }
+#if 0
+ // No need for this. Windows also sets FD_WRITE in this case, and
+ // writability is what we check for anyhow.
+ if (networkEvents.lNetworkEvents & FD_CONNECT) {
+ // There is no specific connect callback (because the BSD sockets
+ // don't work with specific notification for connect); we use
+ // the write callback instead.
+ bool closed;
+ if (networkEvents.iErrorCode[FD_WRITE_BIT] != 0) {
+ // No special handling is required; the callback function
+ // should do getsockopt() with SO_ERROR to get the error.
+ }
+
+ closed = NetManager_doWriteCallback(netDescriptors[index]);
+ if (closed)
+ goto closed;
+ }
+#endif
+ if (networkEvents.lNetworkEvents & FD_CLOSE) {
+ // The close event is handled last, in case there was still
+ // data in the buffers which could be processed.
+ NetDescriptor_close(netDescriptors[index]);
+ goto closed;
+ }
+
+closed: /* No special actions required for now. */
+
+ return 0;
+}
+
+// This function may be called again from inside a callback function
+// triggered by this function. BUG: This may result in callbacks being
+// called multiple times.
+// This function should however not be called from multiple threads at once.
+int
+NetManager_process(uint32 *timeoutMs) {
+ DWORD timeoutTemp;
+ DWORD waitResult;
+ DWORD startEvent;
+
+ timeoutTemp = (DWORD) *timeoutMs;
+
+ // WSAWaitForMultipleEvents only reports events for one socket at a
+ // time. In order to have each socket checked once, we call it
+ // again after it has reported an event, but passing only the
+ // events not yet processed. The second time, the timeout will be set
+ // to 0, so it won't wait.
+ startEvent = 0;
+ while (startEvent < numActiveEvents) {
+ waitResult = WSAWaitForMultipleEvents(numActiveEvents - startEvent,
+ &events[startEvent], FALSE, timeoutTemp, FALSE);
+
+ if (waitResult == WSA_WAIT_IO_COMPLETION)
+ continue;
+
+ if (waitResult == WSA_WAIT_TIMEOUT) {
+ // No events waiting.
+ *timeoutMs = 0;
+ return 0;
+ }
+
+ if (waitResult == WSA_WAIT_FAILED) {
+ errno = getWinsockErrno();
+ *timeoutMs = timeoutTemp;
+ return -1;
+ }
+
+ {
+ DWORD eventIndex = waitResult - WSA_WAIT_EVENT_0;
+ if (NetManager_processEvent((size_t) eventIndex) == -1) {
+ // errno is set
+ *timeoutMs = timeoutTemp;
+ return -1;
+ }
+
+ // Check the rest of the sockets, but don't wait anymore.
+ startEvent += eventIndex + 1;
+ timeoutTemp = 0;
+ }
+ }
+
+ *timeoutMs = timeoutTemp;
+ return 0;
+}
+
+
diff --git a/src/libs/network/netmanager/netmanager_win.h b/src/libs/network/netmanager/netmanager_win.h
new file mode 100644
index 0000000..03d65e4
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_win.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_NETMANAGER_NETMANAGER_WIN_H_
+#define LIBS_NETWORK_NETMANAGER_NETMANAGER_WIN_H_
+
+typedef struct SocketManagementDataWin SocketManagementDataWin;
+typedef SocketManagementDataWin SocketManagementData;
+
+#ifdef NETMANAGER_INTERNAL
+struct SocketManagementDataWin {
+ size_t index;
+ long eventMask;
+};
+#endif /* NETMANAGER_INTERNAL */
+
+
+#endif /* LIBS_NETWORK_NETMANAGER_NETMANAGER_WIN_H_ */
+
+
diff --git a/src/libs/network/netport.c b/src/libs/network/netport.c
new file mode 100644
index 0000000..bfd478c
--- /dev/null
+++ b/src/libs/network/netport.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+#include "netport.h"
+
+#ifdef USE_WINSOCK
+# include <winsock2.h>
+
+int
+winsockErrorToErrno(int winsockError) {
+ switch (winsockError) {
+ case WSAEINTR: return EINTR;
+ case WSAEACCES: return EACCES;
+ case WSAEFAULT: return EFAULT;
+ case WSAEINVAL: return EINVAL;
+ case WSAEMFILE: return EMFILE;
+ case WSAEWOULDBLOCK: return EWOULDBLOCK;
+ case WSAEINPROGRESS: return EINPROGRESS;
+ case WSAEALREADY: return EALREADY;
+ case WSAENOTSOCK: return ENOTSOCK;
+ case WSAEDESTADDRREQ: return EDESTADDRREQ;
+ case WSAEMSGSIZE: return EMSGSIZE;
+ case WSAEPROTOTYPE: return EPROTOTYPE;
+ case WSAENOPROTOOPT: return ENOPROTOOPT;
+ case WSAEPROTONOSUPPORT: return EPROTONOSUPPORT;
+ case WSAESOCKTNOSUPPORT: return ESOCKTNOSUPPORT;
+ case WSAEOPNOTSUPP: return EOPNOTSUPP;
+ case WSAEPFNOSUPPORT: return EPFNOSUPPORT;
+ case WSAEAFNOSUPPORT: return EAFNOSUPPORT;
+ case WSAEADDRINUSE: return EADDRINUSE;
+ case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL;
+ case WSAENETDOWN: return ENETDOWN;
+ case WSAENETUNREACH: return ENETUNREACH;
+ case WSAENETRESET: return ENETRESET;
+ case WSAECONNABORTED: return ECONNABORTED;
+ case WSAECONNRESET: return ECONNRESET;
+ case WSAENOBUFS: return ENOBUFS;
+ case WSAEISCONN: return EISCONN;
+ case WSAENOTCONN: return ENOTCONN;
+ case WSAESHUTDOWN: return ESHUTDOWN;
+ case WSAETIMEDOUT: return ETIMEDOUT;
+ case WSAECONNREFUSED: return ECONNREFUSED;
+ case WSAEHOSTDOWN: return EHOSTDOWN;
+ case WSAEHOSTUNREACH: return EHOSTUNREACH;
+ case WSAEPROCLIM: return EPROCLIM;
+ case WSASYSNOTREADY: return ENOSYS;
+ case WSAVERNOTSUPPORTED: return ENOSYS;
+ case WSANOTINITIALISED: return ENOSYS;
+ case WSAEDISCON: return ECONNRESET;
+ case WSATYPE_NOT_FOUND: return ENODATA;
+ case WSAHOST_NOT_FOUND: return ENODATA;
+ case WSATRY_AGAIN: return EAGAIN;
+ case WSANO_RECOVERY: return EIO;
+ case WSANO_DATA: return ENODATA;
+ case WSA_INVALID_HANDLE: return EBADF;
+ case WSA_INVALID_PARAMETER: return EINVAL;
+ case WSA_IO_INCOMPLETE: return EAGAIN;
+ case WSA_IO_PENDING: return EINPROGRESS;
+ case WSA_NOT_ENOUGH_MEMORY: return ENOMEM;
+ case WSA_OPERATION_ABORTED: return EINTR;
+ case WSAEINVALIDPROCTABLE: return ENOSYS;
+ case WSAEINVALIDPROVIDER: return ENOSYS;
+ case WSAEPROVIDERFAILEDINIT: return ENOSYS;
+ case WSASYSCALLFAILURE: return EIO;
+ default: return EIO;
+ }
+}
+
+int
+getWinsockErrno(void) {
+ return winsockErrorToErrno(WSAGetLastError());
+}
+#endif /* defined (USE_WINSOCK) */
+
diff --git a/src/libs/network/netport.h b/src/libs/network/netport.h
new file mode 100644
index 0000000..ee99699
--- /dev/null
+++ b/src/libs/network/netport.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_NETPORT_H_
+#define LIBS_NETWORK_NETPORT_H_
+
+#include "port.h"
+
+#ifdef USE_WINSOCK
+int winsockErrorToErrno(int winsockError);
+int getWinsockErrno(void);
+# define EAI_SYSTEM 0x02000001
+ // Any value will do that doesn't conflict with an existing value.
+
+#ifdef __MINGW32__
+// MinGW does not have a working gai_strerror() yet.
+static inline const char *
+gai_strerror(int err) {
+ (void) err;
+ return "[gai_strerror() is not available on MinGW]";
+}
+#endif /* defined(__MINGW32__) */
+
+#endif
+
+#endif /* LIBS_NETWORK_NETPORT_H_ */
+
+
diff --git a/src/libs/network/network.h b/src/libs/network/network.h
new file mode 100644
index 0000000..d8c596e
--- /dev/null
+++ b/src/libs/network/network.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_NETWORK_H_
+#define LIBS_NETWORK_NETWORK_H_
+
+void Network_init(void);
+void Network_uninit(void);
+
+#endif /* LIBS_NETWORK_NETWORK_H_ */
+
+
diff --git a/src/libs/network/network_bsd.c b/src/libs/network/network_bsd.c
new file mode 100644
index 0000000..a213c4e
--- /dev/null
+++ b/src/libs/network/network_bsd.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "network.h"
+
+void
+Network_init(void) {
+ // Nothing to do.
+}
+
+void
+Network_uninit(void) {
+ // Nothing to do.
+}
+
diff --git a/src/libs/network/network_win.c b/src/libs/network/network_win.c
new file mode 100644
index 0000000..a5c6abf
--- /dev/null
+++ b/src/libs/network/network_win.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "netport.h"
+
+#include "network.h"
+
+#include "libs/misc.h"
+#include "libs/log.h"
+
+#include <errno.h>
+#include <winsock2.h>
+
+void
+Network_init(void) {
+ WSADATA data;
+ int startupResult;
+ WORD requestVersion = MAKEWORD(2, 2);
+
+ startupResult = WSAStartup(requestVersion, &data);
+ if (startupResult != 0) {
+ int savedErrno = winsockErrorToErrno(startupResult);
+ log_add(log_Fatal, "WSAStartup failed.");
+ errno = savedErrno;
+ explode();
+ }
+
+#ifdef DEBUG
+ log_add(log_Debug, "Winsock version %d.%d found: \"%s\".",
+ LOBYTE(data.wHighVersion), HIBYTE(data.wHighVersion),
+ data.szDescription);
+ log_add(log_Debug, "Requesting to use Winsock version %d.%d, got "
+ "version %d.%d.",
+ LOBYTE(requestVersion), HIBYTE(requestVersion),
+ LOBYTE(data.wVersion), HIBYTE(data.wVersion));
+#endif
+ if (data.wVersion != requestVersion) {
+ log_add(log_Fatal, "Winsock version %d.%d presented, requested "
+ "%d.%d.", LOBYTE(data.wVersion), HIBYTE(data.wVersion),
+ LOBYTE(requestVersion), HIBYTE(requestVersion));
+ (void) WSACleanup();
+ // Ignoring errors; we're going to abort anyhow.
+ explode();
+ }
+}
+
+void
+Network_uninit(void) {
+ int cleanupResult;
+
+ cleanupResult = WSACleanup();
+ if (cleanupResult == SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ log_add(log_Fatal, "WSACleanup failed.");
+ errno = savedErrno;
+ explode();
+ }
+}
+
+
diff --git a/src/libs/network/socket/Makeinfo b/src/libs/network/socket/Makeinfo
new file mode 100644
index 0000000..19e1637
--- /dev/null
+++ b/src/libs/network/socket/Makeinfo
@@ -0,0 +1,11 @@
+uqm_CFILES="socket.c"
+uqm_HFILES="socket.h"
+
+if [ -n "$uqm_USE_WINSOCK" ]; then
+ uqm_CFILES="$uqm_CFILES socket_win.c"
+ uqm_HFILES="$uqm_HFILES socket_win.h"
+else
+ uqm_CFILES="$uqm_CFILES socket_bsd.c"
+ uqm_HFILES="$uqm_CFILES socket_bsd.h"
+fi
+
diff --git a/src/libs/network/socket/socket.c b/src/libs/network/socket/socket.c
new file mode 100644
index 0000000..be7d601
--- /dev/null
+++ b/src/libs/network/socket/socket.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "port.h"
+
+#define SOCKET_INTERNAL
+#include "socket.h"
+
+#ifdef USE_WINSOCK
+# include <winsock2.h>
+#else
+# include <sys/socket.h>
+# include <netinet/in.h>
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+const int protocolFamilyTranslation[] = {
+ /* .[PF_unspec] = */ PF_UNSPEC,
+ /* .[PF_inet] = */ PF_INET,
+ /* .[PF_inet6] = */ PF_INET6,
+};
+
+const int protocolTranslation[] = {
+ /* .[IPProto_tcp] = */ IPPROTO_TCP,
+ /* .[IPProto_udp] = */ IPPROTO_UDP,
+};
+
+const int socketTypeTranslation[] = {
+ /* .[Sock_stream] = */ SOCK_STREAM,
+ /* .[Sock_dgram] = */ SOCK_DGRAM,
+};
+
+
+Socket *
+Socket_open(ProtocolFamily domain, SocketType type, Protocol protocol) {
+ return Socket_openNative(protocolFamilyTranslation[domain],
+ socketTypeTranslation[type], protocolTranslation[protocol]);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
diff --git a/src/libs/network/socket/socket.h b/src/libs/network/socket/socket.h
new file mode 100644
index 0000000..5c75467
--- /dev/null
+++ b/src/libs/network/socket/socket.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_SOCKET_SOCKET_H_
+#define LIBS_NETWORK_SOCKET_SOCKET_H_
+
+typedef struct Socket Socket;
+#define Socket_noSocket ((Socket *) NULL)
+
+#include "port.h"
+
+#ifdef USE_WINSOCK
+# include "socket_win.h"
+#else
+# include "socket_bsd.h"
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+// Defining our own types for protocol families and protocols instead of
+// using the system defines, so that the layer using this API does not have
+// to have anything to do with the system layer.
+
+typedef enum {
+ PF_unspec,
+ PF_inet,
+ PF_inet6,
+} ProtocolFamily;
+typedef ProtocolFamily AddressFamily;
+
+typedef enum {
+ IPProto_tcp,
+ IPProto_udp,
+} Protocol;
+
+typedef enum {
+ Sock_stream,
+ Sock_dgram,
+} SocketType;
+
+#ifdef SOCKET_INTERNAL
+extern const int protocolFamilyTranslation[];
+#define addressFamilyTranslation protocolFamilyTranslation;
+extern const int protocolTranslation[];
+extern const int socketTypeTranslation[];
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+Socket *Socket_open(ProtocolFamily domain, SocketType type,
+ Protocol protocol);
+#ifdef SOCKET_INTERNAL
+Socket *Socket_openNative(int domain, int type, int protocol);
+#endif
+int Socket_close(Socket *sock);
+
+int Socket_connect(Socket *sock, const struct sockaddr *addr,
+ socklen_t addrLen);
+int Socket_bind(Socket *sock, const struct sockaddr *addr,
+ socklen_t addrLen);
+int Socket_listen(Socket *sock, int backlog);
+Socket *Socket_accept(Socket *sock, struct sockaddr *addr, socklen_t *addrLen);
+ssize_t Socket_send(Socket *sock, const void *buf, size_t len, int flags);
+ssize_t Socket_sendto(Socket *sock, const void *buf, size_t len, int flags,
+ const struct sockaddr *addr, socklen_t addrLen);
+ssize_t Socket_recv(Socket *sock, void *buf, size_t len, int flags);
+ssize_t Socket_recvfrom(Socket *sock, void *buf, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromLen);
+
+int Socket_setNonBlocking(Socket *sock);
+int Socket_setReuseAddr(Socket *sock);
+int Socket_setNodelay(Socket *sock);
+int Socket_setTOS(Socket *sock, int tos);
+int Socket_setInteractive(Socket *sock);
+int Socket_setInlineOOB(Socket *sock);
+int Socket_setKeepAlive(Socket *sock);
+int Socket_getError(Socket *sock, int *err);
+
+#endif /* LIBS_NETWORK_SOCKET_SOCKET_H_ */
+
diff --git a/src/libs/network/socket/socket_bsd.c b/src/libs/network/socket/socket_bsd.c
new file mode 100644
index 0000000..2bf25b8
--- /dev/null
+++ b/src/libs/network/socket/socket_bsd.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// Socket functions for BSD sockets.
+
+#define SOCKET_INTERNAL
+#include "socket.h"
+
+#include "libs/log.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+# include <netinet/in_systm.h>
+# include <netinet/in.h>
+#endif
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+static Socket *
+Socket_alloc(void) {
+ return malloc(sizeof (Socket));
+}
+
+static void
+Socket_free(Socket *sock) {
+ free(sock);
+}
+
+Socket *
+Socket_openNative(int domain, int type, int protocol) {
+ Socket *result;
+ int fd;
+
+ fd = socket(domain, type, protocol);
+ if (fd == -1) {
+ // errno is set
+ return Socket_noSocket;
+ }
+
+ result = Socket_alloc();
+ result->fd = fd;
+ return result;
+}
+
+int
+Socket_close(Socket *sock) {
+ int closeResult;
+
+ do {
+ closeResult = close(sock->fd);
+ if (closeResult == 0) {
+ Socket_free(sock);
+ return 0;
+ }
+ } while (errno == EINTR);
+
+ return -1;
+}
+
+int
+Socket_connect(Socket *sock, const struct sockaddr *addr,
+ socklen_t addrLen) {
+ int connectResult;
+
+ do {
+ connectResult = connect(sock->fd, addr, addrLen);
+ } while (connectResult == -1 && errno == EINTR);
+
+ return connectResult;
+}
+
+int
+Socket_bind(Socket *sock, const struct sockaddr *addr, socklen_t addrLen) {
+ return bind(sock->fd, addr, addrLen);
+}
+
+int
+Socket_listen(Socket *sock, int backlog) {
+ return listen(sock->fd, backlog);
+}
+
+Socket *
+Socket_accept(Socket *sock, struct sockaddr *addr, socklen_t *addrLen) {
+ int acceptResult;
+ socklen_t tempAddrLen;
+
+ do {
+ tempAddrLen = *addrLen;
+ acceptResult = accept(sock->fd, addr, &tempAddrLen);
+ if (acceptResult != -1) {
+ Socket *result = Socket_alloc();
+ result->fd = acceptResult;
+ *addrLen = tempAddrLen;
+ return result;
+ }
+
+ } while (errno == EINTR);
+
+ // errno is set
+ return Socket_noSocket;
+}
+
+ssize_t
+Socket_send(Socket *sock, const void *buf, size_t len, int flags) {
+ return send(sock->fd, buf, len, flags);
+}
+
+ssize_t
+Socket_sendto(Socket *sock, const void *buf, size_t len, int flags,
+ const struct sockaddr *addr, socklen_t addrLen) {
+ return sendto(sock->fd, buf, len, flags, addr, addrLen);
+}
+
+ssize_t
+Socket_recv(Socket *sock, void *buf, size_t len, int flags) {
+ return recv(sock->fd, buf, len, flags);
+}
+
+ssize_t
+Socket_recvfrom(Socket *sock, void *buf, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromLen) {
+ return recvfrom(sock->fd, buf, len, flags, from, fromLen);
+}
+
+int
+Socket_setNonBlocking(Socket *sock) {
+ int flags;
+
+ flags = fcntl(sock->fd, F_GETFL);
+ if (flags == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Getting file descriptor flags of socket failed: "
+ "%s.", strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+
+ if (fcntl(sock->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Setting non-blocking mode on socket failed: "
+ "%s.", strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+Socket_setReuseAddr(Socket *sock) {
+ int flag = 1;
+
+ if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof flag)
+ == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Setting socket reuse failed: %s.",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+// Send data as soon as it is available. Do not collect data to send at
+// once.
+int
+Socket_setNodelay(Socket *sock) {
+ int flag = 1;
+
+ if (setsockopt(sock->fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof flag)
+ == -1) {
+#ifdef DEBUG
+ int savedErrno = errno;
+ log_add(log_Warning, "Disabling Nagle algorithm failed: %s.",
+ strerror(errno));
+ errno = savedErrno;
+#endif
+ return -1;
+ }
+ return 0;
+}
+
+// 'tos' should be IPTOS_LOWDELAY, IPTOS_THROUGHPUT, IPTOS_THROUGHPUT,
+// IPTOS_RELIABILITY, or IPTOS_MINCOST.
+int
+Socket_setTOS(Socket *sock, int tos) {
+ if (setsockopt(sock->fd, IPPROTO_IP, IP_TOS, &tos, sizeof tos) == -1) {
+#ifdef DEBUG
+ int savedErrno = errno;
+ log_add(log_Warning, "Setting socket type-of-service failed: %s.",
+ strerror(errno));
+ errno = savedErrno;
+#endif
+ return -1;
+ }
+ return 0;
+}
+
+// This function setups the socket for optimal configuration for an
+// interactive connection.
+int
+Socket_setInteractive(Socket *sock) {
+ if (Socket_setNodelay(sock) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ if (Socket_setTOS(sock, IPTOS_LOWDELAY) == -1) {
+ // errno is set
+ return -1;
+ }
+ return 0;
+}
+
+int
+Socket_setInlineOOB(Socket *sock) {
+ int flag = 1;
+
+ if (setsockopt(sock->fd, SOL_SOCKET, SO_OOBINLINE, &flag, sizeof flag)
+ == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Setting inline OOB on socket failed: %s",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+int
+Socket_setKeepAlive(Socket *sock) {
+ int flag = 1;
+
+ if (setsockopt(sock->fd, IPPROTO_TCP, SO_KEEPALIVE, &flag, sizeof flag)
+ == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Setting keep-alive on socket failed: %s",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+int
+Socket_getError(Socket *sock, int *err) {
+ socklen_t errLen = sizeof(*err);
+
+ if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, err, &errLen) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ assert(errLen == sizeof(*err));
+ // err is set
+ return 0;
+}
+
+
diff --git a/src/libs/network/socket/socket_bsd.h b/src/libs/network/socket/socket_bsd.h
new file mode 100644
index 0000000..8019c01
--- /dev/null
+++ b/src/libs/network/socket/socket_bsd.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_SOCKET_SOCKET_BSD_H_
+#define LIBS_NETWORK_SOCKET_SOCKET_BSD_H_
+
+#include "types.h"
+#include <sys/socket.h>
+
+#ifdef SOCKET_INTERNAL
+struct Socket {
+ int fd;
+};
+#endif /* SOCKET_INTERNAL */
+
+
+#endif /* LIBS_NETWORK_SOCKET_SOCKET_BSD_H_ */
+
diff --git a/src/libs/network/socket/socket_win.c b/src/libs/network/socket/socket_win.c
new file mode 100644
index 0000000..e1ed002
--- /dev/null
+++ b/src/libs/network/socket/socket_win.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// Socket functions for Winsock sockets.
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+#include "../netport.h"
+
+#define SOCKET_INTERNAL
+#include "socket.h"
+
+#include "libs/log.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <winsock2.h>
+
+
+Socket *
+Socket_alloc(void) {
+ return malloc(sizeof (Socket));
+}
+
+void
+Socket_free(Socket *sock) {
+ free(sock);
+}
+
+Socket *
+Socket_openNative(int domain, int type, int protocol) {
+ Socket *result;
+ SOCKET sock;
+
+ sock = socket(domain, type, protocol);
+ if (sock == INVALID_SOCKET) {
+ errno = getWinsockErrno();
+ return Socket_noSocket;
+ }
+
+ result = Socket_alloc();
+ result->sock = sock;
+ return result;
+}
+
+int
+Socket_close(Socket *sock) {
+ int closeResult;
+
+ do {
+ closeResult = closesocket(sock->sock);
+ if (closeResult != SOCKET_ERROR) {
+ Socket_free(sock);
+ return 0;
+ }
+
+ errno = getWinsockErrno();
+ } while (errno == EINTR);
+
+ return -1;
+}
+
+int
+Socket_connect(Socket *sock, const struct sockaddr *addr,
+ socklen_t addrLen) {
+ int connectResult;
+
+ do {
+ connectResult = connect(sock->sock, addr, addrLen);
+ if (connectResult == 0)
+ return 0;
+
+ errno = getWinsockErrno();
+ } while (errno == EINTR);
+
+ if (errno == EWOULDBLOCK) {
+ // Windows returns (WSA)EWOULDBLOCK when a connection is being
+ // initiated on a non-blocking socket, while other platforms
+ // use EINPROGRESS in such cases.
+ errno = EINPROGRESS;
+ }
+
+ return -1;
+}
+
+int
+Socket_bind(Socket *sock, const struct sockaddr *addr, socklen_t addrLen) {
+ int bindResult;
+
+ bindResult = bind(sock->sock, addr, addrLen);
+ if (bindResult == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+Socket_listen(Socket *sock, int backlog) {
+ int listenResult;
+
+ listenResult = listen(sock->sock, backlog);
+ if (listenResult == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ return 0;
+}
+
+Socket *
+Socket_accept(Socket *sock, struct sockaddr *addr, socklen_t *addrLen) {
+ SOCKET acceptResult;
+ socklen_t tempAddrLen;
+
+ do {
+ tempAddrLen = *addrLen;
+ acceptResult = accept(sock->sock, addr, &tempAddrLen);
+ if (acceptResult != INVALID_SOCKET) {
+ Socket *result = Socket_alloc();
+ result->sock = acceptResult;
+ *addrLen = tempAddrLen;
+ return result;
+ }
+
+ errno = getWinsockErrno();
+ } while (errno == EINTR);
+
+ // errno is set
+ return Socket_noSocket;
+}
+
+ssize_t
+Socket_send(Socket *sock, const void *buf, size_t len, int flags) {
+ int sendResult;
+
+ sendResult = send(sock->sock, buf, len, flags);
+ if (sendResult == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ return sendResult;
+}
+
+ssize_t
+Socket_sendto(Socket *sock, const void *buf, size_t len, int flags,
+ const struct sockaddr *addr, socklen_t addrLen) {
+ int sendResult;
+
+ sendResult = sendto(sock->sock, buf, len, flags, addr, addrLen);
+ if (sendResult == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ return sendResult;
+}
+
+ssize_t
+Socket_recv(Socket *sock, void *buf, size_t len, int flags) {
+ int recvResult;
+
+ recvResult = recv(sock->sock, buf, len, flags);
+ if (recvResult == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ return recvResult;
+}
+
+ssize_t
+Socket_recvfrom(Socket *sock, void *buf, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromLen) {
+ int recvResult;
+
+ recvResult = recvfrom(sock->sock, buf, len, flags, from, fromLen);
+ if (recvResult == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ return recvResult;
+}
+
+int
+Socket_setNonBlocking(Socket *sock) {
+ unsigned long flag = 1;
+
+ if (ioctlsocket(sock->sock, FIONBIO, &flag) == SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ log_add(log_Error, "Setting non-block mode on socket failed: %s.",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+int
+Socket_setReuseAddr(Socket *sock) {
+ BOOL flag = 1;
+
+ if (setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char *) &flag, sizeof flag) == SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ log_add(log_Error, "Setting socket reuse failed: %s.",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+// Send data as soon as it is available. Do not collect data to send at
+// once.
+int
+Socket_setNodelay(Socket *sock) {
+ BOOL flag = 1;
+
+ if (setsockopt(sock->sock, IPPROTO_TCP, TCP_NODELAY,
+ (const char *) &flag, sizeof flag) == SOCKET_ERROR) {
+#ifdef DEBUG
+ int savedErrno = getWinsockErrno();
+ log_add(log_Warning, "Disabling Nagle algorithm failed: %s.",
+ strerror(errno));
+ errno = savedErrno;
+#endif
+ return -1;
+ }
+ return 0;
+}
+
+// This function setups the socket for optimal configuration for an
+// interactive connection.
+int
+Socket_setInteractive(Socket *sock) {
+ if (Socket_setNodelay(sock) == -1) {
+ // errno is set
+ return -1;
+ }
+
+#if 0
+ if (Socket_setTOS(sock, IPTOS_LOWDELAY) == -1) {
+ // errno is set
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int
+Socket_setInlineOOB(Socket *sock) {
+ BOOL flag = 1;
+
+ if (setsockopt(sock->sock, SOL_SOCKET, SO_OOBINLINE, (const char *) &flag,
+ sizeof flag) == SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ log_add(log_Error, "Setting inline OOB on socket failed: %s",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+int
+Socket_setKeepAlive(Socket *sock) {
+ BOOL flag = 1;
+
+ if (setsockopt(sock->sock, IPPROTO_TCP, SO_KEEPALIVE,
+ (const char *) &flag, sizeof flag) == SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ log_add(log_Error, "Setting keep-alive on socket failed: %s",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+int
+Socket_getError(Socket *sock, int *err) {
+ int errLen = sizeof(*err);
+
+ if (getsockopt(sock->sock, SOL_SOCKET, SO_ERROR, (char *) err, &errLen)
+ == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ assert(errLen == sizeof(*err));
+ // err is set
+ return 0;
+}
+
+
diff --git a/src/libs/network/socket/socket_win.h b/src/libs/network/socket/socket_win.h
new file mode 100644
index 0000000..ed1674c
--- /dev/null
+++ b/src/libs/network/socket/socket_win.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_SOCKET_SOCKET_WIN_H_
+#define LIBS_NETWORK_SOCKET_SOCKET_WIN_H_
+
+#ifdef SOCKET_INTERNAL
+#include <winsock2.h>
+struct Socket {
+ SOCKET sock;
+};
+#endif /* SOCKET_INTERNAL */
+
+typedef int socklen_t;
+struct sockaddr;
+
+#endif /* LIBS_NETWORK_SOCKET_SOCKET_WIN_H_ */
+
+
diff --git a/src/libs/network/wspiapiwrap.c b/src/libs/network/wspiapiwrap.c
new file mode 100644
index 0000000..2eddeb4
--- /dev/null
+++ b/src/libs/network/wspiapiwrap.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// Only used for MinGW
+
+// HACK. MinGW misses some functionality, so we're #including
+// the actual Windows wspiapi.h file. Because that file includes
+// inline functions that aren't static, it can only be included
+// once per executable when using gcc (for MSVC this is apparently
+// not a problem), so this file is it. The prototypes of these
+// functions are added to wspiapiwrap.h
+
+#include "netport.h"
+
+#if defined(USE_WINSOCK) && defined(__MINGW32__)
+# include <ws2tcpip.h>
+# include <wspiapi.h>
+#endif
+
diff --git a/src/libs/network/wspiapiwrap.h b/src/libs/network/wspiapiwrap.h
new file mode 100644
index 0000000..23361a4
--- /dev/null
+++ b/src/libs/network/wspiapiwrap.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_WSPIAPIWRAP_H_
+#define LIBS_NETWORK_WSPIAPIWRAP_H_
+
+// HACK. See wspiapiwrap.c
+# define getaddrinfo WspiapiGetAddrInfo
+# define getnameinfo WspiapiGetNameInfo
+# define freeaddrinfo WspiapiFreeAddrInfo
+void WINAPI WspiapiFreeAddrInfo (struct addrinfo *ai);
+int WINAPI WspiapiGetAddrInfo(const char *nodename, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res);
+int WINAPI WspiapiGetNameInfo (const struct sockaddr *sa, socklen_t salen,
+ char *host, size_t hostlen, char *serv, size_t servlen, int flags);
+
+#endif /* LIBS_NETWORK_WSPIAPIWRAP_H_ */
+