diff options
author | Stef Walter <stef@memberwebs.com> | 2008-06-07 20:36:27 +0000 |
---|---|---|
committer | Stef Walter <stef@memberwebs.com> | 2008-06-07 20:36:27 +0000 |
commit | abb82291887b6784a13a7fcf719fa1d463781007 (patch) | |
tree | 73975e8357db517f5b71818d107d665d92e8daa1 /common |
Initial import
Diffstat (limited to 'common')
-rw-r--r-- | common/async-resolver.c | 366 | ||||
-rw-r--r-- | common/async-resolver.h | 48 | ||||
-rw-r--r-- | common/server-mainloop.c | 434 | ||||
-rw-r--r-- | common/server-mainloop.h | 56 | ||||
-rw-r--r-- | common/sock-any.c | 406 | ||||
-rw-r--r-- | common/sock-any.h | 103 |
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 (¤t, 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 (¤t, &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), ¤t) <= 0) + memcpy (&(timcb->at), ¤t, 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, ¤t); + } + + 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__ */ |