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