Hash :
709c21c4
Author :
Date :
2009-07-28T04:03:57
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 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;
}