diff options
author | Nick Mathewson <nickm@torproject.org> | 2009-05-05 02:59:26 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2009-05-05 02:59:26 +0000 |
commit | 659d54d5304c476f5cf0ec502235e376e1d2d8a1 (patch) | |
tree | d57d4cebc154d2fe73a0e458940ab6fb2d66f9b4 /listener.c | |
parent | 0fd70978c8d83f0f677a0d119f2737d0b68ac511 (diff) | |
download | libevent-659d54d5304c476f5cf0ec502235e376e1d2d8a1.tar.gz |
Add new code to make and accept connections.
This is stuff that it's easy to get wrong (as I noticed when writing
bench_http), and that takes up a fair amount of space (see http.c).
Also, it's something that we'll eventually want to abstract to use
IOCP, where available.
svn:r1272
Diffstat (limited to 'listener.c')
-rw-r--r-- | listener.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/listener.c b/listener.c new file mode 100644 index 00000000..a84bbeb8 --- /dev/null +++ b/listener.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2009 Niels Provos, Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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> + +#ifdef HAVE_CONFIG_H +#include "event-config.h" +#endif + +#ifdef WIN32 +#include <winsock2.h> +#endif +#include <errno.h> +#ifdef _EVENT_HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef _EVENT_HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef _EVENT_HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <event2/listener.h> +#include <event2/util.h> +#include <event2/event.h> +#include <event2/event_struct.h> +#include "mm-internal.h" +#include "util-internal.h" +#include "log-internal.h" + +struct evconnlistener { + struct event listener; + evconnlistener_cb cb; + void *user_data; + unsigned flags; +}; + +static void listener_read_cb(evutil_socket_t, short, void *); + +struct evconnlistener * +evconnlistener_new(struct event_base *base, + evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, + evutil_socket_t fd) +{ + struct evconnlistener *lev; + if (backlog > 0) { + if (listen(fd, backlog) < 0) + return NULL; + } + lev = mm_calloc(1, sizeof(struct evconnlistener)); + if (!lev) + return NULL; + lev->cb = cb; + lev->user_data = ptr; + lev->flags = flags; + event_assign(&lev->listener, base, fd, EV_READ|EV_PERSIST, + listener_read_cb, lev); + evconnlistener_enable(lev); + return lev; +} + +struct evconnlistener * +evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb, void *ptr, + unsigned flags, int backlog, const struct sockaddr *sa, int socklen) +{ + evutil_socket_t fd; + int on = 1; + int family = sa ? sa->sa_family : AF_UNSPEC; + + fd = socket(family, SOCK_STREAM, 0); + if (fd == -1) + return NULL; + if (evutil_make_socket_nonblocking(fd) < 0) + return NULL; + +#ifndef WIN32 + if (flags & LEV_OPT_CLOSE_ON_EXEC) { + if (fcntl(fd, F_SETFD, 1) == -1) { + EVUTIL_CLOSESOCKET(fd); + return NULL; + } + } +#endif + + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on)); + if (flags & LEV_OPT_REUSEABLE) { + evutil_make_listen_socket_reuseable(fd); + } + + if (sa) { + if (bind(fd, sa, socklen)<0) { + EVUTIL_CLOSESOCKET(fd); + return NULL; + } + } + + return evconnlistener_new(base, cb, ptr, flags, backlog, fd); +} + +void +evconnlistener_free(struct evconnlistener *lev) +{ + event_del(&lev->listener); + if (lev->flags & LEV_OPT_CLOSE_ON_FREE) + EVUTIL_CLOSESOCKET(event_get_fd(&lev->listener)); + mm_free(lev); +} + +int +evconnlistener_enable(struct evconnlistener *lev) +{ + return event_add(&lev->listener, NULL); +} + +int +evconnlistener_disable(struct evconnlistener *lev) +{ + return event_del(&lev->listener); +} + +static void +listener_read_cb(evutil_socket_t fd, short what, void *p) +{ + struct evconnlistener *lev = p; + int err; + while (1) { + struct sockaddr_storage ss; + socklen_t socklen = sizeof(ss); + + evutil_socket_t new_fd = accept(fd, (struct sockaddr*)&ss, &socklen); + if (new_fd < 0) + break; + + if (!(lev->flags & LEV_OPT_LEAVE_SOCKETS_BLOCKING)) + evutil_make_socket_nonblocking(new_fd); + + lev->cb(new_fd, (struct sockaddr*)&ss, (int)socklen, + lev->user_data); + } + err = evutil_socket_geterror(fd); + if (EVUTIL_ERR_ACCEPT_RETRIABLE(err)) + return; + event_sock_warn(fd, "Error from accept() call"); +} |