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__ */  | 
