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