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:
Nick Mathewson 2009-07-28 04:03:57 +00:00
parent b06b2649b4
commit 709c21c48c
18 changed files with 1852 additions and 62 deletions

View File

@ -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.

View File

@ -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 \

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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

View File

@ -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[] = {

View File

@ -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)

View 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_ */

View File

@ -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
View 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;
}

View File

@ -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

View File

@ -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 *);

View File

@ -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
View 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,
};

View File

@ -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