libevent/sample/le-proxy.c
Nick Mathewson 709c21c48c 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
2009-07-28 04:03:57 +00:00

211 lines
4.8 KiB
C

#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;
}