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