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