Home | History | Annotate | Download | only in src
      1 /*
      2  * proxy-polarssl.c - Net stack layer for SOCKS4a/5 proxy connections
      3  *
      4  * Based on proxy-bio.c - Original copyright (c) 2012 The Chromium OS Authors.
      5  *
      6  * This file was adapted by Paul Bakker <p.j.bakker (at) offspark.com>
      7  * All rights reserved.
      8  *
      9  * Use of this source code is governed by a BSD-style license that can be
     10  * found in the LICENSE file.
     11  *
     12  * This file implements a SOCKS4a/SOCKS5 net layer as used by PolarSSL.
     13  */
     14 
     15 #include "config.h"
     16 
     17 #include <arpa/inet.h>
     18 #include <assert.h>
     19 #ifndef __USE_MISC
     20 #define __USE_MISC
     21 #endif
     22 #ifndef __USE_POSIX
     23 #define __USE_POSIX
     24 #endif
     25 #include <netdb.h>
     26 #include <stdint.h>
     27 #include <stdio.h>
     28 
     29 #ifndef HAVE_STRNLEN
     30 #include "src/common/strnlen.h"
     31 #endif
     32 
     33 #include "src/proxy-polarssl.h"
     34 #include "src/util.h"
     35 
     36 int socks4a_connect(proxy_polarssl_ctx *ctx)
     37 {
     38   int r;
     39   unsigned char buf[NI_MAXHOST + 16];
     40   uint16_t port_n;
     41   size_t sz = 0;
     42 
     43   if (!ctx)
     44     return 0;
     45 
     46   verb("V: proxy4: connecting %s:%d", ctx->host, ctx->port);
     47 
     48   port_n = htons(ctx->port);
     49 
     50   /*
     51    * Packet layout:
     52    * 1b: Version (must be 0x04)
     53    * 1b: command (0x01 is connect)
     54    * 2b: port number, big-endian
     55    * 4b: 0x00, 0x00, 0x00, 0x01 (bogus IPv4 addr)
     56    * 1b: 0x00 (empty 'userid' field)
     57    * nb: hostname, null-terminated
     58    */
     59   buf[0] = 0x04;
     60   buf[1] = 0x01;
     61   sz += 2;
     62 
     63   memcpy(buf + 2, &port_n, sizeof(port_n));
     64   sz += sizeof(port_n);
     65 
     66   buf[4] = 0x00;
     67   buf[5] = 0x00;
     68   buf[6] = 0x00;
     69   buf[7] = 0x01;
     70   sz += 4;
     71 
     72   buf[8] = 0x00;
     73   sz += 1;
     74 
     75   memcpy(buf + sz, ctx->host, strlen(ctx->host) + 1);
     76   sz += strlen(ctx->host) + 1;
     77 
     78   r = ctx->f_send(ctx->p_send, buf, sz);
     79   if (r != sz)
     80     return 0;
     81 
     82   /* server reply: 1 + 1 + 2 + 4 */
     83   r = ctx->f_recv(ctx->p_recv, buf, 8);
     84   if (r != 8)
     85     return 0;
     86 
     87   if (buf[1] == 0x5a) {
     88     verb("V: proxy4: connected");
     89     ctx->connected = 1;
     90     return 1;
     91   }
     92   return 0;
     93 }
     94 
     95 int socks5_connect(proxy_polarssl_ctx *ctx)
     96 {
     97   unsigned char buf[NI_MAXHOST + 16];
     98   int r;
     99   uint16_t port_n;
    100   size_t sz = 0;
    101 
    102   if (!ctx)
    103     return 0;
    104 
    105   /* the length for SOCKS addresses is only one byte. */
    106   if (strnlen(ctx->host, UINT8_MAX + 1) == UINT8_MAX + 1)
    107     return 0;
    108 
    109   verb("V: proxy5: connecting %s:%d", ctx->host, ctx->port);
    110 
    111   port_n = htons(ctx->port);
    112 
    113   /*
    114    * Hello packet layout:
    115    * 1b: Version
    116    * 1b: auth methods
    117    * nb: method types
    118    *
    119    * We support only one method (no auth, 0x00). Others listed in RFC
    120    * 1928.
    121    */
    122   buf[0] = 0x05;
    123   buf[1] = 0x01;
    124   buf[2] = 0x00;
    125 
    126   r = ctx->f_send(ctx->p_send, buf, 3);
    127   if (r != 3)
    128     return 0;
    129 
    130   r = ctx->f_recv(ctx->p_recv, buf, 2);
    131   if (r != 2)
    132     return 0;
    133 
    134   if (buf[0] != 0x05 || buf[1] != 0x00) {
    135     verb("V: proxy5: auth error %02x %02x", buf[0], buf[1]);
    136     return 0;
    137   }
    138 
    139   /*
    140    * Connect packet layout:
    141    * 1b: version
    142    * 1b: command (0x01 is connect)
    143    * 1b: reserved, 0x00
    144    * 1b: addr type (0x03 is domain name)
    145    * nb: addr len (1b) + addr bytes, no null termination
    146    * 2b: port, network byte order
    147    */
    148   buf[0] = 0x05;
    149   buf[1] = 0x01;
    150   buf[2] = 0x00;
    151   buf[3] = 0x03;
    152   buf[4] = strlen(ctx->host);
    153   sz += 5;
    154   memcpy(buf + 5, ctx->host, strlen(ctx->host));
    155   sz += strlen(ctx->host);
    156   memcpy(buf + sz, &port_n, sizeof(port_n));
    157   sz += sizeof(port_n);
    158 
    159   r = ctx->f_send(ctx->p_send, buf, sz);
    160   if (r != sz)
    161     return 0;
    162 
    163   /*
    164    * Server's response:
    165    * 1b: version
    166    * 1b: status (0x00 is okay)
    167    * 1b: reserved, 0x00
    168    * 1b: addr type (0x03 is domain name, 0x01 ipv4)
    169    * nb: addr len (1b) + addr bytes, no null termination
    170    * 2b: port, network byte order
    171    */
    172 
    173   /* grab up through the addr type */
    174   r = ctx->f_recv(ctx->p_recv, buf, 4);
    175   if (r != 4)
    176     return 0;
    177 
    178   if (buf[0] != 0x05 || buf[1] != 0x00) {
    179     verb("V: proxy5: connect error %02x %02x", buf[0], buf[1]);
    180     return 0;
    181   }
    182 
    183   if (buf[3] == 0x03) {
    184     unsigned int len;
    185     r = ctx->f_recv(ctx->p_recv, buf + 4, 1);
    186     if (r != 1)
    187       return 0;
    188     /* host (buf[4] bytes) + port (2 bytes) */
    189     len = buf[4] + 2;
    190     while (len) {
    191       r = ctx->f_recv(ctx->p_recv, buf + 5, min(len, sizeof(buf)));
    192       if (r <= 0)
    193         return 0;
    194       len -= min(len, r);
    195     }
    196   } else if (buf[3] == 0x01) {
    197     /* 4 bytes ipv4 addr, 2 bytes port */
    198     r = ctx->f_recv(ctx->p_recv, buf + 4, 6);
    199     if (r != 6)
    200       return 0;
    201   }
    202 
    203   verb("V: proxy5: connected");
    204   ctx->connected = 1;
    205   return 1;
    206 }
    207 
    208 /* SSL socket BIOs don't support BIO_gets, so... */
    209 int sock_gets(proxy_polarssl_ctx *ctx, char *buf, size_t sz)
    210 {
    211   unsigned char c;
    212   while (ctx->f_recv(ctx->p_recv, &c, 1) > 0 && sz > 1) {
    213     *buf++ = c;
    214     sz--;
    215     if (c == '\n') {
    216       *buf = '\0';
    217       return 0;
    218     }
    219   }
    220   return 1;
    221 }
    222 
    223 int http_connect(proxy_polarssl_ctx *ctx)
    224 {
    225   int r;
    226   char buf[4096];
    227   int retcode;
    228 
    229   snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.1\r\n",
    230            ctx->host, ctx->port);
    231   r = ctx->f_send(ctx->p_send, (unsigned char *) buf, strlen(buf));
    232   if (r != strlen(buf))
    233     return 0;
    234   /* required by RFC 2616 14.23 */
    235   snprintf(buf, sizeof(buf), "Host: %s:%d\r\n", ctx->host, ctx->port);
    236   r = ctx->f_send(ctx->p_send, (unsigned char *) buf, strlen(buf));
    237   if (r != strlen(buf))
    238     return 0;
    239   strcpy(buf, "\r\n");
    240   r = ctx->f_send(ctx->p_send, (unsigned char *) buf, strlen(buf));
    241   if (r != strlen(buf))
    242     return 0;
    243 
    244   r = sock_gets(ctx, buf, sizeof(buf));
    245   if (r)
    246     return 0;
    247   /* use %*s to ignore the version */
    248   if (sscanf(buf, "HTTP/%*s %d", &retcode) != 1)
    249     return 0;
    250 
    251   if (retcode < 200 || retcode > 299)
    252     return 0;
    253   while (!(r = sock_gets(ctx, buf, sizeof(buf)))) {
    254     if (!strcmp(buf, "\r\n")) {
    255       /* Done with the header */
    256       ctx->connected = 1;
    257       return 1;
    258     }
    259   }
    260   return 0;
    261 }
    262 
    263 int API proxy_polarssl_init(proxy_polarssl_ctx *ctx)
    264 {
    265   if (!ctx)
    266     return 0;
    267 
    268   memset(ctx, 0, sizeof(proxy_polarssl_ctx));
    269   return 1;
    270 }
    271 
    272 void API proxy_polarssl_set_bio(proxy_polarssl_ctx *ctx,
    273                  int (*f_recv)(void *, unsigned char *, size_t), void *p_recv,
    274                  int (*f_send)(void *, const unsigned char *, size_t), void *p_send)
    275 {
    276   if (!ctx)
    277     return;
    278 
    279   ctx->f_recv = f_recv;
    280   ctx->p_recv = p_recv;
    281   ctx->f_send = f_send;
    282   ctx->p_send = p_send;
    283 }
    284 
    285 int API proxy_polarssl_free(proxy_polarssl_ctx *ctx)
    286 {
    287   if (!ctx)
    288     return 0;
    289 
    290   if (ctx->host)
    291   {
    292     free(ctx->host);
    293     ctx->host = NULL;
    294   }
    295 
    296   return 1;
    297 }
    298 
    299 int API proxy_polarssl_set_scheme(proxy_polarssl_ctx *ctx, const char *scheme)
    300 {
    301   if (!strcmp(scheme, "socks5"))
    302     ctx->f_connect = socks5_connect;
    303   else if (!strcmp(scheme, "socks4"))
    304     ctx->f_connect = socks4a_connect;
    305   else if (!strcmp(scheme, "http"))
    306     ctx->f_connect = http_connect;
    307   else
    308     return 1;
    309   return 0;
    310 }
    311 
    312 int API proxy_polarssl_set_host(proxy_polarssl_ctx *ctx, const char *host)
    313 {
    314   if (strnlen(host, NI_MAXHOST) == NI_MAXHOST)
    315     return 1;
    316   ctx->host = strdup(host);
    317   return 0;
    318 }
    319 
    320 void API proxy_polarssl_set_port(proxy_polarssl_ctx *ctx, uint16_t port)
    321 {
    322   ctx->port = port;
    323 }
    324 
    325 int API proxy_polarssl_recv(void *ctx, unsigned char *data, size_t len)
    326 {
    327   proxy_polarssl_ctx *proxy = (proxy_polarssl_ctx *) ctx;
    328   int r;
    329 
    330   if (!ctx)
    331     return -1;
    332 
    333   if (!proxy->connected)
    334   {
    335     r = proxy->f_connect(ctx);
    336     if (r)
    337       return (r);
    338   }
    339 
    340   return proxy->f_recv(proxy->p_recv, data, len);
    341 }
    342 
    343 
    344 int API proxy_polarssl_send(void *ctx, const unsigned char *data, size_t len)
    345 {
    346   proxy_polarssl_ctx *proxy = (proxy_polarssl_ctx *) ctx;
    347   int r;
    348 
    349   if (!ctx)
    350     return -1;
    351 
    352   if (!proxy->connected)
    353   {
    354     r = proxy->f_connect(ctx);
    355     if (r)
    356       return (r);
    357   }
    358 
    359   return proxy->f_send(proxy->p_send, data, len);
    360 }
    361