1 /*
2 * proxy-bio.c - BIO layer for SOCKS4a/5 proxy connections
3 *
4 * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 *
8 * This file implements a SOCKS4a/SOCKS5 "filter" BIO. In SSL terminology, a BIO
9 * is a stackable IO filter, kind of like sysv streams. These filters are
10 * inserted into a stream to cause it to run SOCKS over whatever transport is
11 * being used. Most commonly, this would be:
12 * SSL BIO (filter) -> SOCKS BIO (filter) -> connect BIO (source/sink)
13 * This configuration represents doing an SSL connection through a SOCKS proxy,
14 * which is itself connected to in plaintext. You might also do:
15 * SSL BIO -> SOCKS BIO -> SSL BIO -> connect BIO
16 * This is an SSL connection through a SOCKS proxy which is itself reached over
17 * SSL.
18 */
19
20 #include <arpa/inet.h>
21 #include <assert.h>
22 #ifndef __USE_MISC
23 #define __USE_MISC
24 #endif
25 #ifndef __USE_POSIX
26 #define __USE_POSIX
27 #endif
28
29 #ifndef NI_MAXHOST
30 #define NI_MAXHOST 1025
31 #endif
32
33 #ifndef UINT8_MAX
34 #define UINT8_MAX (255)
35 #endif
36
37 #include <netdb.h>
38
39 #include <inttypes.h>
40
41 #include "src/proxy-bio-plan9.h"
42
43 int socks4a_connect(BIO *b);
44 int socks5_connect(BIO *b);
45 int http_connect(BIO *b);
46
proxy_new(BIO * b)47 int proxy_new(BIO *b)
48 {
49 struct proxy_ctx *ctx = (struct proxy_ctx *) malloc(sizeof *ctx);
50 if (!ctx)
51 return 0;
52 ctx->connected = 0;
53 ctx->connect = NULL;
54 ctx->host = NULL;
55 ctx->port = 0;
56 b->init = 1;
57 b->flags = 0;
58 b->ptr = ctx;
59 return 1;
60 }
61
proxy_free(BIO * b)62 int proxy_free(BIO *b)
63 {
64 struct proxy_ctx *c;
65 if (!b || !b->ptr)
66 return 1;
67 c = (struct proxy_ctx *) b->ptr;
68 if (c->host)
69 free(c->host);
70 c->host = NULL;
71 b->ptr = NULL;
72 free(c);
73 return 1;
74 }
75
socks4a_connect(BIO * b)76 int socks4a_connect(BIO *b)
77 {
78 struct proxy_ctx *ctx = (struct proxy_ctx *) b->ptr;
79 int r;
80 unsigned char buf[NI_MAXHOST + 16];
81 uint16_t port_n = htons(ctx->port);
82 size_t sz = 0;
83
84 verb("V: proxy4: connecting %s:%d", ctx->host, ctx->port);
85
86 /*
87 * Packet layout:
88 * 1b: Version (must be 0x04)
89 * 1b: command (0x01 is connect)
90 * 2b: port number, big-endian
91 * 4b: 0x00, 0x00, 0x00, 0x01 (bogus IPv4 addr)
92 * 1b: 0x00 (empty 'userid' field)
93 * nb: hostname, null-terminated
94 */
95 buf[0] = 0x04;
96 buf[1] = 0x01;
97 sz += 2;
98
99 memcpy(buf + 2, &port_n, sizeof(port_n));
100 sz += sizeof(port_n);
101
102 buf[4] = 0x00;
103 buf[5] = 0x00;
104 buf[6] = 0x00;
105 buf[7] = 0x01;
106 sz += 4;
107
108 buf[8] = 0x00;
109 sz += 1;
110
111 memcpy(buf + sz, ctx->host, strlen(ctx->host) + 1);
112 sz += strlen(ctx->host) + 1;
113
114 r = BIO_write(b->next_bio, buf, sz);
115 if ( -1 == r )
116 return -1;
117 if ( (size_t) r != sz)
118 return 0;
119
120 /* server reply: 1 + 1 + 2 + 4 */
121 r = BIO_read(b->next_bio, buf, 8);
122 if ( -1 == r )
123 return -1;
124 if ( (size_t) r != 8)
125 return 0;
126 if (buf[1] == 0x5a) {
127 verb("V: proxy4: connected");
128 ctx->connected = 1;
129 return 1;
130 }
131 return 0;
132 }
133
socks5_connect(BIO * b)134 int socks5_connect(BIO *b)
135 {
136 unsigned char buf[NI_MAXHOST + 16];
137 int r;
138 struct proxy_ctx *ctx = (struct proxy_ctx *) b->ptr;
139 uint16_t port_n = htons(ctx->port);
140 size_t sz = 0;
141
142 /* the length for SOCKS addresses is only one byte. */
143 if (strlen(ctx->host) == UINT8_MAX + 1)
144 return 0;
145
146 verb("V: proxy5: connecting %s:%d", ctx->host, ctx->port);
147
148 /*
149 * Hello packet layout:
150 * 1b: Version
151 * 1b: auth methods
152 * nb: method types
153 *
154 * We support only one method (no auth, 0x00). Others listed in RFC
155 * 1928.
156 */
157 buf[0] = 0x05;
158 buf[1] = 0x01;
159 buf[2] = 0x00;
160
161 r = BIO_write(b->next_bio, buf, 3);
162 if (r != 3)
163 return 0;
164
165 r = BIO_read(b->next_bio, buf, 2);
166 if (r != 2)
167 return 0;
168
169 if (buf[0] != 0x05 || buf[1] != 0x00) {
170 verb("V: proxy5: auth error %02x %02x", buf[0], buf[1]);
171 return 0;
172 }
173
174 /*
175 * Connect packet layout:
176 * 1b: version
177 * 1b: command (0x01 is connect)
178 * 1b: reserved, 0x00
179 * 1b: addr type (0x03 is domain name)
180 * nb: addr len (1b) + addr bytes, no null termination
181 * 2b: port, network byte order
182 */
183 buf[0] = 0x05;
184 buf[1] = 0x01;
185 buf[2] = 0x00;
186 buf[3] = 0x03;
187 buf[4] = strlen(ctx->host);
188 sz += 5;
189 memcpy(buf + 5, ctx->host, strlen(ctx->host));
190 sz += strlen(ctx->host);
191 memcpy(buf + sz, &port_n, sizeof(port_n));
192 sz += sizeof(port_n);
193
194 r = BIO_write(b->next_bio, buf, sz);
195 if ( -1 == r )
196 return -1;
197 if ( (size_t) r != sz)
198 return 0;
199
200 /*
201 * Server's response:
202 * 1b: version
203 * 1b: status (0x00 is okay)
204 * 1b: reserved, 0x00
205 * 1b: addr type (0x03 is domain name, 0x01 ipv4)
206 * nb: addr len (1b) + addr bytes, no null termination
207 * 2b: port, network byte order
208 */
209
210 /* grab up through the addr type */
211 r = BIO_read(b->next_bio, buf, 4);
212 if ( -1 == r )
213 return -1;
214 if (r != 4)
215 return 0;
216
217 if (buf[0] != 0x05 || buf[1] != 0x00) {
218 verb("V: proxy5: connect error %02x %02x", buf[0], buf[1]);
219 return 0;
220 }
221
222 if (buf[3] == 0x03) {
223 unsigned int len;
224 r = BIO_read(b->next_bio, buf + 4, 1);
225 if (r != 1)
226 return 0;
227 /* host (buf[4] bytes) + port (2 bytes) */
228 len = buf[4] + 2;
229 while (len) {
230 r = BIO_read(b->next_bio, buf + 5, min(len, sizeof(buf)));
231 if (r <= 0)
232 return 0;
233 len -= min(len, r);
234 }
235 } else if (buf[3] == 0x01) {
236 /* 4 bytes ipv4 addr, 2 bytes port */
237 r = BIO_read(b->next_bio, buf + 4, 6);
238 if (r != 6)
239 return 0;
240 }
241
242 verb("V: proxy5: connected");
243 ctx->connected = 1;
244 return 1;
245 }
246
247 /* SSL socket BIOs don't support BIO_gets, so... */
sock_gets(BIO * b,char * buf,size_t sz)248 int sock_gets(BIO *b, char *buf, size_t sz)
249 {
250 char c;
251 while (BIO_read(b, &c, 1) > 0 && sz > 1) {
252 *buf++ = c;
253 sz--;
254 if (c == '\n') {
255 *buf = '\0';
256 return 0;
257 }
258 }
259 return 1;
260 }
261
http_connect(BIO * b)262 int http_connect(BIO *b)
263 {
264 int r;
265 struct proxy_ctx *ctx = (struct proxy_ctx *) b->ptr;
266 char buf[4096];
267 int retcode;
268
269 snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.1\r\n",
270 ctx->host, ctx->port);
271 r = BIO_write(b->next_bio, buf, strlen(buf));
272 if ( -1 == r )
273 return -1;
274 if ( (size_t) r != strlen(buf))
275 return 0;
276 /* required by RFC 2616 14.23 */
277 snprintf(buf, sizeof(buf), "Host: %s:%d\r\n", ctx->host, ctx->port);
278 r = BIO_write(b->next_bio, buf, strlen(buf));
279 if ( -1 == r )
280 return -1;
281 if ( (size_t) r != strlen(buf))
282 return 0;
283 strcpy(buf, "\r\n");
284 r = BIO_write(b->next_bio, buf, strlen(buf));
285 if ( -1 == r )
286 return -1;
287 if ( (size_t) r != strlen(buf))
288 return 0;
289
290 r = sock_gets(b->next_bio, buf, sizeof(buf));
291 if (r)
292 return 0;
293 /* use %*s to ignore the version */
294 if (sscanf(buf, "HTTP/%*s %d", &retcode) != 1)
295 return 0;
296
297 if (retcode < 200 || retcode > 299)
298 return 0;
299 while (!(r = sock_gets(b->next_bio, buf, sizeof(buf)))) {
300 if (!strcmp(buf, "\r\n")) {
301 /* Done with the header */
302 ctx->connected = 1;
303 return 1;
304 }
305 }
306 return 0;
307 }
308
proxy_write(BIO * b,const char * buf,int sz)309 int proxy_write(BIO *b, const char *buf, int sz)
310 {
311 int r;
312 struct proxy_ctx *ctx = (struct proxy_ctx *) b->ptr;
313
314 assert(buf);
315
316 if (sz <= 0)
317 return 0;
318
319 if (!b->next_bio)
320 return 0;
321
322 if (!ctx->connected) {
323 assert(ctx->connect);
324 if (!ctx->connect(b))
325 return 0;
326 }
327
328 r = BIO_write(b->next_bio, buf, sz);
329 BIO_clear_retry_flags(b);
330 BIO_copy_next_retry(b);
331 return r;
332 }
333
proxy_read(BIO * b,char * buf,int sz)334 int proxy_read(BIO *b, char *buf, int sz)
335 {
336 int r;
337 struct proxy_ctx *ctx = (struct proxy_ctx *) b->ptr;
338
339 assert(buf);
340
341 if (!b->next_bio)
342 return 0;
343
344 if (!ctx->connected) {
345 assert(ctx->connect);
346 if (!ctx->connect(b))
347 return 0;
348 }
349
350 r = BIO_read(b->next_bio, buf, sz);
351 BIO_clear_retry_flags(b);
352 BIO_copy_next_retry(b);
353 return r;
354 }
355
proxy_ctrl(BIO * b,int cmd,long num,void * ptr)356 long proxy_ctrl(BIO *b, int cmd, long num, void *ptr)
357 {
358 long ret;
359 struct proxy_ctx *ctx;
360 if (!b->next_bio)
361 return 0;
362 ctx = (struct proxy_ctx *) b->ptr;
363 assert(ctx);
364
365 switch (cmd) {
366 case BIO_C_DO_STATE_MACHINE:
367 BIO_clear_retry_flags(b);
368 ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
369 BIO_copy_next_retry(b);
370 break;
371 case BIO_CTRL_DUP:
372 ret = 0;
373 break;
374 default:
375 ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
376 }
377 return ret;
378 }
379
proxy_gets(BIO * b,char * buf,int size)380 int proxy_gets(BIO *b, char *buf, int size)
381 {
382 return BIO_gets(b->next_bio, buf, size);
383 }
384
proxy_puts(BIO * b,const char * str)385 int proxy_puts(BIO *b, const char *str)
386 {
387 return BIO_puts(b->next_bio, str);
388 }
389
proxy_callback_ctrl(BIO * b,int cmd,bio_info_cb * fp)390 long proxy_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
391 {
392 if (!b->next_bio)
393 return 0;
394 return BIO_callback_ctrl(b->next_bio, cmd, fp);
395 }
396
397 BIO_METHOD proxy_methods = {
398 BIO_TYPE_MEM,
399 "proxy",
400 proxy_write,
401 proxy_read,
402 proxy_puts,
403 proxy_gets,
404 proxy_ctrl,
405 proxy_new,
406 proxy_free,
407 proxy_callback_ctrl,
408 };
409
BIO_f_proxy()410 BIO_METHOD *BIO_f_proxy()
411 {
412 return &proxy_methods;
413 }
414
415 /* API starts here */
416
BIO_new_proxy()417 BIO API *BIO_new_proxy()
418 {
419 return BIO_new(BIO_f_proxy());
420 }
421
BIO_proxy_set_type(BIO * b,const char * type)422 int API BIO_proxy_set_type(BIO *b, const char *type)
423 {
424 struct proxy_ctx *ctx = (struct proxy_ctx *) b->ptr;
425 if (!strcmp(type, "socks5"))
426 ctx->connect = socks5_connect;
427 else if (!strcmp(type, "socks4a"))
428 ctx->connect = socks4a_connect;
429 else if (!strcmp(type, "http"))
430 ctx->connect = http_connect;
431 else
432 return 1;
433 return 0;
434 }
435
BIO_proxy_set_host(BIO * b,const char * host)436 int API BIO_proxy_set_host(BIO *b, const char *host)
437 {
438 struct proxy_ctx *ctx = (struct proxy_ctx *) b->ptr;
439 if (strlen(host) == NI_MAXHOST)
440 return 1;
441 ctx->host = strdup(host);
442 return 0;
443 }
444
BIO_proxy_set_port(BIO * b,uint16_t port)445 void API BIO_proxy_set_port(BIO *b, uint16_t port)
446 {
447 struct proxy_ctx *ctx = (struct proxy_ctx *) b->ptr;
448 ctx->port = port;
449 }
450