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@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 
socks4a_connect(proxy_polarssl_ctx * ctx)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 
socks5_connect(proxy_polarssl_ctx * ctx)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... */
sock_gets(proxy_polarssl_ctx * ctx,char * buf,size_t sz)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 
http_connect(proxy_polarssl_ctx * ctx)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 
proxy_polarssl_init(proxy_polarssl_ctx * ctx)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 
proxy_polarssl_set_bio(proxy_polarssl_ctx * ctx,int (* f_recv)(void *,unsigned char *,size_t),void * p_recv,int (* f_send)(void *,const unsigned char *,size_t),void * p_send)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 
proxy_polarssl_free(proxy_polarssl_ctx * ctx)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 
proxy_polarssl_set_scheme(proxy_polarssl_ctx * ctx,const char * scheme)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 
proxy_polarssl_set_host(proxy_polarssl_ctx * ctx,const char * host)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 
proxy_polarssl_set_port(proxy_polarssl_ctx * ctx,uint16_t port)320 void API proxy_polarssl_set_port(proxy_polarssl_ctx *ctx, uint16_t port)
321 {
322   ctx->port = port;
323 }
324 
proxy_polarssl_recv(void * ctx,unsigned char * data,size_t len)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 
proxy_polarssl_send(void * ctx,const unsigned char * data,size_t len)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