diff options
author | Jeff Burdges <burdges@gnunet.org> | 2015-07-31 12:20:36 +0000 |
---|---|---|
committer | Jeff Burdges <burdges@gnunet.org> | 2015-07-31 12:20:36 +0000 |
commit | 56349f844b7e04b352c9baeb03e96eeaae54efd2 (patch) | |
tree | 95271fe954b534cd7f868666b382e49f32b93ba4 /src/util/socks.c | |
parent | aa6f2fcf62b6128ac6172dc408e22483afeeef3b (diff) | |
download | gnunet-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.c | 601 |
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 | |||
60 | const 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 | */ | ||
87 | unsigned 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 | */ | ||
109 | struct 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 | |||
157 | void | ||
158 | register_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 | |||
166 | struct GNUNET_CONNECTION_TransmitHandle * | ||
167 | register_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 | */ | ||
178 | void | ||
179 | SOCKS5_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 | */ | ||
190 | void | ||
191 | SOCKS5_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 | */ | ||
293 | void | ||
294 | reciever (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 | */ | ||
314 | void | ||
315 | register_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 | |||
334 | size_t | ||
335 | transmit_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 | */ | ||
382 | struct GNUNET_CONNECTION_TransmitHandle * | ||
383 | register_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 | */ | ||
408 | struct GNUNET_SOCKS_Handshake * | ||
409 | GNUNET_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 | */ | ||
453 | struct GNUNET_SOCKS_Handshake * | ||
454 | GNUNET_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 | */ | ||
468 | void | ||
469 | GNUNET_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 | */ | ||
511 | struct GNUNET_CONNECTION_Handle * | ||
512 | GNUNET_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 | */ | ||
530 | int | ||
531 | GNUNET_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 | */ | ||
547 | struct GNUNET_CONNECTION_Handle * | ||
548 | GNUNET_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 */ | ||