summaryrefslogtreecommitdiff
path: root/src/libs/network/connect
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/network/connect')
-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
7 files changed, 1485 insertions, 0 deletions
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_ */
+