aboutsummaryrefslogtreecommitdiff
path: root/src/util/socks.c
diff options
context:
space:
mode:
authorJeff Burdges <burdges@gnunet.org>2015-07-31 12:20:36 +0000
committerJeff Burdges <burdges@gnunet.org>2015-07-31 12:20:36 +0000
commit56349f844b7e04b352c9baeb03e96eeaae54efd2 (patch)
tree95271fe954b534cd7f868666b382e49f32b93ba4 /src/util/socks.c
parentaa6f2fcf62b6128ac6172dc408e22483afeeef3b (diff)
downloadgnunet-56349f844b7e04b352c9baeb03e96eeaae54efd2.tar.gz
gnunet-56349f844b7e04b352c9baeb03e96eeaae54efd2.zip
Initial SOCKS5 proxy code. This will not yet work properly.
Diffstat (limited to 'src/util/socks.c')
-rw-r--r--src/util/socks.c601
1 files changed, 601 insertions, 0 deletions
diff --git a/src/util/socks.c b/src/util/socks.c
new file mode 100644
index 000000000..43201c53b
--- /dev/null
+++ b/src/util/socks.c
@@ -0,0 +1,601 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013 Jeffrey Burdges (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21/**
22 * @file util/socks.c
23 * @brief SOCKS5 connection support
24 * @author Jeffrey Burdges
25 *
26 * These routines should be called only on newly active connections.
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31
32#define LOG(kind,...) GNUNET_log_from (kind, "socks", __VA_ARGS__)
33
34#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "socks", syscall)
35
36
37
38/* SOCKS5 authentication methods */
39#define SOCKS5_AUTH_REJECT 0xFF /* No acceptable auth method */
40#define SOCKS5_AUTH_NOAUTH 0x00 /* without authentication */
41#define SOCKS5_AUTH_GSSAPI 0x01 /* GSSAPI */
42#define SOCKS5_AUTH_USERPASS 0x02 /* User/Password */
43#define SOCKS5_AUTH_CHAP 0x03 /* Challenge-Handshake Auth Proto. */
44#define SOCKS5_AUTH_EAP 0x05 /* Extensible Authentication Proto. */
45#define SOCKS5_AUTH_MAF 0x08 /* Multi-Authentication Framework */
46
47
48/* SOCKS5 connection responces */
49#define SOCKS5_REP_SUCCEEDED 0x00 /* succeeded */
50#define SOCKS5_REP_FAIL 0x01 /* general SOCKS serer failure */
51#define SOCKS5_REP_NALLOWED 0x02 /* connection not allowed by ruleset */
52#define SOCKS5_REP_NUNREACH 0x03 /* Network unreachable */
53#define SOCKS5_REP_HUNREACH 0x04 /* Host unreachable */
54#define SOCKS5_REP_REFUSED 0x05 /* connection refused */
55#define SOCKS5_REP_EXPIRED 0x06 /* TTL expired */
56#define SOCKS5_REP_CNOTSUP 0x07 /* Command not supported */
57#define SOCKS5_REP_ANOTSUP 0x08 /* Address not supported */
58#define SOCKS5_REP_INVADDR 0x09 /* Inalid address */
59
60const char * SOCKS5_REP_names(int rep)
61{
62 switch (rep) {
63 case SOCKS5_REP_SUCCEEDED: return "succeeded";
64 case SOCKS5_REP_FAIL: return "general SOCKS server failure";
65 case SOCKS5_REP_NALLOWED: return "connection not allowed by ruleset";
66 case SOCKS5_REP_NUNREACH: return "Network unreachable";
67 case SOCKS5_REP_HUNREACH: return "Host unreachable";
68 case SOCKS5_REP_REFUSED: return "connection refused";
69 case SOCKS5_REP_EXPIRED: return "TTL expired";
70 case SOCKS5_REP_CNOTSUP: return "Command not supported";
71 case SOCKS5_REP_ANOTSUP: return "Address not supported";
72 case SOCKS5_REP_INVADDR: return "Invalid address";
73 default: return NULL;
74 }
75};
76
77
78/**
79 * Encode a string for the SOCKS5 protocol by prefixing it a byte stating its
80 * length and stipping the trailing zero byte. Truncates any string longer
81 * than 255 bytes.
82 *
83 * @param b buffer to contain the encoded string
84 * @param s string to encode
85 * @return pointer to the end of the encoded string in the buffer
86 */
87unsigned char * SOCK5_proto_string(unsigned char * b, const char * s)
88{
89 size_t l = strlen(s);
90 if (l>255) {
91 LOG (GNUNET_ERROR_TYPE_WARNING,
92 "SOCKS5 cannot handle hostnames, usernames, or passwords over 255 bytes, truncating.\n");
93 l=255;
94 }
95 *(b++) = (unsigned char)l;
96 strncpy((char *)b,s,l);
97 return b+l;
98}
99
100
101#define SOCKS5_step_greet 0
102#define SOCKS5_step_auth 1
103#define SOCKS5_step_cmd 2
104#define SOCKS5_step_done 3
105
106/**
107 * State of the SOCKS5 handshake.
108 */
109struct GNUNET_SOCKS_Handshake
110{
111
112 /**
113 * Connection handle used for SOCKS5
114 */
115 struct GNUNET_CONNECTION_Handle *socks5_connection;
116
117 /**
118 * Connection handle initially returned to client
119 */
120 struct GNUNET_CONNECTION_Handle *target_connection;
121
122 /**
123 * Transmission handle on socks5_connection.
124 */
125 struct GNUNET_CONNECTION_TransmitHandle *th;
126
127 /**
128 * Our stage in the SOCKS5 handshake
129 */
130 int step;
131
132 /**
133 * Precomputed SOCKS5 handshake ouput buffer
134 */
135 unsigned char outbuf[1024];
136
137 /**
138 * Pointers delineating protoocol steps in the outbut buffer
139 */
140 unsigned char * (outstep[4]);
141
142 /**
143 * SOCKS5 handshake input buffer
144 */
145 unsigned char inbuf[1024];
146
147 /**
148 * Pointers delimiting the current step in the input buffer
149 */
150 unsigned char * instart;
151 unsigned char * inend;
152};
153
154
155/* Regitering prototypes */
156
157void
158register_reciever (struct GNUNET_SOCKS_Handshake *ih, int want);
159
160 /* In fact, the client sends first rule in GNUNet suggests one could take
161 * large mac read sizes without fear of screwing up the proxied protocol,
162 * but we make a proper SOCKS5 client. */
163#define register_reciever_wants(ih) ((SOCKS5_step_cmd == ih->step) ? 10 : 2)
164
165
166struct GNUNET_CONNECTION_TransmitHandle *
167register_sender (struct GNUNET_SOCKS_Handshake *ih,
168 struct GNUNET_TIME_Relative timeout);
169
170
171/**
172 * Conclude the SOCKS5 handshake successfully.
173 *
174 * @param ih SOCKS5 handshake, consumed here.
175 * @param c open unused connection, consumed here.
176 * @return Connection handle that becomes usable when the handshake completes.
177 */
178void
179SOCKS5_handshake_done(struct GNUNET_SOCKS_Handshake *ih)
180{
181 GNUNET_CONNECTION_acivate_proxied (ih->target_connection);
182}
183
184
185/**
186 * Read one step in the SOCKS5 handshake.
187 *
188 * @param ih SOCKS5 Handshake
189 */
190void
191SOCKS5_handshake_step (struct GNUNET_SOCKS_Handshake *ih)
192{
193 unsigned char * b = ih->instart;
194 size_t available = ih->inend - b;
195
196 int want = register_reciever_wants(ih);
197 if (available < want) {
198 register_reciever (ih, want - available);
199 return;
200 }
201 GNUNET_assert (SOCKS5_step_done > ih->step && ih->step >= 0);
202 switch (ih->step) {
203 case SOCKS5_step_greet: /* SOCKS5 server's greeting */
204 if (b[0] != 5)
205 {
206 LOG (GNUNET_ERROR_TYPE_ERROR,
207 "Not a SOCKS5 server\n");
208 GNUNET_assert (0);
209 }
210 switch (b[1]) {
211 case SOCKS5_AUTH_NOAUTH:
212 ih->step=SOCKS5_step_cmd; /* no authentication to do */
213 break;
214 case SOCKS5_AUTH_USERPASS:
215 ih->step=SOCKS5_step_auth;
216 break;
217 case SOCKS5_AUTH_REJECT:
218 LOG (GNUNET_ERROR_TYPE_ERROR,
219 "No authentication method accepted\n");
220 return;
221 default:
222 LOG (GNUNET_ERROR_TYPE_ERROR,
223 "Not a SOCKS5 server / Nonsensical authentication\n");
224 return;
225 }
226 b += 2;
227 break;
228 case SOCKS5_step_auth: /* SOCKS5 server's responce to authentication */
229 if (b[1] != 0)
230 {
231 LOG (GNUNET_ERROR_TYPE_ERROR,
232 "SOCKS5 authentication failed\n");
233 GNUNET_assert (0);
234 }
235 ih->step=SOCKS5_step_cmd;
236 b += 2;
237 break;
238 case SOCKS5_step_cmd: /* SOCKS5 server's responce to command */
239 if (b[0] != 5)
240 {
241 LOG (GNUNET_ERROR_TYPE_ERROR,
242 "SOCKS5 protocol error\n");
243 GNUNET_assert (0);
244 }
245 if (0 != b[1]) {
246 LOG (GNUNET_ERROR_TYPE_ERROR,
247 "SOCKS5 connection error : %s\n",
248 SOCKS5_REP_names(b[1]));
249 return;
250 }
251 b += 3;
252 /* There is no reason to verify host and port afaik. */
253 switch (*(b++)) {
254 case 1: /* IPv4 */
255 b += sizeof(struct in_addr); /* 4 */
256 break;
257 case 4: /* IPv6 */
258 b += sizeof(struct in6_addr); /* 16 */
259 break;
260 case 3: /* hostname */
261 b += *b;
262 break;
263 }
264 b += 2; /* port */
265 if (b > ih->inend) {
266 register_reciever (ih, b - ih->inend);
267 return;
268 }
269 ih->step = SOCKS5_step_done;
270 LOG (GNUNET_ERROR_TYPE_DEBUG,
271 "SOCKS5 server : %s\n",
272 SOCKS5_REP_names(b[1]));
273 ih->instart = b;
274 SOCKS5_handshake_done (ih);
275 return;
276 case SOCKS5_step_done:
277 GNUNET_assert (0);
278 }
279 ih->instart = b;
280 /* Do not reschedule the sender unless we're done reading.
281 * I imagine this lets us avoid ever cancelling the transmit handle. */
282 register_sender (ih, GNUNET_TIME_relative_get_minute_ ());
283}
284
285
286/**
287 * Callback to read from the SOCKS5 proxy.
288 *
289 * @param client the service
290 * @param handler function to call with the message
291 * @param handler_cls closure for @a handler
292 */
293void
294reciever (void *cls,
295 const void *buf, size_t available,
296 const struct sockaddr * addr,
297 socklen_t addrlen, int errCode)
298{
299 struct GNUNET_SOCKS_Handshake * ih = cls;
300 GNUNET_assert (&ih->inend[available] < &ih->inbuf[1024]);
301 memcpy(ih->inend, buf, available);
302 ih->inend += available;
303 SOCKS5_handshake_step (ih);
304}
305
306
307/**
308 * Register callback to read from the SOCKS5 proxy.
309 *
310 * @param client the service
311 * @param handler function to call with the message
312 * @param handler_cls closure for @a handler
313 */
314void
315register_reciever (struct GNUNET_SOCKS_Handshake *ih, int want)
316{
317 GNUNET_CONNECTION_receive (ih->socks5_connection,
318 want,
319 GNUNET_TIME_relative_get_minute_ (),
320 &reciever,
321 ih);
322}
323
324
325/**
326 * Register SOCKS5 handshake sender
327 *
328 * @param cls closure (SOCKS handshake)
329 * @param size number of bytes available in @a buf
330 * @param buf where the callee should write the message
331 * @return number of bytes written to @a buf
332 */
333
334size_t
335transmit_ready (void *cls,
336 size_t size,
337 void *buf)
338{
339 struct GNUNET_SOCKS_Handshake * ih = cls;
340
341 /* connection.c has many routines that call us with buf == NULL :
342 * signal_transmit_error() - DNS, etc. active
343 * connect_fail_continuation()
344 * connect_probe_continuation() - timeout
345 * try_connect_using_address() - DNS failure/timeout
346 * transmit_timeout() - retry failed?
347 * GNUNET_CONNECTION_notify_transmit_ready() can schedule :
348 * transmit_timeout() - DNS still working
349 * connect_error() - DNS done but no socket?
350 * transmit_ready() - scheduler shutdown or timeout, or signal_transmit_error()
351 * We'd need to dig into the scheduler to guess at the reason, as
352 * connection.c tells us nothing itself, but mostly its timouts.
353 * Initially, we'll simply ignore this and leave massive timeouts, but
354 * maybe that should change for error handling pruposes. It appears that
355 * successful operations, including DNS resolution, do not use this. */
356 if (NULL==buf)
357 {
358 GNUNET_break (0);
359 return 0;
360 }
361
362 GNUNET_assert (1024 >= size && size > 0);
363 GNUNET_assert (SOCKS5_step_done > ih->step && ih->step >= 0);
364 unsigned char * b = ih->outstep[ih->step];
365 unsigned char * e = ih->outstep[ih->step++];
366 GNUNET_assert (e <= &ih->outbuf[1024]);
367 unsigned l = e - b;
368 GNUNET_assert (size >= l && l >= 0);
369 memcpy(b, buf, l);
370 register_reciever (ih, register_reciever_wants(ih));
371 return l;
372}
373
374
375/**
376 * Register SOCKS5 handshake sender
377 *
378 * @param ih handshake
379 * @return non-NULL if the notify callback was queued,
380 * NULL if we are already going to notify someone else (busy)
381 */
382struct GNUNET_CONNECTION_TransmitHandle *
383register_sender (struct GNUNET_SOCKS_Handshake *ih,
384 struct GNUNET_TIME_Relative timeout)
385{
386 GNUNET_assert (SOCKS5_step_done > ih->step && ih->step >= 0);
387 unsigned char * b = ih->outstep[ih->step];
388 unsigned char * e = ih->outstep[ih->step+1];
389 GNUNET_assert (ih->outbuf <= b && b < e && e < &ih->outbuf[1024]);
390 ih->th = GNUNET_CONNECTION_notify_transmit_ready (ih->socks5_connection,
391 e - b,
392 timeout,
393 &transmit_ready,
394 ih);
395 return ih->th;
396}
397
398
399/**
400 * Initialize a SOCKS5 handshake for authentication via username and
401 * password. Tor uses SOCKS username and password authentication to assign
402 * programs unique circuits.
403 *
404 * @param user username for the proxy
405 * @param pass password for the proxy
406 * @return Valid SOCKS5 hanbdshake handle
407 */
408struct GNUNET_SOCKS_Handshake *
409GNUNET_SOCKS_init_handshake (const char *user, const char *pass)
410{
411 struct GNUNET_SOCKS_Handshake *ih = GNUNET_new (struct GNUNET_SOCKS_Handshake);
412 unsigned char * b = ih->outbuf;
413
414 ih->outstep[SOCKS5_step_greet] = b;
415 *(b++) = 5; /* SOCKS5 */
416 unsigned char * n = b++;
417 *n = 1; /* Number of authentication methods */
418 /* We support no authentication even when requesting authentication,
419 * but this appears harmless, given the way that Tor uses authentication.
420 * And some SOCKS5 servers might require this. */
421 *(b++) = SOCKS5_AUTH_NOAUTH;
422 if (NULL != user) {
423 *(b++) = SOCKS5_AUTH_USERPASS;
424 (*n)++;
425 }
426 /* There is no apperent reason to support authentication methods beyond
427 * username and password since afaik Tor does not support them. */
428
429 /* We authenticate with an empty username and password if the server demands
430 * them but we do not have any. */
431 if (user == NULL)
432 user = "";
433 if (user == NULL)
434 pass = "";
435
436 ih->outstep[SOCKS5_step_auth] = b;
437 *(b++) = 1; /* subnegotiation ver.: 1 */
438 b = SOCK5_proto_string(b,user);
439 b = SOCK5_proto_string(b,pass);
440
441 ih->outstep[SOCKS5_step_cmd] = b;
442
443 return ih;
444}
445
446
447/**
448 * Initialize a SOCKS5 handshake without authentication, thereby possibly
449 * sharing a Tor circuit with another process.
450 *
451 * @return Valid SOCKS5 hanbdshake handle
452 */
453struct GNUNET_SOCKS_Handshake *
454GNUNET_SOCKS_init_handshake_noauth ()
455{
456 return GNUNET_SOCKS_init_handshake (NULL,NULL);
457}
458
459
460/**
461 * Build request that the SOCKS5 proxy open a TCP/IP stream to the given host
462 * and port.
463 *
464 * @param ih SOCKS5 handshake
465 * @param hostname
466 * @param port
467 */
468void
469GNUNET_SOCKS_set_handshake_destination (struct GNUNET_SOCKS_Handshake *ih,
470 const char *host, uint16_t port)
471{
472 union {
473 struct in_addr in4;
474 struct in6_addr in6;
475 } ia;
476 unsigned char * b = ih->outstep[SOCKS5_step_cmd];
477
478 *(b++) = 5; /* SOCKS5 */
479 *(b++) = 1; /* Establish a TCP/IP stream */
480 *(b++) = 0; /* reserved */
481
482 /* Specify destination */
483 if (1 == inet_pton(AF_INET,host,&ia.in4)) {
484 *(b++)= 1; /* IPv4 */
485 memcpy (b, &ia.in4, sizeof(struct in_addr));
486 b += sizeof(struct in_addr); /* 4 */
487 } else if (1 == inet_pton(AF_INET6,host,&ia.in6)) {
488 *(b++)= 4; /* IPv6 */
489 memcpy (b, &ia.in6, sizeof(struct in6_addr));
490 b += sizeof(struct in6_addr); /* 16 */
491 } else {
492 *(b++)= 3; /* hostname */
493 b = SOCK5_proto_string (b, host);
494 }
495
496 /* Specify port */
497 *(uint16_t*)b = htons (port);
498 b += 2;
499
500 ih->outstep[SOCKS5_step_done] = b;
501}
502
503
504/**
505 * Run a SOCKS5 handshake on an open but unused TCP connection.
506 *
507 * @param ih SOCKS5 handshake, consumed here.
508 * @param c open unused connection, consumed here.
509 * @return Connection handle that becomes usable when the SOCKS5 handshake completes.
510 */
511struct GNUNET_CONNECTION_Handle *
512GNUNET_SOCKS_run_handshake(struct GNUNET_SOCKS_Handshake *ih,
513 struct GNUNET_CONNECTION_Handle *c)
514{
515 ih->socks5_connection=c;
516 ih->target_connection = GNUNET_CONNECTION_create_proxied_from_handshake (c);
517 register_sender (ih, GNUNET_TIME_relative_get_forever_ () );
518 return ih->target_connection;
519}
520
521
522/**
523 * Check if a SOCKS proxy is required by a service. Do not use local service
524 * if a SOCKS proxy port is configured as this could deanonymize a user.
525 *
526 * @param service_name name of service to connect to
527 * @param cfg configuration to use
528 * @return GNUNET_YES if so, GNUNET_NO if not
529 */
530int
531GNUNET_SOCKS_check_service (const char *service_name,
532 const struct GNUNET_CONFIGURATION_Handle *cfg)
533{
534 return GNUNET_CONFIGURATION_have_value (cfg, service_name, "SOCKSPORT") ||
535 GNUNET_CONFIGURATION_have_value (cfg, service_name, "SOCKSHOST");
536}
537
538
539/**
540 * Try to connect to a service configured to use a SOCKS5 proxy.
541 *
542 * @param service_name name of service to connect to
543 * @param cfg configuration to use
544 * @return Connection handle that becomes usable when the handshake completes.
545 * NULL if SOCKS not configured or not configured properly
546 */
547struct GNUNET_CONNECTION_Handle *
548GNUNET_SOCKS_do_connect (const char *service_name,
549 const struct GNUNET_CONFIGURATION_Handle *cfg)
550{
551 struct GNUNET_SOCKS_Handshake *ih;
552 struct GNUNET_CONNECTION_Handle *socks5; /* *proxied */
553 char *host0,*host1,*user,*pass;
554 unsigned long long port0,port1;
555
556 if (GNUNET_YES != GNUNET_SOCKS_check_service (service_name, cfg))
557 return NULL;
558 if (GNUNET_OK !=
559 GNUNET_CONFIGURATION_get_value_number (cfg, service_name, "SOCKSPORT", &port0))
560 port0 = 9050;
561 /* A typical Tor client should usually try port 9150 for the TBB too, but
562 * GUNNet can probably assume a system Tor instalation. */
563 if (GNUNET_OK !=
564 GNUNET_CONFIGURATION_get_value_string (cfg, service_name, "SOCKSHOST", &host0))
565 host0 = "127.0.0.1";
566 if (port0 > 65535 || port0 <= 0)
567 {
568 LOG (GNUNET_ERROR_TYPE_WARNING,
569 _
570 ("Attempting to use invalid port or hostname for SOCKS proxy for service `%s'.\n"),
571 service_name);
572 return NULL;
573 }
574
575 if ((GNUNET_OK !=
576 GNUNET_CONFIGURATION_get_value_number (cfg, service_name, "PORT", &port1))
577 || (port1 > 65535) || (port1 <= 0) ||
578 (GNUNET_OK !=
579 GNUNET_CONFIGURATION_get_value_string (cfg, service_name, "HOST", &host1)))
580 {
581 LOG (GNUNET_ERROR_TYPE_WARNING,
582 _
583 ("Attempting to proxy service `%s' to invalid port or hostname.\n"),
584 service_name);
585 return NULL;
586 }
587
588 socks5 = GNUNET_CONNECTION_create_from_connect (cfg, host0, port0);
589 GNUNET_free (host0);
590
591 /* Sets to NULL if they do not exist */
592 GNUNET_CONFIGURATION_get_value_string (cfg, service_name, "SOCKSUSER", &user);
593 GNUNET_CONFIGURATION_get_value_string (cfg, service_name, "SOCKSPASS", &pass);
594 ih = GNUNET_SOCKS_init_handshake(user,pass);
595 GNUNET_SOCKS_set_handshake_destination (ih,host1,port1);
596 GNUNET_free (host1);
597
598 return GNUNET_SOCKS_run_handshake(ih,socks5);
599}
600
601/* socks.c */