mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-08 11:53:00 -04:00
Bufferevent support for openssl.
This code adds a new Bufferevent type that is only compiled when the openssl library is present. It supports using an SSL object and an event alert mechanism, which can either be an fd or an underlying bufferevent. There is still more work to do: the unit tests are incomplete, and we need to support flush and shutdown much better. Sometimes events are generated needlessly: this will hose performance. There's a new encrypting proxy in sample/le-proxy.c. This code has only been tested on OSX, and nowhere else. svn:r1382
This commit is contained in:
parent
b06b2649b4
commit
709c21c48c
@ -1,3 +1,6 @@
|
||||
Changes in 2.0.3-alpha:
|
||||
o Add a new code to support SSL/TLS on bufferevents, using the OpenSSL library (where available).
|
||||
|
||||
Changes in 2.0.2-alpha:
|
||||
o Add a new flag to bufferevents to make all callbacks automatically deferred.
|
||||
o Make evdns functionality locked, and automatically defer dns callbacks.
|
||||
|
@ -67,6 +67,9 @@ lib_LTLIBRARIES = libevent.la libevent_core.la libevent_extra.la
|
||||
if PTHREADS
|
||||
lib_LTLIBRARIES += libevent_pthreads.la
|
||||
endif
|
||||
if OPENSSL
|
||||
lib_LTLIBRARIES += libevent_openssl.la
|
||||
endif
|
||||
|
||||
SUBDIRS = . include sample test
|
||||
|
||||
@ -128,6 +131,12 @@ libevent_extra_la_SOURCES = $(EXTRA_SRC)
|
||||
libevent_extra_la_LIBADD =
|
||||
libevent_extra_la_LDFLAGS = -version-info $(VERSION_INFO)
|
||||
|
||||
if OPENSSL
|
||||
libevent_openssl_la_SOURCES = bufferevent_openssl.c
|
||||
libevent_openssl_la_LIBADD = -lcrypto -lssl
|
||||
libevent_openssl_la_LDFLAGS = -release $(RELEASE) -version-info $(VERSION_INFO)
|
||||
endif
|
||||
|
||||
noinst_HEADERS = util-internal.h mm-internal.h ipv6-internal.h \
|
||||
evrpc-internal.h strlcpy-internal.h evbuffer-internal.h \
|
||||
bufferevent-internal.h http-internal.h event-internal.h \
|
||||
|
@ -173,6 +173,10 @@ void _bufferevent_run_writecb(struct bufferevent *bufev);
|
||||
* it to run with events "what". Otherwise just run the eventcb. */
|
||||
void _bufferevent_run_eventcb(struct bufferevent *bufev, short what);
|
||||
|
||||
/** Internal: Add the event 'ev' with timeout tv, unless tv is set to 0, in
|
||||
* which case add ev with no timeout. */
|
||||
int _bufferevent_add_event(struct event *ev, const struct timeval *tv);
|
||||
|
||||
/* =========
|
||||
* These next functions implement timeouts for bufferevents that aren't doing
|
||||
* anything else with ev_read and ev_write, to handle timeouts.
|
||||
@ -227,4 +231,5 @@ void _bufferevent_generic_adj_timeouts(struct bufferevent *bev);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _BUFFEREVENT_INTERNAL_H_ */
|
||||
|
@ -325,7 +325,7 @@ bufferevent_enable(struct bufferevent *bufev, short event)
|
||||
short impl_events = event;
|
||||
int r = 0;
|
||||
|
||||
BEV_LOCK(bufev);
|
||||
_bufferevent_incref_and_lock(bufev);
|
||||
if (bufev_private->read_suspended)
|
||||
impl_events &= ~EV_READ;
|
||||
|
||||
@ -334,7 +334,7 @@ bufferevent_enable(struct bufferevent *bufev, short event)
|
||||
if (bufev->be_ops->enable(bufev, impl_events) < 0)
|
||||
r = -1;
|
||||
|
||||
BEV_UNLOCK(bufev);
|
||||
_bufferevent_decref_and_unlock(bufev);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -525,10 +525,17 @@ bufferevent_enable_locking(struct bufferevent *bufev, void *lock)
|
||||
#ifdef _EVENT_DISABLE_THREAD_SUPPORT
|
||||
return -1;
|
||||
#else
|
||||
struct bufferevent *underlying;
|
||||
|
||||
if (BEV_UPCAST(bufev)->lock)
|
||||
return -1;
|
||||
underlying = bufferevent_get_underlying(bufev);
|
||||
|
||||
if (!lock) {
|
||||
if (!lock && underlying && BEV_UPCAST(underlying)->lock) {
|
||||
lock = BEV_UPCAST(underlying)->lock;
|
||||
BEV_UPCAST(bufev)->lock = lock;
|
||||
BEV_UPCAST(bufev)->own_lock = 0;
|
||||
} else if (!lock) {
|
||||
EVTHREAD_ALLOC_LOCK(lock);
|
||||
if (!lock)
|
||||
return -1;
|
||||
@ -541,6 +548,9 @@ bufferevent_enable_locking(struct bufferevent *bufev, void *lock)
|
||||
evbuffer_enable_locking(bufev->input, lock);
|
||||
evbuffer_enable_locking(bufev->output, lock);
|
||||
|
||||
if (underlying && !BEV_UPCAST(underlying)->lock)
|
||||
bufferevent_enable_locking(underlying, lock);
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@ -632,3 +642,11 @@ _bufferevent_generic_adj_timeouts(struct bufferevent *bev)
|
||||
event_del(&bev->ev_write);
|
||||
}
|
||||
|
||||
int
|
||||
_bufferevent_add_event(struct event *ev, const struct timeval *tv)
|
||||
{
|
||||
if (tv->tv_sec == 0 && tv->tv_usec == 0)
|
||||
return event_add(ev, NULL);
|
||||
else
|
||||
return event_add(ev, tv);
|
||||
}
|
||||
|
@ -187,12 +187,7 @@ bufferevent_filter_new(struct bufferevent *underlying,
|
||||
return NULL;
|
||||
}
|
||||
if (options & BEV_OPT_THREADSAFE) {
|
||||
void *lock = BEV_UPCAST(underlying)->lock;
|
||||
if (!lock) {
|
||||
bufferevent_enable_locking(underlying, NULL);
|
||||
lock = BEV_UPCAST(underlying)->lock;
|
||||
}
|
||||
bufferevent_enable_locking(downcast(bufev_f), lock);
|
||||
bufferevent_enable_locking(downcast(bufev_f), NULL);
|
||||
}
|
||||
|
||||
bufev_f->underlying = underlying;
|
||||
|
1138
bufferevent_openssl.c
Normal file
1138
bufferevent_openssl.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -88,14 +88,8 @@ const struct bufferevent_ops bufferevent_ops_socket = {
|
||||
be_socket_ctrl,
|
||||
};
|
||||
|
||||
static int
|
||||
be_socket_add(struct event *ev, const struct timeval *tv)
|
||||
{
|
||||
if (tv->tv_sec == 0 && tv->tv_usec == 0)
|
||||
return event_add(ev, NULL);
|
||||
else
|
||||
return event_add(ev, tv);
|
||||
}
|
||||
#define be_socket_add(ev, t) \
|
||||
_bufferevent_add_event((ev), (t))
|
||||
|
||||
static void
|
||||
bufferevent_socket_outbuf_cb(struct evbuffer *buf,
|
||||
@ -285,45 +279,35 @@ bufferevent_socket_connect(struct bufferevent *bev,
|
||||
struct bufferevent_private *bufev_p =
|
||||
EVUTIL_UPCAST(bev, struct bufferevent_private, bev);
|
||||
|
||||
int family = sa->sa_family;
|
||||
evutil_socket_t fd;
|
||||
int made_socket = 0;
|
||||
int result = -1;
|
||||
int r;
|
||||
int result=-1;
|
||||
|
||||
_bufferevent_incref_and_lock(bev);
|
||||
|
||||
if (!bufev_p)
|
||||
goto done;
|
||||
|
||||
fd = event_get_fd(&bev->ev_read);
|
||||
if (fd < 0) {
|
||||
made_socket = 1;
|
||||
if ((fd = socket(family, SOCK_STREAM, 0)) < 0)
|
||||
goto done;
|
||||
if (evutil_make_socket_nonblocking(fd) < 0) {
|
||||
EVUTIL_CLOSESOCKET(fd);
|
||||
goto done;
|
||||
}
|
||||
be_socket_setfd(bev, fd);
|
||||
}
|
||||
|
||||
if (connect(fd, sa, socklen)<0) {
|
||||
int e = evutil_socket_geterror(fd);
|
||||
if (EVUTIL_ERR_CONNECT_RETRIABLE(e)) {
|
||||
if (! be_socket_enable(bev, EV_WRITE)) {
|
||||
bufev_p->connecting = 1;
|
||||
result = 0;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
fd = bufferevent_getfd(bev);
|
||||
r = evutil_socket_connect(&fd, sa, socklen);
|
||||
if (r < 0) {
|
||||
_bufferevent_run_eventcb(bev, BEV_EVENT_ERROR);
|
||||
/* do something about the error? */
|
||||
goto done;
|
||||
}
|
||||
|
||||
bufferevent_setfd(bev, fd);
|
||||
if (r == 0) {
|
||||
if (! bufferevent_enable(bev, EV_WRITE)) {
|
||||
bufev_p->connecting = 1;
|
||||
result = 0;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
/* The connect succeeded already. How odd. */
|
||||
_bufferevent_run_eventcb(bev, BEV_EVENT_CONNECTED);
|
||||
}
|
||||
|
||||
result = 0;
|
||||
done:
|
||||
_bufferevent_decref_and_unlock(bev);
|
||||
return result;
|
||||
|
23
configure.in
23
configure.in
@ -36,6 +36,10 @@ AC_ARG_ENABLE(thread-support,
|
||||
AC_ARG_ENABLE(malloc-replacement,
|
||||
AS_HELP_STRING(--disable-malloc-replacement, disable support for replacing the memory mgt functions),
|
||||
[], [enable_malloc_replacement=yes])
|
||||
AC_ARG_ENABLE(openssl,
|
||||
AS_HELP_STRING(--disable-openssl, disable support for openssl encryption),
|
||||
[], [enable_openssl=yes])
|
||||
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
dnl Uncomment "AC_DISABLE_SHARED" to make shared librraries not get
|
||||
@ -64,6 +68,18 @@ LIBS="$save_LIBS"
|
||||
AC_SUBST(ZLIB_LIBS)
|
||||
AM_CONDITIONAL(ZLIB_REGRESS, [test "$have_zlib" = "yes"])
|
||||
|
||||
dnl See if we have openssl. This doesn't go in LIBS either.
|
||||
save_LIBS="$LIBS"
|
||||
LIBS=""
|
||||
OPENSSL_LIBS=""
|
||||
have_openssl=no
|
||||
AC_SEARCH_LIBS([SSL_new], [ssl],
|
||||
[have_openssl=yes
|
||||
OPENSSL_LIBS="$LIBS"
|
||||
AC_DEFINE(HAVE_OPENSSL, 1, [Define if the system has openssl])])
|
||||
LIBS="$save_LIBS"
|
||||
AC_SUBST(OPENSSL_LIBS)
|
||||
|
||||
dnl Checks for header files.
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in.h netinet/in6.h sys/socket.h sys/uio.h arpa/inet.h sys/eventfd.h sys/mman.h sys/sendfile.h)
|
||||
@ -414,7 +430,7 @@ AC_TRY_COMPILE([],
|
||||
[Define to appropriate substitue if compiler doesnt have __func__])))
|
||||
|
||||
|
||||
# check if we can compile with pthreads for the unittests
|
||||
# check if we can compile with pthreads
|
||||
have_pthreads=no
|
||||
ACX_PTHREAD([
|
||||
AC_DEFINE(HAVE_PTHREADS, 1,
|
||||
@ -422,7 +438,7 @@ ACX_PTHREAD([
|
||||
have_pthreads=yes])
|
||||
AM_CONDITIONAL(PTHREADS, [test "$have_pthreads" != "no" && test "$enable_thread_support" != "no"])
|
||||
|
||||
# check if we should compile locking into the library
|
||||
# check if we should compile locking into the library
|
||||
if test x$enable_thread_support = xno; then
|
||||
AC_DEFINE(DISABLE_THREAD_SUPPORT, 1,
|
||||
[Define if libevent should not be compiled with thread support])
|
||||
@ -434,6 +450,9 @@ if test x$enable_malloc_replacement = xno; then
|
||||
[Define if libevent should not allow replacing the mm functions])
|
||||
fi
|
||||
|
||||
# check if we have and should use openssl
|
||||
AM_CONDITIONAL(OPENSSL, [test "$enable_openssl" != "no" && test "$have_openssl" = "yes"])
|
||||
|
||||
# Add some more warnings which we use in development but not in the
|
||||
# released versions. (Some relevant gcc versions can't handle these.)
|
||||
if test x$enable_gcc_warnings = xyes; then
|
||||
|
32
evutil.c
32
evutil.c
@ -265,6 +265,38 @@ evutil_socket_geterror(evutil_socket_t sock)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 1 for connected, 0 for not yet, -1 for error. */
|
||||
int
|
||||
evutil_socket_connect(evutil_socket_t *fd_ptr, struct sockaddr *sa, int socklen)
|
||||
{
|
||||
int made_fd = 0;
|
||||
|
||||
if (*fd_ptr < 0) {
|
||||
made_fd = 1;
|
||||
if ((*fd_ptr = socket(sa->sa_family, SOCK_STREAM, 0)) < 0)
|
||||
goto err;
|
||||
if (evutil_make_socket_nonblocking(*fd_ptr) < 0) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (connect(*fd_ptr, sa, socklen) < 0) {
|
||||
int e = evutil_socket_geterror(*fd_ptr);
|
||||
if (EVUTIL_ERR_CONNECT_RETRIABLE(e))
|
||||
return 0;
|
||||
goto err;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
err:
|
||||
if (made_fd) {
|
||||
EVUTIL_CLOSESOCKET(*fd_ptr);
|
||||
*fd_ptr = -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
#define E(code, s) { code, (s " [" #code " ]") }
|
||||
static struct { int code; const char *msg; } windows_socket_errors[] = {
|
||||
|
@ -1,21 +1,31 @@
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
# XXXX Don't do all this twice.
|
||||
|
||||
EXTRA_SRC = event2/buffer.h event2/buffer_compat.h \
|
||||
event2/thread.h event2/bufferevent.h event2/bufferevent_compat.h \
|
||||
event2/bufferevent_struct.h event2/event.h event2/event_compat.h \
|
||||
event2/event_struct.h event2/tag.h event2/tag_compat.h event2/util.h \
|
||||
event2/http.h event2/http_struct.h event2/http_compat.h \
|
||||
event2/listener.h
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
event2/buffer.h event2/buffer_compat.h \
|
||||
event2/thread.h event2/bufferevent.h \
|
||||
EVENT2_EXPORT = \
|
||||
event2/buffer.h \
|
||||
event2/buffer_compat.h \
|
||||
event2/bufferevent.h \
|
||||
event2/bufferevent_compat.h \
|
||||
event2/bufferevent_struct.h event2/event.h event2/event_compat.h \
|
||||
event2/event_struct.h event2/tag.h event2/tag_compat.h event2/util.h \
|
||||
event2/http.h event2/http_struct.h event2/http_compat.h \
|
||||
event2/rpc.h event2/rpc_struct.h event2/rpc_compat.h \
|
||||
event2/dns.h event2/dns_struct.h event2/dns_compat.h \
|
||||
event2/listener.h
|
||||
event2/bufferevent_ssl.h \
|
||||
event2/bufferevent_struct.h \
|
||||
event2/dns.h \
|
||||
event2/dns_compat.h \
|
||||
event2/dns_struct.h \
|
||||
event2/event.h \
|
||||
event2/event_compat.h \
|
||||
event2/event_struct.h \
|
||||
event2/http.h \
|
||||
event2/http_compat.h \
|
||||
event2/http_struct.h \
|
||||
event2/listener.h \
|
||||
event2/rpc.h \
|
||||
event2/rpc_compat.h \
|
||||
event2/rpc_struct.h \
|
||||
event2/tag.h \
|
||||
event2/tag_compat.h \
|
||||
event2/thread.h \
|
||||
event2/util.h
|
||||
|
||||
EXTRA_SRC = $(EVENT2_EXPORT)
|
||||
|
||||
nobase_include_HEADERS = $(EVENT2_EXPORT)
|
||||
|
||||
|
70
include/event2/bufferevent_ssl.h
Normal file
70
include/event2/bufferevent_ssl.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Niels Provos and 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.
|
||||
*/
|
||||
#ifndef _EVENT2_BUFFEREVENT_SSL_H_
|
||||
#define _EVENT2_BUFFEREVENT_SSL_H_
|
||||
|
||||
/** @file bufferevent_ssl.h
|
||||
|
||||
OpenSSL support for bufferevents.
|
||||
*/
|
||||
|
||||
#include <event-config.h>
|
||||
#include <event2/bufferevent.h>
|
||||
#include <event2/util.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ssl_st;
|
||||
|
||||
enum bufferevent_ssl_state {
|
||||
BUFFEREVENT_SSL_OPEN = 0,
|
||||
BUFFEREVENT_SSL_CONNECTING = 1,
|
||||
BUFFEREVENT_SSL_ACCEPTING = 2
|
||||
};
|
||||
|
||||
#ifdef _EVENT_HAVE_OPENSSL
|
||||
struct bufferevent *
|
||||
bufferevent_openssl_filter_new(struct event_base *base,
|
||||
struct bufferevent *underlying,
|
||||
struct ssl_st *ssl,
|
||||
enum bufferevent_ssl_state state,
|
||||
enum bufferevent_options options);
|
||||
|
||||
struct bufferevent *
|
||||
bufferevent_openssl_socket_new(struct event_base *base,
|
||||
evutil_socket_t fd,
|
||||
struct ssl_st *ssl,
|
||||
enum bufferevent_ssl_state state,
|
||||
enum bufferevent_options options);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _EVENT2_BUFFEREVENT_SSL_H_ */
|
@ -9,6 +9,12 @@ event_test_sources = event-test.c
|
||||
time_test_sources = time-test.c
|
||||
signal_test_sources = signal-test.c
|
||||
|
||||
if OPENSSL
|
||||
noinst_PROGRAMS += le-proxy
|
||||
le_proxy_sources = le-proxy.c
|
||||
le_proxy_LDADD = $(LDADD) ../libevent_openssl.la -lcrypto -lssl
|
||||
endif
|
||||
|
||||
verify:
|
||||
|
||||
DISTCLEANFILES = *~
|
||||
|
210
sample/le-proxy.c
Normal file
210
sample/le-proxy.c
Normal file
@ -0,0 +1,210 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <event2/bufferevent_ssl.h>
|
||||
#include <event2/bufferevent.h>
|
||||
#include <event2/buffer.h>
|
||||
#include <event2/listener.h>
|
||||
#include <event2/util.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
static struct event_base *base;
|
||||
static struct sockaddr_storage listen_on_addr;
|
||||
static struct sockaddr_storage connect_to_addr;
|
||||
static int connect_to_addrlen;
|
||||
static int use_wrapper = 1;
|
||||
|
||||
static SSL_CTX *ssl_ctx = NULL;
|
||||
|
||||
static void
|
||||
readcb(struct bufferevent *bev, void *ctx)
|
||||
{
|
||||
struct bufferevent *partner = ctx;
|
||||
struct evbuffer *src, *dst;
|
||||
src = bufferevent_get_input(bev);
|
||||
if (!partner) {
|
||||
evbuffer_drain(src, evbuffer_get_length(src));
|
||||
return;
|
||||
}
|
||||
dst = bufferevent_get_output(partner);
|
||||
|
||||
evbuffer_add_buffer(dst, src);
|
||||
}
|
||||
|
||||
static void
|
||||
close_on_finished_writecb(struct bufferevent *bev, void *ctx)
|
||||
{
|
||||
struct evbuffer *b = bufferevent_get_output(bev);
|
||||
if (evbuffer_get_length(b) == 0) {
|
||||
bufferevent_free(bev);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
eventcb(struct bufferevent *bev, short what, void *ctx)
|
||||
{
|
||||
struct bufferevent *partner = ctx;
|
||||
|
||||
if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
|
||||
if (what & BEV_EVENT_ERROR)
|
||||
perror("maybe an error");
|
||||
|
||||
if (partner) {
|
||||
/* Flush all pending data */
|
||||
readcb(bev, ctx);
|
||||
|
||||
/* Disconnect this one from the partner. */
|
||||
bufferevent_setcb(partner,
|
||||
NULL, close_on_finished_writecb,
|
||||
eventcb, NULL);
|
||||
}
|
||||
bufferevent_free(bev);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
syntax(void)
|
||||
{
|
||||
fputs("Syntax:\n", stderr);
|
||||
fputs(" le-proxy [-s] [-W] <listen-on-addr> <connect-to-addr>\n", stderr);
|
||||
fputs("Example:\n", stderr);
|
||||
fputs(" le-proxy 127.0.0.1:8888 1.2.3.4:80\n", stderr);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
accept_cb(struct evconnlistener *listener, evutil_socket_t fd,
|
||||
struct sockaddr *a, int slen, void *p)
|
||||
{
|
||||
struct bufferevent *b_out, *b_in;
|
||||
/* Create two linked bufferevent objects: one to connect, one for the
|
||||
* new connection */
|
||||
b_in = bufferevent_socket_new(base, fd,
|
||||
BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
|
||||
|
||||
if (!ssl_ctx || use_wrapper)
|
||||
b_out = bufferevent_socket_new(base, -1,
|
||||
BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
|
||||
else {
|
||||
SSL *ssl = SSL_new(ssl_ctx);
|
||||
b_out = bufferevent_openssl_socket_new(base, -1, ssl,
|
||||
BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
|
||||
}
|
||||
|
||||
assert(b_in && b_out);
|
||||
|
||||
if (bufferevent_socket_connect(b_out,
|
||||
(struct sockaddr*)&connect_to_addr, connect_to_addrlen)<0) {
|
||||
perror("bufferevent_socket_connect");
|
||||
bufferevent_free(b_out);
|
||||
bufferevent_free(b_in);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ssl_ctx && use_wrapper) {
|
||||
struct bufferevent *b_ssl;
|
||||
SSL *ssl = SSL_new(ssl_ctx);
|
||||
b_ssl = bufferevent_openssl_filter_new(base,
|
||||
b_out, ssl, BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
|
||||
if (!b_ssl) {
|
||||
perror("Bufferevent_openssl_new");
|
||||
bufferevent_free(b_out);
|
||||
bufferevent_free(b_in);
|
||||
}
|
||||
b_out = b_ssl;
|
||||
}
|
||||
|
||||
bufferevent_setcb(b_in, readcb, NULL, eventcb, b_out);
|
||||
bufferevent_setcb(b_out, readcb, NULL, eventcb, b_in);
|
||||
|
||||
bufferevent_enable(b_in, EV_READ|EV_WRITE);
|
||||
bufferevent_enable(b_out, EV_READ|EV_WRITE);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
int socklen;
|
||||
|
||||
int use_ssl = 0;
|
||||
struct evconnlistener *listener;
|
||||
|
||||
if (argc < 3)
|
||||
syntax();
|
||||
|
||||
for (i=1; i < argc; ++i) {
|
||||
if (!strcmp(argv[i], "-s")) {
|
||||
use_ssl = 1;
|
||||
} else if (!strcmp(argv[i], "-W")) {
|
||||
use_wrapper = 0;
|
||||
} else if (argv[i][0] == '-') {
|
||||
syntax();
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (i+2 != argc)
|
||||
syntax();
|
||||
|
||||
memset(&listen_on_addr, 0, sizeof(listen_on_addr));
|
||||
socklen = sizeof(listen_on_addr);
|
||||
if (evutil_parse_sockaddr_port(argv[i],
|
||||
(struct sockaddr*)&listen_on_addr, &socklen)<0) {
|
||||
int p = atoi(argv[i]);
|
||||
struct sockaddr_in *sin = (struct sockaddr_in*)&listen_on_addr;
|
||||
if (p < 1 || p > 65535)
|
||||
syntax();
|
||||
sin->sin_port = htons(p);
|
||||
sin->sin_addr.s_addr = htonl(0x7f000001);
|
||||
sin->sin_family = AF_INET;
|
||||
socklen = sizeof(struct sockaddr_in);
|
||||
}
|
||||
|
||||
memset(&connect_to_addr, 0, sizeof(connect_to_addr));
|
||||
connect_to_addrlen = sizeof(connect_to_addr);
|
||||
if (evutil_parse_sockaddr_port(argv[i+1],
|
||||
(struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0)
|
||||
syntax();
|
||||
|
||||
base = event_base_new();
|
||||
if (!base) {
|
||||
perror("event_base_new()");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (use_ssl) {
|
||||
int r;
|
||||
SSL_library_init();
|
||||
ERR_load_crypto_strings();
|
||||
SSL_load_error_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
r = RAND_poll();
|
||||
if (r == 0) {
|
||||
fprintf(stderr, "RAND_poll() failed.\n");
|
||||
return 1;
|
||||
}
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_method());
|
||||
}
|
||||
|
||||
listener = evconnlistener_new_bind(base, accept_cb, NULL,
|
||||
LEV_OPT_CLOSE_ON_FREE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_REUSEABLE,
|
||||
-1, (struct sockaddr*)&listen_on_addr, socklen);
|
||||
|
||||
event_base_dispatch(base);
|
||||
|
||||
return 0;
|
||||
}
|
@ -33,11 +33,17 @@ endif
|
||||
if BUILD_WIN32
|
||||
regress_SOURCES += regress_iocp.c
|
||||
endif
|
||||
|
||||
regress_LDADD = ../libevent.la $(PTHREAD_LIBS) $(ZLIB_LIBS)
|
||||
regress_CFLAGS = -I$(top_srcdir) -I$(top_srcdir)/compat \
|
||||
-I$(top_srcdir)/include $(PTHREAD_CFLAGS) $(ZLIB_CFLAGS)
|
||||
regress_LDFLAGS = $(PTHREAD_CFLAGS)
|
||||
|
||||
if OPENSSL
|
||||
regress_SOURCES += regress_ssl.c
|
||||
regress_LDADD += ../libevent_openssl.la -lcrypto -lssl
|
||||
endif
|
||||
|
||||
bench_SOURCES = bench.c
|
||||
bench_LDADD = ../libevent.la
|
||||
bench_cascade_SOURCES = bench_cascade.c
|
||||
|
@ -46,6 +46,7 @@ extern struct testcase_t rpc_testcases[];
|
||||
extern struct testcase_t edgetriggered_testcases[];
|
||||
extern struct testcase_t minheap_testcases[];
|
||||
extern struct testcase_t iocp_testcases[];
|
||||
extern struct testcase_t ssl_testcases[];
|
||||
|
||||
void regress_threads(void *);
|
||||
void test_bufferevent_zlib(void *);
|
||||
|
@ -288,6 +288,9 @@ struct testgroup_t testgroups[] = {
|
||||
{ "thread/", thread_testcases },
|
||||
#ifdef WIN32
|
||||
{ "iocp/", iocp_testcases },
|
||||
#endif
|
||||
#ifdef _EVENT_HAVE_OPENSSL
|
||||
{ "ssl/", ssl_testcases },
|
||||
#endif
|
||||
END_OF_GROUPS
|
||||
};
|
||||
|
278
test/regress_ssl.c
Normal file
278
test/regress_ssl.c
Normal file
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Niels Provos and 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.
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <event2/util.h>
|
||||
#include <event2/event.h>
|
||||
#include <event2/bufferevent_ssl.h>
|
||||
#include <event2/buffer.h>
|
||||
|
||||
#include "regress.h"
|
||||
#include "tinytest.h"
|
||||
#include "tinytest_macros.h"
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* A short pre-generated key, to save the cost of doing an RSA key generation
|
||||
* step during the unit tests. It's only 512 bits long, and it is published
|
||||
* in this file, so you would have to be very foolish to consider using it in
|
||||
* your own code. */
|
||||
static const char KEY[] =
|
||||
"-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
"MIIBOgIBAAJBAKibTEzXjj+sqpipePX1lEk5BNFuL/dDBbw8QCXgaJWikOiKHeJq\n"
|
||||
"3FQ0OmCnmpkdsPFE4x3ojYmmdgE2i0dJwq0CAwEAAQJAZ08gpUS+qE1IClps/2gG\n"
|
||||
"AAer6Bc31K2AaiIQvCSQcH440cp062QtWMC3V5sEoWmdLsbAHFH26/9ZHn5zAflp\n"
|
||||
"gQIhANWOx/UYeR8HD0WREU5kcuSzgzNLwUErHLzxP7U6aojpAiEAyh2H35CjN/P7\n"
|
||||
"NhcZ4QYw3PeUWpqgJnaE/4i80BSYkSUCIQDLHFhLYLJZ80HwHTADif/ISn9/Ow6b\n"
|
||||
"p6BWh3DbMar/eQIgBPS6azH5vpp983KXkNv9AL4VZi9ac/b+BeINdzC6GP0CIDmB\n"
|
||||
"U6GFEQTZ3IfuiVabG5pummdC4DNbcdI+WKrSFNmQ\n"
|
||||
"-----END RSA PRIVATE KEY-----\n";
|
||||
|
||||
static EVP_PKEY *
|
||||
getkey(void)
|
||||
{
|
||||
EVP_PKEY *key;
|
||||
BIO *bio;
|
||||
|
||||
/* new read-only BIO backed by KEY. */
|
||||
bio = BIO_new_mem_buf((char*)KEY, -1);
|
||||
tt_assert(bio);
|
||||
|
||||
key = PEM_read_bio_PrivateKey(bio,NULL,NULL,NULL);
|
||||
BIO_free(bio);
|
||||
tt_assert(key);
|
||||
|
||||
return key;
|
||||
end:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static X509 *
|
||||
getcert(void)
|
||||
{
|
||||
/* Dummy code to make a quick-and-dirty valid certificate with
|
||||
OpenSSL. Don't copy this code into your own program! It does a
|
||||
number of things in a stupid and insecure way. */
|
||||
X509 *x509 = NULL;
|
||||
X509_NAME *name = NULL;
|
||||
EVP_PKEY *key = getkey();
|
||||
int nid;
|
||||
time_t now = time(NULL);
|
||||
|
||||
tt_assert(key);
|
||||
|
||||
x509 = X509_new();
|
||||
tt_assert(x509);
|
||||
tt_assert(0 != X509_set_version(x509, 2));
|
||||
tt_assert(0 != ASN1_INTEGER_set(X509_get_serialNumber(x509),
|
||||
(long)now));
|
||||
|
||||
name = X509_NAME_new();
|
||||
tt_assert(name);
|
||||
tt_assert(NID_undef != (nid = OBJ_txt2nid("commonName")));
|
||||
tt_assert(0 != X509_NAME_add_entry_by_NID(
|
||||
name, nid, MBSTRING_ASC, (unsigned char*)"example.com",
|
||||
-1, -1, 0));
|
||||
|
||||
X509_set_subject_name(x509, name);
|
||||
X509_set_issuer_name(x509, name);
|
||||
|
||||
X509_time_adj(X509_get_notBefore(x509), 0, &now);
|
||||
now += 3600;
|
||||
X509_time_adj(X509_get_notAfter(x509), 0, &now);
|
||||
X509_set_pubkey(x509, key);
|
||||
tt_assert(0 != X509_sign(x509, key, EVP_sha1()));
|
||||
|
||||
return x509;
|
||||
end:
|
||||
X509_free(x509);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static SSL_CTX *
|
||||
get_ssl_ctx(void)
|
||||
{
|
||||
static SSL_CTX *the_ssl_ctx = NULL;
|
||||
if (the_ssl_ctx)
|
||||
return the_ssl_ctx;
|
||||
return (the_ssl_ctx = SSL_CTX_new(SSLv23_method()));
|
||||
}
|
||||
|
||||
static void
|
||||
init_ssl(void)
|
||||
{
|
||||
SSL_library_init();
|
||||
ERR_load_crypto_strings();
|
||||
SSL_load_error_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
}
|
||||
|
||||
/* ====================
|
||||
Here's a simple test: we read a number from the input, increment it, and
|
||||
reply, until we get to 1001.
|
||||
*/
|
||||
|
||||
static int test_is_done = 0;
|
||||
static int n_connected = 0;
|
||||
static int got_close = 0;
|
||||
static int got_error = 0;
|
||||
|
||||
static void
|
||||
respond_to_number(struct bufferevent *bev, void *ctx)
|
||||
{
|
||||
struct evbuffer *b = bufferevent_get_input(bev);
|
||||
char *line;
|
||||
int n;
|
||||
line = evbuffer_readln(b, NULL, EVBUFFER_EOL_LF);
|
||||
if (! line)
|
||||
return;
|
||||
n = atoi(line);
|
||||
if (n <= 0)
|
||||
TT_FAIL(("Bad number: %s", line));
|
||||
TT_BLATHER(("The number was %d", n));
|
||||
if (n == 1001) {
|
||||
++test_is_done;
|
||||
bufferevent_free(bev); /* Should trigger close on other side. */
|
||||
return;
|
||||
}
|
||||
++n;
|
||||
evbuffer_add_printf(bufferevent_get_output(bev),
|
||||
"%d\n", n);
|
||||
}
|
||||
|
||||
static void
|
||||
eventcb(struct bufferevent *bev, short what, void *ctx)
|
||||
{
|
||||
TT_BLATHER(("Got event %d", (int)what));
|
||||
if (what & BEV_EVENT_CONNECTED)
|
||||
++n_connected;
|
||||
else if (what & BEV_EVENT_EOF) {
|
||||
TT_BLATHER(("Got a good EOF"));
|
||||
++got_close;
|
||||
bufferevent_free(bev);
|
||||
} else if (what & BEV_EVENT_ERROR) {
|
||||
TT_BLATHER(("Got an error."));
|
||||
++got_error;
|
||||
bufferevent_free(bev);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
regress_bufferevent_openssl(void *arg)
|
||||
{
|
||||
struct basic_test_data *data = arg;
|
||||
|
||||
struct bufferevent *bev1, *bev2;
|
||||
SSL *ssl1, *ssl2;
|
||||
X509 *cert = getcert();
|
||||
EVP_PKEY *key = getkey();
|
||||
tt_assert(cert);
|
||||
tt_assert(key);
|
||||
|
||||
init_ssl();
|
||||
|
||||
ssl1 = SSL_new(get_ssl_ctx());
|
||||
ssl2 = SSL_new(get_ssl_ctx());
|
||||
|
||||
SSL_use_certificate(ssl2, cert);
|
||||
SSL_use_PrivateKey(ssl2, key);
|
||||
|
||||
if (strstr((char*)data->setup_data, "socketpair")) {
|
||||
bev1 = bufferevent_openssl_socket_new(
|
||||
data->base,
|
||||
data->pair[0],
|
||||
ssl1,
|
||||
BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
|
||||
bev2 = bufferevent_openssl_socket_new(
|
||||
data->base,
|
||||
data->pair[1],
|
||||
ssl2,
|
||||
BUFFEREVENT_SSL_ACCEPTING,
|
||||
BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
|
||||
} else if (strstr((char*)data->setup_data, "filter")) {
|
||||
struct bufferevent *bev_ll1, *bev_ll2;
|
||||
bev_ll1 = bufferevent_socket_new(data->base, data->pair[0],
|
||||
BEV_OPT_CLOSE_ON_FREE);
|
||||
bev_ll2 = bufferevent_socket_new(data->base, data->pair[1],
|
||||
BEV_OPT_CLOSE_ON_FREE);
|
||||
tt_assert(bev_ll1);
|
||||
tt_assert(bev_ll2);
|
||||
bev1 = bufferevent_openssl_filter_new(
|
||||
data->base,
|
||||
bev_ll1,
|
||||
ssl1,
|
||||
BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
|
||||
bev2 = bufferevent_openssl_filter_new(
|
||||
data->base,
|
||||
bev_ll2,
|
||||
ssl2,
|
||||
BUFFEREVENT_SSL_ACCEPTING,
|
||||
BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
|
||||
} else {
|
||||
TT_DIE(("Bad setup data %s", (char*)data->setup_data));
|
||||
}
|
||||
|
||||
bufferevent_enable(bev1, EV_READ|EV_WRITE);
|
||||
bufferevent_enable(bev2, EV_READ|EV_WRITE);
|
||||
|
||||
bufferevent_setcb(bev1, respond_to_number, NULL, eventcb, NULL);
|
||||
bufferevent_setcb(bev2, respond_to_number, NULL, eventcb, NULL);
|
||||
|
||||
evbuffer_add_printf(bufferevent_get_output(bev1), "1\n");
|
||||
|
||||
event_base_dispatch(data->base);
|
||||
|
||||
tt_assert(test_is_done == 1);
|
||||
tt_assert(n_connected == 2);
|
||||
/* We don't handle shutdown properly yet.
|
||||
tt_int_op(got_close, ==, 1);
|
||||
tt_int_op(got_error, ==, 0);
|
||||
*/
|
||||
end:
|
||||
return;
|
||||
}
|
||||
|
||||
struct testcase_t ssl_testcases[] = {
|
||||
|
||||
{ "bufferevent_socketpair", regress_bufferevent_openssl, TT_ISOLATED,
|
||||
&basic_setup, (void*)"socketpair" },
|
||||
{ "bufferevent_filter", regress_bufferevent_openssl,
|
||||
TT_ISOLATED,
|
||||
&basic_setup, (void*)"filter" },
|
||||
|
||||
END_OF_TESTCASES,
|
||||
};
|
@ -129,6 +129,9 @@ int evutil_strncasecmp(const char *, const char *, size_t);
|
||||
#define EVUTIL_UPCAST(ptr, type, field) \
|
||||
((type *)((char*)ptr) - evutil_offsetof(type, field))
|
||||
|
||||
|
||||
int evutil_socket_connect(evutil_socket_t *fd_ptr, struct sockaddr *sa, int socklen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user