• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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