From 8982c72e5c941d3b5e77cda9b2bf84fc3b6d80ff Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Tue, 23 Jan 2007 13:46:18 +0000 Subject: [PATCH] Netevent work git-svn-id: file:///svn/unbound/trunk@27 be551aaa-1e26-0410-a405-d3ace91eadb9 --- Makefile.in | 4 +- configure.ac | 1 + doc/Changelog | 4 + doc/plan | 2 +- util/log.c | 21 ++++- util/log.h | 11 ++- util/netevent.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++ util/netevent.h | 135 +++++++++++++++++++++++---- 8 files changed, 392 insertions(+), 26 deletions(-) create mode 100644 util/netevent.c diff --git a/Makefile.in b/Makefile.in index 0d44e9ffd..557c7a555 100644 --- a/Makefile.in +++ b/Makefile.in @@ -67,11 +67,11 @@ all: $(COMMON_OBJ) unbound unittest unbound: $(COMMON_OBJ) $(DAEMON_OBJ) $(INFO) Link $@ - $Q$(LINK) -o $@ $^ + $Q$(LINK) -o $@ $^ $(LIBS) unittest: $(COMMON_OBJ) $(UNITTEST_OBJ) $(INFO) Link $@ - $Q$(LINK) -o $@ $^ + $Q$(LINK) -o $@ $^ $(LIBS) clean: rm -f *.o *.d *.lo *~ tags diff --git a/configure.ac b/configure.ac index a331450b3..7e592f749 100644 --- a/configure.ac +++ b/configure.ac @@ -188,6 +188,7 @@ AC_CHECK_TYPE(in_port_t, [], [AC_DEFINE([in_port_t], [uint16_t], [in_port_t])], # check to see if libraries are needed for these functions. AC_CHECK_LIB(socket, socket) AC_CHECK_LIB(nsl, inet_pton) +AC_CHECK_LIB(event, event_set) AC_FUNC_MALLOC diff --git a/doc/Changelog b/doc/Changelog index 5049a9666..f517f0b80 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +23 January 2007: Wouter + - added libevent to configure to link with. + - util/netevent setup work. + 22 January 2007: Wouter - Designed header file for network communication. diff --git a/doc/plan b/doc/plan index c23cdee90..7a90a1705 100644 --- a/doc/plan +++ b/doc/plan @@ -35,7 +35,7 @@ Roughly the boxes are as follows: 0.8 Library use - resolver validator lib (and test apps) 0.9 Corner cases - be able to resolve in the wild. Run fuzzers. Run as many tests as we can think of. -1.0 El product. Run shadow for a resolver in production for several +0.10 Beta release. Run shadow for a resolver in production for several weeks. For boxes 0.5-1.0 the planning is to be revised, at the 0.5 stage external diff --git a/util/log.c b/util/log.c index 56e4e2151..3c20045bf 100644 --- a/util/log.c +++ b/util/log.c @@ -23,13 +23,13 @@ log_init() } void -log_vmsg(const char *format, va_list args) +log_vmsg(const char* type, const char *format, va_list args) { char message[MAXSYSLOGMSGLEN]; const char* ident="unbound"; vsnprintf(message, sizeof(message), format, args); - fprintf(stderr, "[%d] %s[%d]: %s\n", - (int)time(NULL), ident, (int)getpid(), message); + fprintf(stderr, "[%d] %s[%d] %s: %s\n", + (int)time(NULL), ident, (int)getpid(), type, message); } /** @@ -41,6 +41,19 @@ log_info(const char *format, ...) { va_list args; va_start(args, format); - log_vmsg(format, args); + log_vmsg("info", format, args); + va_end(args); +} + +/** + * implementation of log_err + * @param format: format string printf-style. + */ +void +log_err(const char *format, ...) +{ + va_list args; + va_start(args, format); + log_vmsg("error", format, args); va_end(args); } diff --git a/util/log.h b/util/log.h index 06f0a27b0..bffe11286 100644 --- a/util/log.h +++ b/util/log.h @@ -27,16 +27,25 @@ void log_init(); /** + * Log informational message. * Pass printf formatted arguments. No trailing newline is needed. * @param format: printf-style format string. Arguments follow. */ void log_info(const char* format, ...) ATTR_FORMAT(printf, 1, 2); +/** + * Log error message. + * Pass printf formatted arguments. No trailing newline is needed. + * @param format: printf-style format string. Arguments follow. + */ +void log_err(const char* format, ...) ATTR_FORMAT(printf, 1, 2); + /** * va_list argument version of log_info. + * @param type: string to designate type of message (info, error). * @param format: the printf style format to print. no newline. * @param args: arguments for format string. */ -void log_vmsg(const char *format, va_list args); +void log_vmsg(const char* type, const char *format, va_list args); #endif /* UTIL_LOG_H */ diff --git a/util/netevent.c b/util/netevent.c new file mode 100644 index 000000000..c26552644 --- /dev/null +++ b/util/netevent.c @@ -0,0 +1,240 @@ +/* + * util/netevent.c - event notification + * + * Copyright (c) 2007, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + * + */ + +/** + * \file + * + * This file contains event notification functions. + */ + +#include "util/netevent.h" +#include "util/log.h" +#include + +/* we use libevent */ +#include + +/** + * The internal event structure for keeping libevent info for the event. + * Possibly other structures (list, tree) this is part of. + */ +struct internal_event { + /** libevent event type, alloced here */ + struct event ev; +}; + +/** + * Internal base structure, so that every thread has its own events. + */ +struct internal_base { + /** libevent event_base type. */ + struct event_base* base; +}; + +struct comm_base* comm_base_create() +{ + struct comm_base* b = (struct comm_base*)calloc(1, + sizeof(struct comm_base)); + if(!b) + return NULL; + b->eb = (struct internal_base*)calloc(1, sizeof(struct internal_base)); + if(!b->eb) { + free(b); + return NULL; + } + b->eb->base = event_init(); + if(!b->eb->base) { + free(b->eb); + free(b); + return NULL; + } + return b; +} + +void comm_base_delete(struct comm_base* b) +{ + /* No way to delete event_base! leaks. */ + b->eb->base = NULL; + free(b->eb); + free(b); +} + +void comm_base_dispatch(struct comm_base* b) +{ + int retval; + while(1) { + retval = event_base_dispatch(b->eb->base); + if(retval != 0) { + log_err("event_dispatch returned error %d, " + "errno is %s", retval, strerror(errno)); + } + } +} + +/** + * libevent callback routine for commpoint udp + * @param fd: file descriptor. + * @param event: event bits from libevent: + * EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT. + * @param arg: the comm_point structure. + */ +static void +comm_point_udp_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(event), + void* arg) +{ + struct comm_point* c = (struct comm_point*)arg; + log_info("callback udp for %x", (int)c); +} + +/** + * libevent callback routine for commpoint tcp accept listeners. + * @param fd: file descriptor. + * @param event: event bits from libevent: + * EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT. + * @param arg: the comm_point structure. + */ +static void +comm_point_tcp_accept_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(event), + void* arg) +{ + struct comm_point* c = (struct comm_point*)arg; + log_info("callback tcpaccept for %x", (int)c); +} + +struct comm_point* comm_point_create_udp(struct comm_base *base, + int fd, struct buffer* buffer, + comm_point_callback_t* callback, void* callback_arg) +{ + struct comm_point* c = (struct comm_point*)calloc(1, + sizeof(struct comm_point)); + short evbits; + if(!c) + return NULL; + c->ev = (struct internal_event*)calloc(1, + sizeof(struct internal_event)); + if(!c->ev) { + free(c); + return NULL; + } + c->fd = fd; + c->buffer = buffer; + c->timeout = NULL; + c->tcp_is_reading = 0; + c->tcp_byte_count = 0; + c->tcp_parent = NULL; + c->cur_tcp_count = 0; + c->max_tcp_count = 0; + c->tcp_handlers = NULL; + c->tcp_free = NULL; + c->type = comm_udp; + c->tcp_do_close = 0; + c->tcp_do_toggle_rw = 0; + c->callback = callback; + c->cb_arg = callback_arg; + evbits = EV_READ | EV_PERSIST; + /* libevent stuff */ + event_set(&c->ev->ev, c->fd, evbits, comm_point_udp_callback, c); + if(event_base_set(base->eb->base, &c->ev->ev) != 0 || + event_add(&c->ev->ev, c->timeout) != 0 ) { + log_err("could not add udp event"); + comm_point_delete(c); + return NULL; + } + return c; +} + +struct comm_point* +comm_point_create_tcp_handler(struct comm_base *base, + struct comm_point* parent, size_t bufsize, + comm_point_callback_t* callback, void* callback_arg) +{ + return NULL; +} + +struct comm_point* +comm_point_create_tcp(struct comm_base *base, int fd, int num, size_t bufsize, + comm_point_callback_t* callback, void* callback_arg) +{ + struct comm_point* c = (struct comm_point*)calloc(1, + sizeof(struct comm_point)); + short evbits; + int i; + /* first allocate the TCP accept listener */ + if(!c) + return NULL; + c->ev = (struct internal_event*)calloc(1, + sizeof(struct internal_event)); + if(!c->ev) { + free(c); + return NULL; + } + c->fd = fd; + c->buffer = NULL; + c->timeout = NULL; + c->tcp_is_reading = 0; + c->tcp_byte_count = 0; + c->tcp_parent = NULL; + c->cur_tcp_count = 0; + c->max_tcp_count = num; + c->tcp_handlers = (struct comm_point**)calloc(num, + sizeof(struct comm_point*)); + c->tcp_free = NULL; + c->type = comm_tcp_accept; + c->tcp_do_close = 0; + c->tcp_do_toggle_rw = 0; + c->callback = NULL; + c->cb_arg = NULL; + evbits = EV_READ | EV_PERSIST; + /* libevent stuff */ + event_set(&c->ev->ev, c->fd, evbits, comm_point_tcp_accept_callback, c); + if( event_base_set(base->eb->base, &c->ev->ev) != 0 || + event_add(&c->ev->ev, c->timeout) != 0 ) + { + log_err("could not add tcpacc event"); + if(!event_del(&c->ev->ev)) { + log_err("could not event_del tcpacc event"); + } + free(c->tcp_handlers); + free(c->ev); + free(c); + return NULL; + } + + /* now prealloc the tcp handlers */ + for(i=0; itcp_handlers[i] = comm_point_create_tcp_handler(base, + c, bufsize, callback, callback_arg); + } + + return c; +} + +void comm_point_close(struct comm_point* c) +{ + if(c->fd != -1) + close(c->fd); + c->fd = -1; + if(event_del(&c->ev->ev) != 0) { + log_err("could not event_del on close"); + } +} + +void comm_point_delete(struct comm_point* c) +{ + comm_point_close(c); + if(c->tcp_handlers) { + int i; + for(i=0; imax_tcp_count; i++) + comm_point_delete(c->tcp_handlers[i]); + free(c->tcp_handlers); + } + free(c->ev); + free(c); +} + diff --git a/util/netevent.h b/util/netevent.h index e3632aaeb..e0cddb3d2 100644 --- a/util/netevent.h +++ b/util/netevent.h @@ -30,10 +30,31 @@ #include "config.h" struct buffer; -/** internal event notification data storage structure. */ +/* internal event notification data storage structure. */ struct internal_event; +struct internal_base; +struct comm_point; -/** Communication point to the network */ +/** callback from communication point function type */ +typedef int comm_point_callback_t(struct comm_point*, void*, int); + +/** + * A communication point dispatcher. Thread specific. + */ +struct comm_base { + /** behind the scenes structure. with say libevent info. alloced */ + struct internal_base* eb; +}; + +/** + * Communication point to the network + * These behaviours can be accomplished by setting the flags + * and passing return values from the callback. + * udp frontside: called after readdone. sendafter. + * tcp frontside: called readdone, sendafter. close. + * udp behind: called after readdone. No send after. + * tcp behind: write done, read done, then called. No send after. + */ struct comm_point { /** behind the scenes structure, with say libevent info. alloced. */ struct internal_event* ev; @@ -53,7 +74,7 @@ struct comm_point { /** The current read/write count for TCP */ size_t tcp_byte_count; /** parent communication point (for TCP sockets) */ - struct comm_point tcp_parent; + struct comm_point *tcp_parent; /* -------- TCP Accept -------- */ /** current number of TCP connections on this socket */ @@ -62,7 +83,7 @@ struct comm_point { int max_tcp_count; /** malloced array of tcp handlers for a tcp-accept, of size max_tcp_count. */ - struct comm_point *tcp_handlers; + struct comm_point **tcp_handlers; /** linked list of free tcp_handlers to use for new queries. For tcp_accept the first entry, for tcp_handlers the next one. */ struct comm_point *tcp_free; @@ -77,28 +98,106 @@ struct comm_point { comm_tcp } type; - /** what to do when read/write is done. + /* ---------- Behaviour ----------- */ + /** if set, the connection is closed on error, on timeout, + and after read/write completes. No callback is done. */ + int tcp_do_close; - For a query this means it is read in and ready to be processed. - After that the buffer will be sent back to client. - tcp_accept does not get called back, is NULL then. + /** if set, read/write completes: + read/write state of tcp is toggled. + buffer reset/bytecount reset. + this flag cleared. + So that when that is done the callback is called. */ + int tcp_do_toggle_rw; - udp frontside: called after readdone. sendafter. - tcp frontside: called readdone, sendafter. close. - udp behind: called after readdone. No send after. - tcp behind: write done, read done, then called. No send after. + /** callback when done. + tcp_accept does not get called back, is NULL then. + If a timeout happens, callback with timeout=1 is called. + If an error happens, callback is called with error set + nonzero. If nonzero, it is an errno value. + If the connection is closed (by remote end) then the + callback is called with error set to -1. declare as: - int my_callback(struct comm_point*, void* cb_arg, int timeout); + int my_callback(struct comm_point* c, void* my_arg, + int timeout, int error); - if the routine returns 0, no answer is sent back. - For TCP handlers after the answer is sent back the fd is closed. - If a timeout happens, TCP handler is closed, and callback with - timeout=1 is called. + if the routine returns 0, nothing is done. + Notzero, the buffer will be sent back to client. + For UDP this is done without changing the commpoint. + In TCP it sets write state. */ - int (*)(struct comm_point*, void*) callback; + comm_point_callback_t* callback; /** argument to pass to callback. */ void *cb_arg; }; +/** + * Create a new comm base. + * @return: the new comm base. NULL on error. + */ +struct comm_base* comm_base_create(); + +/** + * Destroy a comm base. + * All comm points must have been deleted. + * @param b: the base to delete. + */ +void comm_base_delete(struct comm_base* b); + +/** + * Dispatch the comm base events. + * @param b: the communication to perform. + */ +void comm_base_dispatch(struct comm_base* b); + +/** + * Create an UDP comm point. Calls malloc. + * setups the structure with the parameters you provide. + * @param base: in which base to alloc the commpoint. + * @param fd : file descriptor of open UDP socket. + * @param buffer: shared buffer by UDP sockets from this thread. + * @param callback: callback function pointer. + * @param callback_arg: will be passed to your callback function. + * @return: returns the allocated communication point. NULL on error. + * Sets timeout to NULL. Turns off TCP options. + */ +struct comm_point* comm_point_create_udp(struct comm_base *base, + int fd, struct buffer* buffer, + comm_point_callback_t* callback, void* callback_arg); + +/** + * Create a TCP listener comm point. Calls malloc. + * Setups the structure with the parameters you provide. + * Also Creates TCP Handlers, pre allocated for you. + * Uses the parameters you provide. + * @param base: in which base to alloc the commpoint. + * @param fd: file descriptor of open TCP socket set to listen nonblocking. + * @param num: becomes max_tcp_count, the routine allocates that + * many tcp handler commpoints. + * @param bufsize: size of buffer to create for handlers. + * @param callback: callback function pointer for TCP handlers. + * @param callback_arg: will be passed to your callback function. + * @return: returns the TCP listener commpoint. You can find the + * TCP handlers in the array inside the listener commpoint. + * returns NULL on error. + * Inits timeout to NULL. All handlers are on the free list. + */ +struct comm_point* comm_point_create_tcp(struct comm_base *base, + int fd, int num, size_t bufsize, + comm_point_callback_t* callback, void* callback_arg); + +/** + * Close a comm point fd. + * @param c: comm point to close. + */ +void comm_point_close(struct comm_point* c); + +/** + * Close and deallocate (free) the comm point. If the comm point is + * a tcp-accept point, also its tcp-handler points are deleted. + * @param c: comm point to delete. + */ +void comm_point_delete(struct comm_point* c); + #endif /* NET_EVENT_H */ -- 2.47.2