Austin Schuh | a273376 | 2015-09-06 17:46:50 -0700 | [diff] [blame^] | 1 | /* |
| 2 | This example code shows how to write an (optionally encrypting) SSL proxy |
| 3 | with Libevent's bufferevent layer. |
| 4 | |
| 5 | XXX It's a little ugly and should probably be cleaned up. |
| 6 | */ |
| 7 | |
| 8 | #include <stdio.h> |
| 9 | #include <assert.h> |
| 10 | #include <stdlib.h> |
| 11 | #include <string.h> |
| 12 | #include <errno.h> |
| 13 | |
| 14 | #ifdef WIN32 |
| 15 | #include <winsock2.h> |
| 16 | #include <ws2tcpip.h> |
| 17 | #else |
| 18 | #include <sys/socket.h> |
| 19 | #include <netinet/in.h> |
| 20 | #endif |
| 21 | |
| 22 | #include <event2/bufferevent_ssl.h> |
| 23 | #include <event2/bufferevent.h> |
| 24 | #include <event2/buffer.h> |
| 25 | #include <event2/listener.h> |
| 26 | #include <event2/util.h> |
| 27 | |
| 28 | #include <openssl/ssl.h> |
| 29 | #include <openssl/err.h> |
| 30 | #include <openssl/rand.h> |
| 31 | |
| 32 | static struct event_base *base; |
| 33 | static struct sockaddr_storage listen_on_addr; |
| 34 | static struct sockaddr_storage connect_to_addr; |
| 35 | static int connect_to_addrlen; |
| 36 | static int use_wrapper = 1; |
| 37 | |
| 38 | static SSL_CTX *ssl_ctx = NULL; |
| 39 | |
| 40 | #define MAX_OUTPUT (512*1024) |
| 41 | |
| 42 | static void drained_writecb(struct bufferevent *bev, void *ctx); |
| 43 | static void eventcb(struct bufferevent *bev, short what, void *ctx); |
| 44 | |
| 45 | static void |
| 46 | readcb(struct bufferevent *bev, void *ctx) |
| 47 | { |
| 48 | struct bufferevent *partner = ctx; |
| 49 | struct evbuffer *src, *dst; |
| 50 | size_t len; |
| 51 | src = bufferevent_get_input(bev); |
| 52 | len = evbuffer_get_length(src); |
| 53 | if (!partner) { |
| 54 | evbuffer_drain(src, len); |
| 55 | return; |
| 56 | } |
| 57 | dst = bufferevent_get_output(partner); |
| 58 | evbuffer_add_buffer(dst, src); |
| 59 | |
| 60 | if (evbuffer_get_length(dst) >= MAX_OUTPUT) { |
| 61 | /* We're giving the other side data faster than it can |
| 62 | * pass it on. Stop reading here until we have drained the |
| 63 | * other side to MAX_OUTPUT/2 bytes. */ |
| 64 | bufferevent_setcb(partner, readcb, drained_writecb, |
| 65 | eventcb, bev); |
| 66 | bufferevent_setwatermark(partner, EV_WRITE, MAX_OUTPUT/2, |
| 67 | MAX_OUTPUT); |
| 68 | bufferevent_disable(bev, EV_READ); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | static void |
| 73 | drained_writecb(struct bufferevent *bev, void *ctx) |
| 74 | { |
| 75 | struct bufferevent *partner = ctx; |
| 76 | |
| 77 | /* We were choking the other side until we drained our outbuf a bit. |
| 78 | * Now it seems drained. */ |
| 79 | bufferevent_setcb(bev, readcb, NULL, eventcb, partner); |
| 80 | bufferevent_setwatermark(bev, EV_WRITE, 0, 0); |
| 81 | if (partner) |
| 82 | bufferevent_enable(partner, EV_READ); |
| 83 | } |
| 84 | |
| 85 | static void |
| 86 | close_on_finished_writecb(struct bufferevent *bev, void *ctx) |
| 87 | { |
| 88 | struct evbuffer *b = bufferevent_get_output(bev); |
| 89 | |
| 90 | if (evbuffer_get_length(b) == 0) { |
| 91 | bufferevent_free(bev); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | static void |
| 96 | eventcb(struct bufferevent *bev, short what, void *ctx) |
| 97 | { |
| 98 | struct bufferevent *partner = ctx; |
| 99 | |
| 100 | if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { |
| 101 | if (what & BEV_EVENT_ERROR) { |
| 102 | unsigned long err; |
| 103 | while ((err = (bufferevent_get_openssl_error(bev)))) { |
| 104 | const char *msg = (const char*) |
| 105 | ERR_reason_error_string(err); |
| 106 | const char *lib = (const char*) |
| 107 | ERR_lib_error_string(err); |
| 108 | const char *func = (const char*) |
| 109 | ERR_func_error_string(err); |
| 110 | fprintf(stderr, |
| 111 | "%s in %s %s\n", msg, lib, func); |
| 112 | } |
| 113 | if (errno) |
| 114 | perror("connection error"); |
| 115 | } |
| 116 | |
| 117 | if (partner) { |
| 118 | /* Flush all pending data */ |
| 119 | readcb(bev, ctx); |
| 120 | |
| 121 | if (evbuffer_get_length( |
| 122 | bufferevent_get_output(partner))) { |
| 123 | /* We still have to flush data from the other |
| 124 | * side, but when that's done, close the other |
| 125 | * side. */ |
| 126 | bufferevent_setcb(partner, |
| 127 | NULL, close_on_finished_writecb, |
| 128 | eventcb, NULL); |
| 129 | bufferevent_disable(partner, EV_READ); |
| 130 | } else { |
| 131 | /* We have nothing left to say to the other |
| 132 | * side; close it. */ |
| 133 | bufferevent_free(partner); |
| 134 | } |
| 135 | } |
| 136 | bufferevent_free(bev); |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | static void |
| 141 | syntax(void) |
| 142 | { |
| 143 | fputs("Syntax:\n", stderr); |
| 144 | fputs(" le-proxy [-s] [-W] <listen-on-addr> <connect-to-addr>\n", stderr); |
| 145 | fputs("Example:\n", stderr); |
| 146 | fputs(" le-proxy 127.0.0.1:8888 1.2.3.4:80\n", stderr); |
| 147 | |
| 148 | exit(1); |
| 149 | } |
| 150 | |
| 151 | static void |
| 152 | accept_cb(struct evconnlistener *listener, evutil_socket_t fd, |
| 153 | struct sockaddr *a, int slen, void *p) |
| 154 | { |
| 155 | struct bufferevent *b_out, *b_in; |
| 156 | /* Create two linked bufferevent objects: one to connect, one for the |
| 157 | * new connection */ |
| 158 | b_in = bufferevent_socket_new(base, fd, |
| 159 | BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); |
| 160 | |
| 161 | if (!ssl_ctx || use_wrapper) |
| 162 | b_out = bufferevent_socket_new(base, -1, |
| 163 | BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); |
| 164 | else { |
| 165 | SSL *ssl = SSL_new(ssl_ctx); |
| 166 | b_out = bufferevent_openssl_socket_new(base, -1, ssl, |
| 167 | BUFFEREVENT_SSL_CONNECTING, |
| 168 | BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); |
| 169 | } |
| 170 | |
| 171 | assert(b_in && b_out); |
| 172 | |
| 173 | if (bufferevent_socket_connect(b_out, |
| 174 | (struct sockaddr*)&connect_to_addr, connect_to_addrlen)<0) { |
| 175 | perror("bufferevent_socket_connect"); |
| 176 | bufferevent_free(b_out); |
| 177 | bufferevent_free(b_in); |
| 178 | return; |
| 179 | } |
| 180 | |
| 181 | if (ssl_ctx && use_wrapper) { |
| 182 | struct bufferevent *b_ssl; |
| 183 | SSL *ssl = SSL_new(ssl_ctx); |
| 184 | b_ssl = bufferevent_openssl_filter_new(base, |
| 185 | b_out, ssl, BUFFEREVENT_SSL_CONNECTING, |
| 186 | BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); |
| 187 | if (!b_ssl) { |
| 188 | perror("Bufferevent_openssl_new"); |
| 189 | bufferevent_free(b_out); |
| 190 | bufferevent_free(b_in); |
| 191 | } |
| 192 | b_out = b_ssl; |
| 193 | } |
| 194 | |
| 195 | bufferevent_setcb(b_in, readcb, NULL, eventcb, b_out); |
| 196 | bufferevent_setcb(b_out, readcb, NULL, eventcb, b_in); |
| 197 | |
| 198 | bufferevent_enable(b_in, EV_READ|EV_WRITE); |
| 199 | bufferevent_enable(b_out, EV_READ|EV_WRITE); |
| 200 | } |
| 201 | |
| 202 | int |
| 203 | main(int argc, char **argv) |
| 204 | { |
| 205 | int i; |
| 206 | int socklen; |
| 207 | |
| 208 | int use_ssl = 0; |
| 209 | struct evconnlistener *listener; |
| 210 | |
| 211 | if (argc < 3) |
| 212 | syntax(); |
| 213 | |
| 214 | for (i=1; i < argc; ++i) { |
| 215 | if (!strcmp(argv[i], "-s")) { |
| 216 | use_ssl = 1; |
| 217 | } else if (!strcmp(argv[i], "-W")) { |
| 218 | use_wrapper = 0; |
| 219 | } else if (argv[i][0] == '-') { |
| 220 | syntax(); |
| 221 | } else |
| 222 | break; |
| 223 | } |
| 224 | |
| 225 | if (i+2 != argc) |
| 226 | syntax(); |
| 227 | |
| 228 | memset(&listen_on_addr, 0, sizeof(listen_on_addr)); |
| 229 | socklen = sizeof(listen_on_addr); |
| 230 | if (evutil_parse_sockaddr_port(argv[i], |
| 231 | (struct sockaddr*)&listen_on_addr, &socklen)<0) { |
| 232 | int p = atoi(argv[i]); |
| 233 | struct sockaddr_in *sin = (struct sockaddr_in*)&listen_on_addr; |
| 234 | if (p < 1 || p > 65535) |
| 235 | syntax(); |
| 236 | sin->sin_port = htons(p); |
| 237 | sin->sin_addr.s_addr = htonl(0x7f000001); |
| 238 | sin->sin_family = AF_INET; |
| 239 | socklen = sizeof(struct sockaddr_in); |
| 240 | } |
| 241 | |
| 242 | memset(&connect_to_addr, 0, sizeof(connect_to_addr)); |
| 243 | connect_to_addrlen = sizeof(connect_to_addr); |
| 244 | if (evutil_parse_sockaddr_port(argv[i+1], |
| 245 | (struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) |
| 246 | syntax(); |
| 247 | |
| 248 | base = event_base_new(); |
| 249 | if (!base) { |
| 250 | perror("event_base_new()"); |
| 251 | return 1; |
| 252 | } |
| 253 | |
| 254 | if (use_ssl) { |
| 255 | int r; |
| 256 | SSL_library_init(); |
| 257 | ERR_load_crypto_strings(); |
| 258 | SSL_load_error_strings(); |
| 259 | OpenSSL_add_all_algorithms(); |
| 260 | r = RAND_poll(); |
| 261 | if (r == 0) { |
| 262 | fprintf(stderr, "RAND_poll() failed.\n"); |
| 263 | return 1; |
| 264 | } |
| 265 | ssl_ctx = SSL_CTX_new(SSLv23_method()); |
| 266 | } |
| 267 | |
| 268 | listener = evconnlistener_new_bind(base, accept_cb, NULL, |
| 269 | LEV_OPT_CLOSE_ON_FREE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_REUSEABLE, |
| 270 | -1, (struct sockaddr*)&listen_on_addr, socklen); |
| 271 | |
| 272 | event_base_dispatch(base); |
| 273 | |
| 274 | evconnlistener_free(listener); |
| 275 | event_base_free(base); |
| 276 | |
| 277 | return 0; |
| 278 | } |