mirror of
https://github.com/libevent/libevent.git
synced 2025-01-31 09:12:55 +08:00
709c21c48c
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
211 lines
4.8 KiB
C
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;
|
|
}
|