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