michael@0: /* michael@0: This example code shows how to write an (optionally encrypting) SSL proxy michael@0: with Libevent's bufferevent layer. michael@0: michael@0: XXX It's a little ugly and should probably be cleaned up. michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #ifdef WIN32 michael@0: #include michael@0: #include michael@0: #else michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: static struct event_base *base; michael@0: static struct sockaddr_storage listen_on_addr; michael@0: static struct sockaddr_storage connect_to_addr; michael@0: static int connect_to_addrlen; michael@0: static int use_wrapper = 1; michael@0: michael@0: static SSL_CTX *ssl_ctx = NULL; michael@0: michael@0: #define MAX_OUTPUT (512*1024) michael@0: michael@0: static void drained_writecb(struct bufferevent *bev, void *ctx); michael@0: static void eventcb(struct bufferevent *bev, short what, void *ctx); michael@0: michael@0: static void michael@0: readcb(struct bufferevent *bev, void *ctx) michael@0: { michael@0: struct bufferevent *partner = ctx; michael@0: struct evbuffer *src, *dst; michael@0: size_t len; michael@0: src = bufferevent_get_input(bev); michael@0: len = evbuffer_get_length(src); michael@0: if (!partner) { michael@0: evbuffer_drain(src, len); michael@0: return; michael@0: } michael@0: dst = bufferevent_get_output(partner); michael@0: evbuffer_add_buffer(dst, src); michael@0: michael@0: if (evbuffer_get_length(dst) >= MAX_OUTPUT) { michael@0: /* We're giving the other side data faster than it can michael@0: * pass it on. Stop reading here until we have drained the michael@0: * other side to MAX_OUTPUT/2 bytes. */ michael@0: bufferevent_setcb(partner, readcb, drained_writecb, michael@0: eventcb, bev); michael@0: bufferevent_setwatermark(partner, EV_WRITE, MAX_OUTPUT/2, michael@0: MAX_OUTPUT); michael@0: bufferevent_disable(bev, EV_READ); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: drained_writecb(struct bufferevent *bev, void *ctx) michael@0: { michael@0: struct bufferevent *partner = ctx; michael@0: michael@0: /* We were choking the other side until we drained our outbuf a bit. michael@0: * Now it seems drained. */ michael@0: bufferevent_setcb(bev, readcb, NULL, eventcb, partner); michael@0: bufferevent_setwatermark(bev, EV_WRITE, 0, 0); michael@0: if (partner) michael@0: bufferevent_enable(partner, EV_READ); michael@0: } michael@0: michael@0: static void michael@0: close_on_finished_writecb(struct bufferevent *bev, void *ctx) michael@0: { michael@0: struct evbuffer *b = bufferevent_get_output(bev); michael@0: michael@0: if (evbuffer_get_length(b) == 0) { michael@0: bufferevent_free(bev); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: eventcb(struct bufferevent *bev, short what, void *ctx) michael@0: { michael@0: struct bufferevent *partner = ctx; michael@0: michael@0: if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { michael@0: if (what & BEV_EVENT_ERROR) { michael@0: unsigned long err; michael@0: while ((err = (bufferevent_get_openssl_error(bev)))) { michael@0: const char *msg = (const char*) michael@0: ERR_reason_error_string(err); michael@0: const char *lib = (const char*) michael@0: ERR_lib_error_string(err); michael@0: const char *func = (const char*) michael@0: ERR_func_error_string(err); michael@0: fprintf(stderr, michael@0: "%s in %s %s\n", msg, lib, func); michael@0: } michael@0: if (errno) michael@0: perror("connection error"); michael@0: } michael@0: michael@0: if (partner) { michael@0: /* Flush all pending data */ michael@0: readcb(bev, ctx); michael@0: michael@0: if (evbuffer_get_length( michael@0: bufferevent_get_output(partner))) { michael@0: /* We still have to flush data from the other michael@0: * side, but when that's done, close the other michael@0: * side. */ michael@0: bufferevent_setcb(partner, michael@0: NULL, close_on_finished_writecb, michael@0: eventcb, NULL); michael@0: bufferevent_disable(partner, EV_READ); michael@0: } else { michael@0: /* We have nothing left to say to the other michael@0: * side; close it. */ michael@0: bufferevent_free(partner); michael@0: } michael@0: } michael@0: bufferevent_free(bev); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: syntax(void) michael@0: { michael@0: fputs("Syntax:\n", stderr); michael@0: fputs(" le-proxy [-s] [-W] \n", stderr); michael@0: fputs("Example:\n", stderr); michael@0: fputs(" le-proxy 127.0.0.1:8888 1.2.3.4:80\n", stderr); michael@0: michael@0: exit(1); michael@0: } michael@0: michael@0: static void michael@0: accept_cb(struct evconnlistener *listener, evutil_socket_t fd, michael@0: struct sockaddr *a, int slen, void *p) michael@0: { michael@0: struct bufferevent *b_out, *b_in; michael@0: /* Create two linked bufferevent objects: one to connect, one for the michael@0: * new connection */ michael@0: b_in = bufferevent_socket_new(base, fd, michael@0: BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); michael@0: michael@0: if (!ssl_ctx || use_wrapper) michael@0: b_out = bufferevent_socket_new(base, -1, michael@0: BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); michael@0: else { michael@0: SSL *ssl = SSL_new(ssl_ctx); michael@0: b_out = bufferevent_openssl_socket_new(base, -1, ssl, michael@0: BUFFEREVENT_SSL_CONNECTING, michael@0: BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); michael@0: } michael@0: michael@0: assert(b_in && b_out); michael@0: michael@0: if (bufferevent_socket_connect(b_out, michael@0: (struct sockaddr*)&connect_to_addr, connect_to_addrlen)<0) { michael@0: perror("bufferevent_socket_connect"); michael@0: bufferevent_free(b_out); michael@0: bufferevent_free(b_in); michael@0: return; michael@0: } michael@0: michael@0: if (ssl_ctx && use_wrapper) { michael@0: struct bufferevent *b_ssl; michael@0: SSL *ssl = SSL_new(ssl_ctx); michael@0: b_ssl = bufferevent_openssl_filter_new(base, michael@0: b_out, ssl, BUFFEREVENT_SSL_CONNECTING, michael@0: BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); michael@0: if (!b_ssl) { michael@0: perror("Bufferevent_openssl_new"); michael@0: bufferevent_free(b_out); michael@0: bufferevent_free(b_in); michael@0: } michael@0: b_out = b_ssl; michael@0: } michael@0: michael@0: bufferevent_setcb(b_in, readcb, NULL, eventcb, b_out); michael@0: bufferevent_setcb(b_out, readcb, NULL, eventcb, b_in); michael@0: michael@0: bufferevent_enable(b_in, EV_READ|EV_WRITE); michael@0: bufferevent_enable(b_out, EV_READ|EV_WRITE); michael@0: } michael@0: michael@0: int michael@0: main(int argc, char **argv) michael@0: { michael@0: int i; michael@0: int socklen; michael@0: michael@0: int use_ssl = 0; michael@0: struct evconnlistener *listener; michael@0: michael@0: if (argc < 3) michael@0: syntax(); michael@0: michael@0: for (i=1; i < argc; ++i) { michael@0: if (!strcmp(argv[i], "-s")) { michael@0: use_ssl = 1; michael@0: } else if (!strcmp(argv[i], "-W")) { michael@0: use_wrapper = 0; michael@0: } else if (argv[i][0] == '-') { michael@0: syntax(); michael@0: } else michael@0: break; michael@0: } michael@0: michael@0: if (i+2 != argc) michael@0: syntax(); michael@0: michael@0: memset(&listen_on_addr, 0, sizeof(listen_on_addr)); michael@0: socklen = sizeof(listen_on_addr); michael@0: if (evutil_parse_sockaddr_port(argv[i], michael@0: (struct sockaddr*)&listen_on_addr, &socklen)<0) { michael@0: int p = atoi(argv[i]); michael@0: struct sockaddr_in *sin = (struct sockaddr_in*)&listen_on_addr; michael@0: if (p < 1 || p > 65535) michael@0: syntax(); michael@0: sin->sin_port = htons(p); michael@0: sin->sin_addr.s_addr = htonl(0x7f000001); michael@0: sin->sin_family = AF_INET; michael@0: socklen = sizeof(struct sockaddr_in); michael@0: } michael@0: michael@0: memset(&connect_to_addr, 0, sizeof(connect_to_addr)); michael@0: connect_to_addrlen = sizeof(connect_to_addr); michael@0: if (evutil_parse_sockaddr_port(argv[i+1], michael@0: (struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) michael@0: syntax(); michael@0: michael@0: base = event_base_new(); michael@0: if (!base) { michael@0: perror("event_base_new()"); michael@0: return 1; michael@0: } michael@0: michael@0: if (use_ssl) { michael@0: int r; michael@0: SSL_library_init(); michael@0: ERR_load_crypto_strings(); michael@0: SSL_load_error_strings(); michael@0: OpenSSL_add_all_algorithms(); michael@0: r = RAND_poll(); michael@0: if (r == 0) { michael@0: fprintf(stderr, "RAND_poll() failed.\n"); michael@0: return 1; michael@0: } michael@0: ssl_ctx = SSL_CTX_new(SSLv23_method()); michael@0: } michael@0: michael@0: listener = evconnlistener_new_bind(base, accept_cb, NULL, michael@0: LEV_OPT_CLOSE_ON_FREE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_REUSEABLE, michael@0: -1, (struct sockaddr*)&listen_on_addr, socklen); michael@0: michael@0: event_base_dispatch(base); michael@0: michael@0: evconnlistener_free(listener); michael@0: event_base_free(base); michael@0: michael@0: return 0; michael@0: }