|
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 } |