Home | History | Annotate | Download | only in src
      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 
     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 
     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 
     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 
    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... */
    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 
    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 
    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 
    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 
    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 
    362 int proxy_gets (BIO *b, char *buf, int size)
    363 {
    364   return BIO_gets (b->next_bio, buf, size);
    365 }
    366 
    367 int proxy_puts (BIO *b, const char *str)
    368 {
    369   return BIO_puts (b->next_bio, str);
    370 }
    371 
    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 
    393 BIO_METHOD *BIO_f_proxy()
    394 {
    395   return &proxy_methods;
    396 }
    397 
    398 /* API starts here */
    399 
    400 BIO API *BIO_new_proxy()
    401 {
    402   return BIO_new (BIO_f_proxy());
    403 }
    404 
    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 
    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 
    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