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