summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/async-resolver.c366
-rw-r--r--common/async-resolver.h48
-rw-r--r--common/server-mainloop.c434
-rw-r--r--common/server-mainloop.h56
-rw-r--r--common/sock-any.c406
-rw-r--r--common/sock-any.h103
6 files changed, 1413 insertions, 0 deletions
diff --git a/common/async-resolver.c b/common/async-resolver.c
new file mode 100644
index 0000000..8485bb2
--- /dev/null
+++ b/common/async-resolver.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2006, Stefan Walter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "async-resolver.h"
+#include "server-mainloop.h"
+
+/* -----------------------------------------------------------------------------
+ * THREAD COMMUNICATION
+ */
+
+#define TSIGNAL_UNITITIALIZED { -1, -1 }
+
+static int
+tsignal_init(int* sig)
+{
+ if(pipe(sig) == -1)
+ return -1;
+ fcntl(sig[0], F_SETFL, fcntl(sig[0], F_GETFL, 0) | O_NONBLOCK);
+ return 0;
+}
+
+static int
+tsignal_get_fd(int* sig)
+{
+ return sig[0];
+}
+
+static void
+tsignal_wake(int* sig)
+{
+ write(sig[1], "1", 1);
+}
+
+static void
+tsignal_clear(int* sig)
+{
+ char buf[16];
+ while(read(sig[0], buf, sizeof(buf)) > 0);
+}
+
+static void
+tsignal_wait(int* sig, struct timeval* tv)
+{
+ fd_set watch;
+ FD_ZERO(&watch);
+ FD_SET(sig[0], &watch);
+ select(sig[0], &watch, NULL, NULL, tv);
+}
+
+static void
+tsignal_uninit(int* sig)
+{
+ if(sig[1] != -1)
+ close(sig[1]);
+ sig[1] = -1;
+ if(sig[0] != -1)
+ close(sig[0]);
+ sig[0] = -1;
+}
+
+/* -----------------------------------------------------------------------------
+ * RESOLVER
+ */
+
+typedef struct _resolve_request
+{
+ char hostname[256];
+ char servname[256];
+ struct addrinfo hints;
+ async_resolve_callback cb;
+ void *arg;
+
+ int gaierr;
+ int errn;
+ struct addrinfo *ai;
+
+ struct _resolve_request *next;
+}
+resolve_request;
+
+/* The queues */
+static int res_quit = 0;
+static resolve_request* res_requests = NULL;
+static resolve_request* res_done = NULL;
+
+/* Thread communication */
+static pthread_t res_thread = 0;
+static pthread_mutex_t res_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int res_request_signal[2] = TSIGNAL_UNITITIALIZED;
+static int res_done_signal[2] = TSIGNAL_UNITITIALIZED;
+
+static void*
+resolver_thread(void* arg)
+{
+ resolve_request* req;
+ resolve_request* r;
+ struct timeval tv;
+
+ while(!res_quit)
+ {
+ pthread_mutex_lock(&res_mutex);
+
+ /* Dig out any requests */
+ req = res_requests;
+ if(req)
+ {
+ res_requests = req->next;
+ req->next = NULL;
+ }
+
+ pthread_mutex_unlock(&res_mutex);
+
+ /* No requests, wait for a request */
+ if(!req)
+ {
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000;
+ tsignal_wait(res_request_signal, &tv);
+ tsignal_clear(res_request_signal);
+ continue;
+ }
+
+ /* The actual resolve */
+ req->gaierr = getaddrinfo(req->hostname, req->servname[0] ? req->servname : NULL,
+ &req->hints, &req->ai);
+ req->errn = errno;
+
+ /* A timeout */
+ if(!req->gaierr && !req->ai)
+ {
+ req->gaierr = EAI_SYSTEM;
+ req->errn = ETIMEDOUT;
+ }
+
+ /* Append the result to done */
+ pthread_mutex_lock(&res_mutex);
+
+ if(!res_done)
+ {
+ res_done = req;
+ }
+ else
+ {
+ r = res_done;
+ while(r->next)
+ r = r->next;
+ r->next = req;
+ }
+
+ pthread_mutex_unlock(&res_mutex);
+
+ /* Tell the main thread to check outbound */
+ tsignal_wake(res_done_signal);
+ }
+
+ return NULL;
+}
+
+static void
+resolver_done(int fd, int type, void* arg)
+{
+ resolve_request* req;
+ resolve_request* r;
+
+ tsignal_clear(res_done_signal);
+
+ pthread_mutex_lock(&res_mutex);
+
+ req = res_done;
+ res_done = NULL;
+
+ pthread_mutex_unlock(&res_mutex);
+
+ while(req)
+ {
+ /* Send off the result */
+ errno = req->errn;
+ (req->cb)(req->gaierr, req->ai, req->arg);
+
+ /* And free it all */
+ r = req->next;
+ if(req->ai)
+ freeaddrinfo(req->ai);
+ free(req);
+
+ req = r;
+ }
+}
+
+int
+async_resolver_init()
+{
+ int r;
+
+ /* The signal pipes */
+ if(tsignal_init(res_request_signal) < 0)
+ return -1;
+ if(tsignal_init(res_done_signal) < 0)
+ return -1;
+
+ if(server_watch(tsignal_get_fd(res_done_signal), SERVER_READ, resolver_done, NULL) == -1)
+ return -1;
+
+ r = pthread_create(&res_thread, NULL, resolver_thread, NULL);
+ if(r != 0)
+ {
+ res_thread = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+async_resolver_queue(const char* hostname, const char* servname,
+ struct addrinfo* hints, async_resolve_callback cb, void* arg)
+{
+ resolve_request* req;
+ resolve_request* r;
+ char* t;
+
+ if(!res_thread)
+ {
+ /* All errors go to callback */
+ errno = ESRCH;
+ (cb)(EAI_SYSTEM, NULL, arg);
+ return;
+ }
+
+ req = calloc(1, sizeof(resolve_request));
+ if(!req)
+ {
+ /* All errors go to callback */
+ (cb)(EAI_MEMORY, NULL, arg);
+ return;
+ }
+
+ req->cb = cb;
+ req->arg = arg;
+
+ strncpy(req->hostname, hostname, sizeof(req->hostname));
+ req->hostname[sizeof(req->hostname) - 1] = 0;
+
+ /* A colon and we try to split */
+ t = strchr(req->hostname, ':');
+ if(t)
+ {
+ *t = 0;
+ strncpy(req->servname, t + 1, sizeof(req->servname));
+ }
+
+ if(servname && !req->servname[0])
+ strncpy(req->servname, servname, sizeof(req->servname));
+ req->servname[sizeof(req->servname) - 1] = 0;
+
+ if(hints)
+ memcpy(&(req->hints), hints, sizeof(req->hints));
+
+ /* Append the result to requests */
+ pthread_mutex_lock(&res_mutex);
+
+ if(!res_requests)
+ {
+ res_requests = req;
+ }
+ else
+ {
+ r = res_requests;
+ while(r->next)
+ r = r->next;
+ r->next = req;
+ }
+
+ pthread_mutex_unlock(&res_mutex);
+
+ tsignal_wake(res_request_signal);
+}
+
+void
+async_resolver_uninit()
+{
+ resolve_request* req;
+
+ /* No more responses from this point on */
+ if(tsignal_get_fd(res_done_signal) != -1)
+ server_unwatch(tsignal_get_fd(res_done_signal));
+
+ pthread_mutex_lock(&res_mutex);
+
+ while(res_requests)
+ {
+ req = res_requests->next;
+ if(res_requests->ai)
+ freeaddrinfo(res_requests->ai);
+ free(res_requests);
+ res_requests = req;
+ }
+
+ while(res_done)
+ {
+ req = res_done->next;
+ if(res_done->ai)
+ freeaddrinfo(res_done->ai);
+ free(res_done);
+ res_done = req;
+ }
+
+ pthread_mutex_unlock(&res_mutex);
+
+ /* Wake up the resolver thread */
+ res_quit = 1;
+ tsignal_uninit(res_request_signal);
+
+ /* Wait for it to finish */
+ if(res_thread)
+ {
+ pthread_join(res_thread, NULL);
+ res_thread = 0;
+ }
+
+ /* And close up the signals in the other direction */
+ tsignal_uninit(res_done_signal);
+}
diff --git a/common/async-resolver.h b/common/async-resolver.h
new file mode 100644
index 0000000..105c39a
--- /dev/null
+++ b/common/async-resolver.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2006, Stefan Walter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef __ASYNC_RESOLVER_H__
+#define __ASYNC_RESOLVER_H__
+
+#include <netdb.h>
+
+typedef void (*async_resolve_callback)(int ecode, struct addrinfo* ai, void* arg);
+
+int async_resolver_init (void);
+void async_resolver_uninit (void);
+
+void async_resolver_queue(const char* hostname, const char* servname,
+ struct addrinfo* hints, async_resolve_callback cb, void* arg);
+
+
+#endif /* __ASYNC_RESOLVER_H__ */
diff --git a/common/server-mainloop.c b/common/server-mainloop.c
new file mode 100644
index 0000000..fcd471d
--- /dev/null
+++ b/common/server-mainloop.c
@@ -0,0 +1,434 @@
+/*
+ * Copyright (c) 2006, Stefan Walter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "server-mainloop.h"
+
+#include <sys/time.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+typedef struct _socket_callback {
+ int fd;
+ server_socket_callback callback;
+ void *arg;
+
+ struct _socket_callback *next;
+} socket_callback;
+
+typedef struct _timer_callback {
+ struct timeval at;
+ struct timeval interval;
+ server_timer_callback callback;
+ void* arg;
+
+ struct _timer_callback *next;
+} timer_callback;
+
+typedef struct _server_context {
+ int stopped;
+ fd_set read_fds;
+ fd_set write_fds;
+ int max_fd;
+ socket_callback *callbacks;
+ timer_callback *timers;
+} server_context;
+
+/* Global context */
+static server_context ctx;
+
+static void
+timeval_add(struct timeval *t1, struct timeval *t2)
+{
+ assert (t1->tv_usec < 1000000);
+ assert (t2->tv_usec < 1000000);
+
+ t1->tv_sec += t2->tv_sec;
+ t1->tv_usec += t2->tv_usec;
+ if (t1->tv_usec >= 1000000) {
+ t1->tv_usec -= 1000000;
+ t1->tv_sec += 1;
+ }
+}
+
+static void
+timeval_subtract (struct timeval *t1, struct timeval *t2)
+{
+ assert (t1->tv_usec < 1000000);
+ assert (t2->tv_usec < 1000000);
+
+ t1->tv_sec -= t2->tv_sec;
+ if (t1->tv_usec < t2->tv_usec) {
+ t1->tv_usec += 1000000;
+ t1->tv_sec -= 1;
+ }
+ t1->tv_usec -= t2->tv_usec;
+}
+
+static int
+timeval_compare(struct timeval *t1, struct timeval *t2)
+{
+ assert (t1->tv_usec < 1000000);
+ assert (t2->tv_usec < 1000000);
+
+ if (t1->tv_sec > t2->tv_sec)
+ return 1;
+ else if (t1->tv_sec < t2->tv_sec)
+ return -1;
+ else {
+ if (t1->tv_usec > t2->tv_usec)
+ return 1;
+ else if (t1->tv_usec < t2->tv_usec)
+ return -1;
+ else
+ return 0;
+ }
+}
+
+#define timeval_empty(tv) \
+ ((tv)->tv_sec == 0 && (tv)->tv_usec == 0)
+
+#define timeval_to_ms(tv) \
+ ((((uint64_t)(tv).tv_sec) * 1000L) + (((uint64_t)(tv).tv_usec) / 1000L))
+
+#define timeval_dump(tv) \
+ (fprintf(stderr, "{ %d:%d }", (uint)((tv).tv_sec), (uint)((tv).tv_usec / 1000)))
+
+static int
+add_timer (int ms, int oneshot, server_timer_callback callback, void *arg)
+{
+ struct timeval interval;
+ timer_callback *cb;
+
+ assert (ms || oneshot);
+ assert (callback != NULL);
+
+ interval.tv_sec = ms / 1000;
+ interval.tv_usec = (ms % 1000) * 1000; /* into micro seconds */
+
+ cb = (timer_callback*)calloc (1, sizeof (*cb));
+ if(!cb) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (gettimeofday (&(cb->at), NULL) == -1) {
+ free(cb);
+ return -1;
+ }
+
+ timeval_add (&(cb->at), &interval);
+
+ if (oneshot)
+ memset (&(cb->interval), 0, sizeof (cb->interval));
+ else
+ memcpy (&(cb->interval), &interval, sizeof(cb->interval));
+
+ cb->callback = callback;
+ cb->arg = arg;
+
+ cb->next = ctx.timers;
+ ctx.timers = cb;
+
+ return 0;
+}
+
+static timer_callback*
+remove_timer(timer_callback *timcb)
+{
+ timer_callback *cb;
+ timer_callback *next;
+
+ if (!ctx.timers)
+ return NULL;
+
+ /* First in list */;
+ if (ctx.timers == timcb) {
+ cb = ctx.timers;
+ ctx.timers = ctx.timers->next;
+ free (cb);
+ return ctx.timers;
+ }
+
+ /* One ahead processing of rest */
+ for (cb = ctx.timers; cb->next; cb = cb->next) {
+ if(cb->next == timcb) {
+ next = cb->next->next;
+ free (cb->next);
+ cb->next = next;
+ return cb->next;
+ }
+ }
+
+ /* Couldn't remove, return self */
+ return timcb;
+}
+
+void
+server_init (void)
+{
+ memset (&ctx, 0, sizeof (ctx));
+ FD_ZERO (&ctx.read_fds);
+ FD_ZERO (&ctx.write_fds);
+
+ ctx.max_fd = -1;
+ ctx.stopped = 1;
+ ctx.callbacks = NULL;
+ ctx.timers = NULL;
+}
+
+void
+server_uninit (void)
+{
+ timer_callback *timcb;
+ timer_callback *timn;
+ socket_callback *sockcb;
+ socket_callback *sockn;
+
+ for(timcb = ctx.timers; timcb; timcb = timn) {
+ timn = timcb->next;
+ free (timcb);
+ }
+
+ ctx.timers = NULL;
+
+ for(sockcb = ctx.callbacks; sockcb; sockcb = sockn) {
+ sockn = sockcb->next;
+ free (sockcb);
+ }
+
+ ctx.timers = NULL;
+}
+
+uint64_t
+server_get_time (void)
+{
+ struct timeval tv;
+ if (gettimeofday (&tv, NULL) == -1)
+ return 0L;
+ return timeval_to_ms (tv);
+}
+
+int
+server_run (void)
+{
+ struct timeval *timeout;
+ struct timeval tv, current;
+ timer_callback *timcb;
+ socket_callback *sockcb, *socknx;
+ fd_set rfds, wfds;
+ int r;
+
+ /* No watches have been set */
+ assert (ctx.max_fd > -1);
+
+ ctx.stopped = 0;
+
+ while (!ctx.stopped) {
+ /* Watch for the various fds */
+ memcpy (&rfds, &ctx.read_fds, sizeof (rfds));
+ memcpy (&wfds, &ctx.write_fds, sizeof (wfds));
+
+ /* Prepare for timers */
+ timeout = NULL;
+ if (gettimeofday (&current, NULL) == -1)
+ return -1;
+
+ /* Cycle through timers */
+ for (timcb = ctx.timers; timcb; ) {
+ assert (timcb->callback);
+
+ /* Call any timers that have already passed */
+ if (timeval_compare (&current, &timcb->at) >= 0) {
+
+ /* Convert to milliseconds, and make the call */
+ r = (timcb->callback) (timeval_to_ms (current), timcb->arg);
+
+ /* Reset timer if so desired */
+ if (r == 1 && !timeval_empty (&timcb->interval)) {
+ timeval_add (&timcb->at, &timcb->interval);
+
+ /* If the time has already passed, just use current time */
+ if (timeval_compare (&(timcb->at), &current) <= 0)
+ memcpy (&(timcb->at), &current, sizeof (timcb->at));
+
+ /* Otherwise remove it. Either one shot, or returned 0 */
+ } else {
+ timcb = remove_timer (timcb);
+ continue;
+ }
+ }
+
+ /* Get soonest timer */
+ if (!timeout || timeval_compare (&timcb->at, timeout) < 0)
+ timeout = &timcb->at;
+
+ timcb = timcb->next;
+ }
+
+ /* Convert to an offset */
+ if (timeout) {
+ memcpy (&tv, timeout, sizeof (tv));
+ timeout = &tv;
+ timeval_subtract (timeout, &current);
+ }
+
+ if (ctx.stopped)
+ continue;
+
+ /* fprintf(stderr, "selecting with timeout: ");
+ timeval_dump(timeout);
+ fprintf(stderr, "\n"); */
+
+ r = select (ctx.max_fd, &rfds, &wfds, NULL, timeout);
+ if (r < 0) {
+ /* Interrupted so try again, and possibly exit */
+ if (errno == EINTR)
+ continue;
+
+ /* Programmer errors */
+ assert (errno != EBADF);
+ assert (errno != EINVAL);
+ return r;
+ }
+
+ /* Timeout, just jump to timeout processing */
+ if(r == 0)
+ continue;
+
+ for (sockcb = ctx.callbacks; sockcb; ) {
+ assert (sockcb->fd != -1);
+ socknx = sockcb->next;
+
+ /* Call any that are set */
+ if (FD_ISSET (sockcb->fd, &rfds))
+ (sockcb->callback) (sockcb->fd, SERVER_READ, sockcb->arg);
+ if (FD_ISSET (sockcb->fd, &wfds))
+ (sockcb->callback) (sockcb->fd, SERVER_WRITE, sockcb->arg);
+
+ sockcb = socknx;
+ }
+ }
+
+ return 0;
+}
+
+void
+server_stop (void)
+{
+ ctx.stopped = 1;
+}
+
+int
+server_stopped (void)
+{
+ return ctx.stopped;
+}
+
+int
+server_watch (int fd, int type, server_socket_callback callback, void *arg)
+{
+ socket_callback *cb;
+ assert (type != 0);
+ assert (fd != -1);
+ assert (callback != NULL);
+
+ cb = (socket_callback*)calloc (sizeof(*cb), 1);
+ if(!cb) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ cb->fd = fd;
+ cb->callback = callback;
+ cb->arg = arg;
+
+ cb->next = ctx.callbacks;
+ ctx.callbacks = cb;
+
+ if (type & SERVER_READ)
+ FD_SET (fd, &ctx.read_fds);
+ if (type & SERVER_WRITE)
+ FD_SET (fd, &ctx.write_fds);
+
+ if (fd >= ctx.max_fd)
+ ctx.max_fd = fd + 1;
+
+ return 0;
+}
+
+void
+server_unwatch (int fd)
+{
+ socket_callback* cb;
+
+ assert (fd != -1);
+
+ FD_CLR (fd, &ctx.read_fds);
+ FD_CLR (fd, &ctx.write_fds);
+
+ if (!ctx.callbacks)
+ return;
+
+ /* First in list */;
+ if (ctx.callbacks->fd == fd) {
+ cb = ctx.callbacks;
+ ctx.callbacks = cb->next;
+ free (cb);
+ return;
+ }
+
+ /* One ahead processing of rest */
+ for (cb = ctx.callbacks; cb->next; cb = cb->next) {
+ if (cb->next->fd == fd) {
+ cb->next = cb->next->next;
+ free (cb->next);
+ return;
+ }
+ }
+}
+
+int
+server_timer (int ms, server_timer_callback callback, void *arg)
+{
+ return add_timer (ms, 0, callback, arg);
+}
+
+int
+server_oneshot (int ms, server_timer_callback callback, void *arg)
+{
+ return add_timer (ms, 1, callback, arg);
+}
diff --git a/common/server-mainloop.h b/common/server-mainloop.h
new file mode 100644
index 0000000..09c7846
--- /dev/null
+++ b/common/server-mainloop.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2006, Stefan Walter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef __SERVER_MAINLOOP_H__
+#define __SERVER_MAINLOOP_H__
+
+#include <stdint.h>
+
+#define SERVER_READ 0x01
+#define SERVER_WRITE 0x02
+
+typedef void (*server_socket_callback) (int fd, int type, void* arg);
+typedef int (*server_timer_callback) (uint64_t when, void* arg);
+
+void server_init (void);
+void server_uninit (void);
+int server_run (void);
+void server_stop (void);
+int server_stopped (void);
+int server_watch (int fd, int type, server_socket_callback callback, void* arg);
+void server_unwatch (int fd);
+int server_timer (int length, server_timer_callback callback, void* arg);
+int server_oneshot (int length, server_timer_callback callback, void* arg);
+uint64_t server_get_time (void);
+
+#endif /* __SERVER_MAINLOOP_H__ */
diff --git a/common/sock-any.c b/common/sock-any.c
new file mode 100644
index 0000000..4476fde
--- /dev/null
+++ b/common/sock-any.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2004, Stefan Walter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Stef Walter <stef@memberwebs.com>
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "sock-any.h"
+
+#include <arpa/inet.h>
+
+#define LOCALHOST_ADDR 0x7F000001
+
+int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts)
+{
+ size_t l;
+ char buf[256];
+ char* t;
+ char* t2;
+ int defport = (opts & 0xFFFF);
+
+ memset(any, 0, sizeof(*any));
+
+ /* Just a port? */
+ do
+ {
+ #define PORT_CHARS "0123456789"
+ #define PORT_MIN 1
+ #define PORT_MAX 5
+
+ int port = 0;
+
+ l = strspn(addr, PORT_CHARS);
+ if(l < PORT_MIN || l > PORT_MAX || addr[l] != 0)
+ break;
+
+ port = strtol(addr, &t2, 10);
+ if(*t2 || port <= 0 || port >= 65536)
+ break;
+
+ any->s.in.sin_port = htons(port);
+
+ /* Fill in the type based on defaults */
+#ifdef HAVE_INET6
+ if(opts & SANY_OPT_DEFINET6)
+ any->s.in.sin_family = AF_INET6;
+ else
+#endif
+ any->s.in.sin_family = AF_INET;
+
+ /* Fill in the address based on defaults */
+ if(opts & SANY_OPT_DEFLOCAL)
+ {
+#ifdef HAVE_INET6
+ if(opts & SANY_OPT_DEFINET6)
+ memcpy(&(any->s.in.sin6_addr), &in6addr_loopback, sizeof(struct in6_addr));
+ else
+#endif
+ any->s.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ }
+
+ /*
+ * Note the 'any' option is the default since we zero out
+ * the entire structure above.
+ */
+
+ any->namelen = sizeof(any->s.in);
+ return AF_INET;
+ }
+ while(0);
+
+ /* Look and see if we can parse an ipv4 address */
+ do
+ {
+ #define IPV4_PORT_CHARS
+ #define IPV4_CHARS "0123456789."
+ #define IPV4_MIN 3
+ #define IPV4_MAX 21
+
+ int port = 0;
+ t = NULL;
+
+ l = strlen(addr);
+ if(l < IPV4_MIN || l > IPV4_MAX)
+ break;
+
+ strcpy(buf, addr);
+
+ /* Find the last set that contains just numbers */
+ l = strspn(buf, IPV4_CHARS);
+ if(l < IPV4_MIN)
+ break;
+
+ /* Either end of string or port */
+ if(buf[l] != 0 && buf[l] != ':')
+ break;
+
+ /* Get the port out */
+ if(buf[l] != 0)
+ {
+ t = buf + l + 1;
+ buf[l] = 0;
+ }
+
+ if(t)
+ {
+ port = strtol(t, &t2, 10);
+ if(*t2 || port <= 0 || port >= 65536)
+ break;
+ }
+
+ any->s.in.sin_family = AF_INET;
+ any->s.in.sin_port = htons((unsigned short)(port <= 0 ? defport : port));
+
+ if(inet_pton(AF_INET, buf, &(any->s.in.sin_addr)) <= 0)
+ break;
+
+ any->namelen = sizeof(any->s.in);
+ return AF_INET;
+ }
+ while(0);
+
+#ifdef HAVE_INET6
+ do
+ {
+ #define IPV6_CHARS "0123456789:"
+ #define IPV6_MIN 3
+ #define IPV6_MAX 51
+
+ int port = -1;
+ t = NULL;
+
+ l = strlen(addr);
+ if(l < IPV6_MIN || l > IPV6_MAX)
+ break;
+
+ /* If it starts with a '[' then we can get port */
+ if(buf[0] == '[')
+ {
+ port = 0;
+ addr++;
+ }
+
+ strcpy(buf, addr);
+
+ /* Find the last set that contains just numbers */
+ l = strspn(buf, IPV6_CHARS);
+ if(l < IPV6_MIN)
+ break;
+
+ /* Either end of string or port */
+ if(buf[l] != 0)
+ {
+ /* If had bracket, then needs to end with a bracket */
+ if(port != 0 || buf[l] != ']')
+ break;
+
+ /* Get the port out */
+ t = buf + l + 1;
+
+ if(*t = ':')
+ t++;
+ }
+
+ if(t)
+ {
+ port = strtol(t, &t, 10);
+ if(*t || port <= 0 || port >= 65536)
+ break;
+ }
+
+ any->s.in6.sin6_family = AF_INET6;
+ any->s.in6.sin6_port = htons((unsigned short)port <= 0 : defport : port);
+
+ if(inet_pton(AF_INET6, buf, &(any->s.in6.sin6_addr)) >= 0)
+ break;
+
+ any->namelen = sizeof(any->s.in6);
+ return AF_INET6;
+ }
+ while(0);
+#endif
+
+ /* A unix socket path */
+ do
+ {
+ /* No colon and must have a path component */
+ if(strchr(addr, ':') || !strchr(addr, '/'))
+ break;
+
+ l = strlen(addr);
+ if(l >= sizeof(any->s.un.sun_path))
+ break;
+
+ any->s.un.sun_family = AF_UNIX;
+ strcpy(any->s.un.sun_path, addr);
+
+ any->namelen = sizeof(any->s.un) - (sizeof(any->s.un.sun_path) - l);
+ return AF_UNIX;
+ }
+ while(0);
+
+ /* A DNS name and a port? */
+ do
+ {
+ struct addrinfo* res;
+ int port = 0;
+ int family = 0;
+ t = NULL;
+
+ l = strlen(addr);
+ if(l >= 255 || !isalpha(addr[0]))
+ break;
+
+ /* Some basic illegal character checks */
+ if(strcspn(addr, " /\\") != l)
+ break;
+
+ strcpy(buf, addr);
+
+ /* Find the last set that contains just numbers */
+ t = strchr(buf, ':');
+ if(t)
+ {
+ *t = 0;
+ t++;
+ }
+
+ if(t)
+ {
+ port = strtol(t, &t2, 10);
+ if(*t2 || port <= 0 || port >= 65536)
+ break;
+ }
+
+ if(!(opts & SANY_OPT_NORESOLV))
+ {
+ /* Try and resolve the domain name */
+ if(getaddrinfo(buf, NULL, NULL, &res) != 0 || !res)
+ break;
+
+ memcpy(&(any->s.a), res->ai_addr, sizeof(struct sockaddr));
+ any->namelen = res->ai_addrlen;
+ family = any->s.a.sa_family;
+ freeaddrinfo(res);
+ }
+ else
+ {
+ family = SANY_AF_DNS;
+#ifdef HAVE_INET6
+ if(opt & SANY_OPT_DEFINET6)
+ {
+ any->s.a.sa_family = AF_INET6;
+ any->namelen = sizeof(any->s.in6);
+ }
+ else
+#endif
+ {
+ any->s.a.sa_family = AF_INET;
+ any->namelen = sizeof(any->s.in);
+ }
+ }
+
+ port = htons((unsigned short)(port <= 0 ? defport : port));
+
+ switch(any->s.a.sa_family)
+ {
+ case PF_INET:
+ any->s.in.sin_port = port;
+ break;
+#ifdef HAVE_INET6
+ case PF_INET6:
+ any->s.in6.sin6_port = port;
+ break;
+#endif
+ };
+
+ return family;
+ }
+ while(0);
+
+ return -1;
+}
+
+int sock_any_ntop(const struct sockaddr_any* any, char* addr, size_t addrlen, int opts)
+{
+ int len = 0;
+ int port = 0;
+
+ switch(any->s.a.sa_family)
+ {
+ case AF_UNIX:
+ len = strlen(any->s.un.sun_path);
+ if(addrlen < len + 1)
+ {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ strcpy(addr, any->s.un.sun_path);
+ break;
+
+ case AF_INET:
+ if(inet_ntop(any->s.a.sa_family, &(any->s.in.sin_addr), addr, addrlen) == NULL)
+ return -1;
+ port = ntohs(any->s.in.sin_port);
+ break;
+
+#ifdef HAVE_INET6
+ case AF_INET6:
+ if(inet_ntop(any->s.a.sa_family, &(any->s.in6.sin6_addr), addr, addrlen) == NULL)
+ return -1;
+ port = ntohs(any->s.in6.sin6_port);
+ break;
+#endif
+
+ default:
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ if(!(opts & SANY_OPT_NOPORT) && port != 0)
+ {
+ strncat(addr, ":", addrlen);
+ addr[addrlen - 1] = 0;
+
+ len = strlen(addr);
+ addr += len;
+ addrlen -= len;
+
+ snprintf(addr, addrlen, "%d", port);
+ }
+
+ return 0;
+}
+
+int sock_any_cmp(const struct sockaddr_any* a1, const struct sockaddr_any* a2, int opts)
+{
+ if(a1->s.a.sa_family != a2->s.a.sa_family)
+ return -1;
+
+ switch(a1->s.a.sa_family)
+ {
+ case AF_UNIX:
+ return strcmp(a1->s.un.sun_path, a2->s.un.sun_path);
+
+ case AF_INET:
+ if(memcmp(&(a1->s.in.sin_addr), &(a2->s.in.sin_addr), sizeof(a2->s.in.sin_addr)) != 0)
+ return -1;
+ if(!(opts && SANY_OPT_NOPORT) && a1->s.in.sin_port != a2->s.in.sin_port)
+ return -1;
+ return 0;
+#ifdef HAVE_INET6
+ case AF_INET6:
+ if(memcmp(&(a1->s.in6.sin6_addr), &(a2->s.in6.sin6_addr), sizeof(a2->s.in6.sin6_addr)) != 0)
+ return -1;
+ if(!(opts && SANY_OPT_NOPORT) && a1->s.in6.sin6_port != a2->s.in6.sin6_port)
+ return -1;
+ return 0;
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+}
diff --git a/common/sock-any.h b/common/sock-any.h
new file mode 100644
index 0000000..e9b57ef
--- /dev/null
+++ b/common/sock-any.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2004, Stefan Walter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Stef Walter <stef@memberwebs.com>
+ *
+ */
+
+#ifndef __SOCK_ANY_H__
+#define __SOCK_ANY_H__
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+struct sockaddr_any
+{
+ union _sockaddr_any
+ {
+ /* The header */
+ struct sockaddr a;
+
+ /* The different types */
+ struct sockaddr_un un;
+ struct sockaddr_in in;
+#ifdef HAVE_INET6
+ struct sockaddr_in6 in6;
+#endif
+ } s;
+ size_t namelen;
+};
+
+#define SANY_ADDR(any) ((any).s.a)
+#define SANY_LEN(any) ((any).namelen)
+#define SANY_TYPE(any) ((any).s.a.sa_family)
+
+/* -------------------------------------------------------------------------- */
+
+/* Returns AF_XXX family type or -1 */
+int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts);
+
+/* The default port to fill in when no IP/IPv6 port specified */
+#define SANY_OPT_DEFPORT(p) (int)((p) & 0xFFFF)
+
+/* When only port specified default to IPANY */
+#define SANY_OPT_DEFANY 0x00000000
+
+/* When only port specified default to LOCALHOST */
+#define SANY_OPT_DEFLOCAL 0x00100000
+
+/* When only port specified default to IPv6 */
+#define SANY_OPT_DEFINET6 0x00200000
+
+/* Don't resolve host name */
+#define SANY_OPT_NORESOLV 0x01000000
+
+/* The family type returned when resolving is needed */
+#define SANY_AF_DNS 0x01000000
+
+/* -------------------------------------------------------------------------- */
+
+/* Returns -1 when failed */
+int sock_any_ntop(const struct sockaddr_any* any, char* addr, size_t addrlen, int opts);
+
+/* Don't print or compare the port */
+#define SANY_OPT_NOPORT 0x01000000
+
+/* -------------------------------------------------------------------------- */
+
+/* Returns 0 for equal */
+int sock_any_cmp(const struct sockaddr_any* a1, const struct sockaddr_any* a2, int opts);
+
+#endif /* __SOCK_ANY_H__ */