Line data Source code
1 : /*********************************************************************
2 : *
3 : * File : $Source: /cvsroot/ijbswa/current/gateway.c,v $
4 : *
5 : * Purpose : Contains functions to connect to a server, possibly
6 : * using a "forwarder" (i.e. HTTP proxy and/or a SOCKS4
7 : * or SOCKS5 proxy).
8 : *
9 : * Copyright : Written by and Copyright (C) 2001-2020 the
10 : * Privoxy team. https://www.privoxy.org/
11 : *
12 : * Based on the Internet Junkbuster originally written
13 : * by and Copyright (C) 1997 Anonymous Coders and
14 : * Junkbusters Corporation. http://www.junkbusters.com
15 : *
16 : * This program is free software; you can redistribute it
17 : * and/or modify it under the terms of the GNU General
18 : * Public License as published by the Free Software
19 : * Foundation; either version 2 of the License, or (at
20 : * your option) any later version.
21 : *
22 : * This program is distributed in the hope that it will
23 : * be useful, but WITHOUT ANY WARRANTY; without even the
24 : * implied warranty of MERCHANTABILITY or FITNESS FOR A
25 : * PARTICULAR PURPOSE. See the GNU General Public
26 : * License for more details.
27 : *
28 : * The GNU General Public License should be included with
29 : * this file. If not, you can view it at
30 : * http://www.gnu.org/copyleft/gpl.html
31 : * or write to the Free Software Foundation, Inc., 59
32 : * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33 : *
34 : *********************************************************************/
35 :
36 :
37 : #include "config.h"
38 :
39 : #include <stdio.h>
40 : #include <sys/types.h>
41 :
42 : #ifndef _WIN32
43 : #include <netinet/in.h>
44 : #endif
45 :
46 : #include <errno.h>
47 : #include <string.h>
48 : #include "assert.h"
49 :
50 : #ifdef _WIN32
51 : #include <winsock2.h>
52 : #endif /* def _WIN32 */
53 :
54 : #ifdef __BEOS__
55 : #include <netdb.h>
56 : #endif /* def __BEOS__ */
57 :
58 : #include "project.h"
59 : #include "jcc.h"
60 : #include "errlog.h"
61 : #include "jbsockets.h"
62 : #include "gateway.h"
63 : #include "miscutil.h"
64 : #include "list.h"
65 : #include "parsers.h"
66 :
67 : #ifdef FEATURE_CONNECTION_KEEP_ALIVE
68 : #ifdef HAVE_POLL
69 : #ifdef __GLIBC__
70 : #include <sys/poll.h>
71 : #else
72 : #include <poll.h>
73 : #endif /* def __GLIBC__ */
74 : #endif /* HAVE_POLL */
75 : #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
76 :
77 : static jb_socket socks4_connect(const struct forward_spec *fwd,
78 : const char *target_host,
79 : int target_port,
80 : struct client_state *csp);
81 :
82 : static jb_socket socks5_connect(const struct forward_spec *fwd,
83 : const char *target_host,
84 : int target_port,
85 : struct client_state *csp);
86 :
87 : enum {
88 : SOCKS4_REQUEST_GRANTED = 90,
89 : SOCKS4_REQUEST_REJECT = 91,
90 : SOCKS4_REQUEST_IDENT_FAILED = 92,
91 : SOCKS4_REQUEST_IDENT_CONFLICT = 93
92 : };
93 :
94 : enum {
95 : SOCKS5_REQUEST_GRANTED = 0,
96 : SOCKS5_REQUEST_FAILED = 1,
97 : SOCKS5_REQUEST_DENIED = 2,
98 : SOCKS5_REQUEST_NETWORK_UNREACHABLE = 3,
99 : SOCKS5_REQUEST_HOST_UNREACHABLE = 4,
100 : SOCKS5_REQUEST_CONNECTION_REFUSED = 5,
101 : SOCKS5_REQUEST_TTL_EXPIRED = 6,
102 : SOCKS5_REQUEST_PROTOCOL_ERROR = 7,
103 : SOCKS5_REQUEST_BAD_ADDRESS_TYPE = 8
104 : };
105 :
106 : /* structure of a socks client operation */
107 : struct socks_op {
108 : unsigned char vn; /* socks version number */
109 : unsigned char cd; /* command code */
110 : unsigned char dstport[2]; /* destination port */
111 : unsigned char dstip[4]; /* destination address */
112 : char userid; /* first byte of userid */
113 : char padding[3]; /* make sure sizeof(struct socks_op) is endian-independent. */
114 : /* more bytes of the userid follow, terminated by a NULL */
115 : };
116 :
117 : /* structure of a socks server reply */
118 : struct socks_reply {
119 : unsigned char vn; /* socks version number */
120 : unsigned char cd; /* command code */
121 : unsigned char dstport[2]; /* destination port */
122 : unsigned char dstip[4]; /* destination address */
123 : };
124 :
125 : static const char socks_userid[] = "anonymous";
126 :
127 : #ifdef FEATURE_CONNECTION_SHARING
128 : #ifndef FEATURE_CONNECTION_KEEP_ALIVE
129 : #error Using FEATURE_CONNECTION_SHARING without FEATURE_CONNECTION_KEEP_ALIVE is impossible
130 : #endif
131 :
132 : #define MAX_REUSABLE_CONNECTIONS 10
133 :
134 : static struct reusable_connection reusable_connection[MAX_REUSABLE_CONNECTIONS];
135 : static int mark_connection_unused(const struct reusable_connection *connection);
136 :
137 : /*********************************************************************
138 : *
139 : * Function : initialize_reusable_connections
140 : *
141 : * Description : Initializes the reusable_connection structures.
142 : * Must be called with connection_reuse_mutex locked.
143 : *
144 : * Parameters : N/A
145 : *
146 : * Returns : void
147 : *
148 : *********************************************************************/
149 3098 : extern void initialize_reusable_connections(void)
150 : {
151 3098 : unsigned int slot = 0;
152 :
153 : #if !defined(HAVE_POLL) && !defined(_WIN32)
154 : log_error(LOG_LEVEL_INFO,
155 : "Detecting already dead connections might not work "
156 : "correctly on your platform. In case of problems, "
157 : "unset the keep-alive-timeout option.");
158 : #endif
159 :
160 34078 : for (slot = 0; slot < SZ(reusable_connection); slot++)
161 : {
162 30980 : mark_connection_closed(&reusable_connection[slot]);
163 : }
164 :
165 3098 : log_error(LOG_LEVEL_CONNECT, "Initialized %d socket slots.", slot);
166 3098 : }
167 :
168 :
169 : /*********************************************************************
170 : *
171 : * Function : remember_connection
172 : *
173 : * Description : Remembers a server connection for reuse later on.
174 : *
175 : * Parameters :
176 : * 1 : connection = The server connection to remember.
177 : *
178 : * Returns : void
179 : *
180 : *********************************************************************/
181 0 : void remember_connection(const struct reusable_connection *connection)
182 : {
183 0 : unsigned int slot = 0;
184 0 : int free_slot_found = FALSE;
185 :
186 0 : assert(NULL != connection);
187 0 : assert(connection->sfd != JB_INVALID_SOCKET);
188 :
189 0 : if (mark_connection_unused(connection))
190 : {
191 0 : return;
192 : }
193 :
194 0 : privoxy_mutex_lock(&connection_reuse_mutex);
195 :
196 : /* Find free socket slot. */
197 0 : for (slot = 0; slot < SZ(reusable_connection); slot++)
198 : {
199 0 : if (reusable_connection[slot].sfd == JB_INVALID_SOCKET)
200 : {
201 0 : assert(reusable_connection[slot].in_use == 0);
202 0 : log_error(LOG_LEVEL_CONNECT,
203 : "Remembering socket %d for %s:%d in slot %d.",
204 : connection->sfd, connection->host, connection->port, slot);
205 0 : free_slot_found = TRUE;
206 0 : break;
207 : }
208 : }
209 :
210 0 : if (!free_slot_found)
211 : {
212 0 : log_error(LOG_LEVEL_CONNECT,
213 : "No free slots found to remember socket for %s:%d. Last slot %d.",
214 : connection->host, connection->port, slot);
215 0 : privoxy_mutex_unlock(&connection_reuse_mutex);
216 0 : close_socket(connection->sfd);
217 0 : return;
218 : }
219 :
220 0 : assert(slot < SZ(reusable_connection));
221 0 : assert(NULL != connection->host);
222 0 : reusable_connection[slot].host = strdup_or_die(connection->host);
223 0 : reusable_connection[slot].sfd = connection->sfd;
224 0 : reusable_connection[slot].port = connection->port;
225 0 : reusable_connection[slot].in_use = 0;
226 0 : reusable_connection[slot].timestamp = connection->timestamp;
227 0 : reusable_connection[slot].request_sent = connection->request_sent;
228 0 : reusable_connection[slot].response_received = connection->response_received;
229 0 : reusable_connection[slot].keep_alive_timeout = connection->keep_alive_timeout;
230 0 : reusable_connection[slot].requests_sent_total = connection->requests_sent_total;
231 :
232 0 : assert(reusable_connection[slot].gateway_host == NULL);
233 0 : assert(reusable_connection[slot].gateway_port == 0);
234 0 : assert(reusable_connection[slot].auth_username == NULL);
235 0 : assert(reusable_connection[slot].auth_password == NULL);
236 0 : assert(reusable_connection[slot].forwarder_type == SOCKS_NONE);
237 0 : assert(reusable_connection[slot].forward_host == NULL);
238 0 : assert(reusable_connection[slot].forward_port == 0);
239 :
240 0 : reusable_connection[slot].forwarder_type = connection->forwarder_type;
241 0 : if (NULL != connection->gateway_host)
242 : {
243 0 : reusable_connection[slot].gateway_host = strdup_or_die(connection->gateway_host);
244 : }
245 : else
246 : {
247 0 : reusable_connection[slot].gateway_host = NULL;
248 : }
249 0 : reusable_connection[slot].gateway_port = connection->gateway_port;
250 0 : if (NULL != connection->auth_username)
251 : {
252 0 : reusable_connection[slot].auth_username = strdup_or_die(connection->auth_username);
253 : }
254 : else
255 : {
256 0 : reusable_connection[slot].auth_username = NULL;
257 : }
258 0 : if (NULL != connection->auth_password)
259 : {
260 0 : reusable_connection[slot].auth_password = strdup_or_die(connection->auth_password);
261 : }
262 : else
263 : {
264 0 : reusable_connection[slot].auth_password = NULL;
265 : }
266 :
267 0 : if (NULL != connection->forward_host)
268 : {
269 0 : reusable_connection[slot].forward_host = strdup_or_die(connection->forward_host);
270 : }
271 : else
272 : {
273 0 : reusable_connection[slot].forward_host = NULL;
274 : }
275 0 : reusable_connection[slot].forward_port = connection->forward_port;
276 :
277 0 : privoxy_mutex_unlock(&connection_reuse_mutex);
278 : }
279 : #endif /* def FEATURE_CONNECTION_SHARING */
280 :
281 :
282 : /*********************************************************************
283 : *
284 : * Function : mark_connection_closed
285 : *
286 : * Description : Marks a reused connection closed.
287 : *
288 : * Parameters :
289 : * 1 : closed_connection = The connection to mark as closed.
290 : *
291 : * Returns : void
292 : *
293 : *********************************************************************/
294 40550 : void mark_connection_closed(struct reusable_connection *closed_connection)
295 : {
296 40550 : closed_connection->in_use = FALSE;
297 40550 : closed_connection->sfd = JB_INVALID_SOCKET;
298 40550 : freez(closed_connection->host);
299 40550 : closed_connection->port = 0;
300 40550 : closed_connection->timestamp = 0;
301 40550 : closed_connection->request_sent = 0;
302 40550 : closed_connection->response_received = 0;
303 40550 : closed_connection->keep_alive_timeout = 0;
304 40550 : closed_connection->requests_sent_total = 0;
305 40550 : closed_connection->forwarder_type = SOCKS_NONE;
306 40550 : freez(closed_connection->gateway_host);
307 40550 : closed_connection->gateway_port = 0;
308 40550 : freez(closed_connection->auth_username);
309 40550 : freez(closed_connection->auth_password);
310 40550 : freez(closed_connection->forward_host);
311 40550 : closed_connection->forward_port = 0;
312 40550 : }
313 :
314 :
315 : #ifdef FEATURE_CONNECTION_SHARING
316 : /*********************************************************************
317 : *
318 : * Function : forget_connection
319 : *
320 : * Description : Removes a previously remembered connection from
321 : * the list of reusable connections.
322 : *
323 : * Parameters :
324 : * 1 : sfd = The socket belonging to the connection in question.
325 : *
326 : * Returns : void
327 : *
328 : *********************************************************************/
329 0 : void forget_connection(jb_socket sfd)
330 : {
331 0 : unsigned int slot = 0;
332 :
333 0 : assert(sfd != JB_INVALID_SOCKET);
334 :
335 0 : privoxy_mutex_lock(&connection_reuse_mutex);
336 :
337 0 : for (slot = 0; slot < SZ(reusable_connection); slot++)
338 : {
339 0 : if (reusable_connection[slot].sfd == sfd)
340 : {
341 0 : assert(reusable_connection[slot].in_use);
342 :
343 0 : log_error(LOG_LEVEL_CONNECT,
344 : "Forgetting socket %d for %s:%d in slot %d.",
345 : sfd, reusable_connection[slot].host,
346 : reusable_connection[slot].port, slot);
347 0 : mark_connection_closed(&reusable_connection[slot]);
348 0 : break;
349 : }
350 : }
351 :
352 0 : privoxy_mutex_unlock(&connection_reuse_mutex);
353 :
354 0 : }
355 : #endif /* def FEATURE_CONNECTION_SHARING */
356 :
357 :
358 : #ifdef FEATURE_CONNECTION_KEEP_ALIVE
359 : /*********************************************************************
360 : *
361 : * Function : string_or_none
362 : *
363 : * Description : Returns a given string or "none" if a NULL pointer
364 : * is given.
365 : * Helper function for connection_destination_matches().
366 : *
367 : * Parameters :
368 : * 1 : string = The string to check.
369 : *
370 : * Returns : The string if non-NULL, "none" otherwise.
371 : *
372 : *********************************************************************/
373 0 : static const char *string_or_none(const char *string)
374 : {
375 0 : return(string != NULL ? string : "none");
376 : }
377 :
378 :
379 : /*********************************************************************
380 : *
381 : * Function : connection_detail_matches
382 : *
383 : * Description : Helper function for connection_destination_matches().
384 : * Compares strings which can be NULL.
385 : *
386 : * Parameters :
387 : * 1 : connection_detail = The connection detail to compare.
388 : * 2 : fowarder_detail = The forwarder detail to compare.
389 : *
390 : * Returns : TRUE for yes, FALSE otherwise.
391 : *
392 : *********************************************************************/
393 652 : static int connection_detail_matches(const char *connection_detail,
394 : const char *forwarder_detail)
395 : {
396 652 : if (connection_detail == NULL && forwarder_detail == NULL)
397 : {
398 : /* Both details are unset. */
399 652 : return TRUE;
400 : }
401 :
402 0 : if ((connection_detail == NULL && forwarder_detail != NULL)
403 0 : || (connection_detail != NULL && forwarder_detail == NULL))
404 : {
405 : /* Only one detail isn't set. */
406 0 : return FALSE;
407 : }
408 :
409 : /* Both details are set, but do they match? */
410 0 : return(!strcmpic(connection_detail, forwarder_detail));
411 :
412 : }
413 :
414 :
415 : /*********************************************************************
416 : *
417 : * Function : connection_destination_matches
418 : *
419 : * Description : Determines whether a remembered connection can
420 : * be reused. That is, whether the destination and
421 : * the forwarding settings match.
422 : *
423 : * Parameters :
424 : * 1 : connection = The connection to check.
425 : * 2 : http = The destination for the connection.
426 : * 3 : fwd = The forwarder settings.
427 : *
428 : * Returns : TRUE for yes, FALSE otherwise.
429 : *
430 : *********************************************************************/
431 234 : int connection_destination_matches(const struct reusable_connection *connection,
432 : const struct http_request *http,
433 : const struct forward_spec *fwd)
434 : {
435 234 : if ((connection->forwarder_type != fwd->type)
436 234 : || (connection->gateway_port != fwd->gateway_port)
437 234 : || (connection->forward_port != fwd->forward_port)
438 234 : || (connection->port != http->port))
439 : {
440 71 : return FALSE;
441 : }
442 :
443 163 : if (!connection_detail_matches(connection->gateway_host, fwd->gateway_host))
444 : {
445 0 : log_error(LOG_LEVEL_CONNECT,
446 : "Gateway mismatch. Previous gateway: %s. Current gateway: %s",
447 0 : string_or_none(connection->gateway_host),
448 0 : string_or_none(fwd->gateway_host));
449 0 : return FALSE;
450 : }
451 :
452 163 : if (!connection_detail_matches(connection->auth_username, fwd->auth_username))
453 : {
454 0 : log_error(LOG_LEVEL_CONNECT, "Socks user name mismatch. "
455 : "Previous user name: %s. Current user name: %s",
456 0 : string_or_none(connection->auth_username),
457 0 : string_or_none(fwd->auth_username));
458 0 : return FALSE;
459 : }
460 :
461 163 : if (!connection_detail_matches(connection->auth_password, fwd->auth_password))
462 : {
463 0 : log_error(LOG_LEVEL_CONNECT, "Socks user name mismatch. "
464 : "Previous password: %s. Current password: %s",
465 0 : string_or_none(connection->auth_password),
466 0 : string_or_none(fwd->auth_password));
467 0 : return FALSE;
468 : }
469 :
470 163 : if (!connection_detail_matches(connection->forward_host, fwd->forward_host))
471 : {
472 0 : log_error(LOG_LEVEL_CONNECT,
473 : "Forwarding proxy mismatch. Previous proxy: %s. Current proxy: %s",
474 0 : string_or_none(connection->forward_host),
475 0 : string_or_none(fwd->forward_host));
476 0 : return FALSE;
477 : }
478 :
479 163 : return (!strcmpic(connection->host, http->host));
480 :
481 : }
482 : #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
483 :
484 :
485 : #ifdef FEATURE_CONNECTION_SHARING
486 : /*********************************************************************
487 : *
488 : * Function : close_unusable_connections
489 : *
490 : * Description : Closes remembered connections that have timed
491 : * out or have been closed on the other side.
492 : *
493 : * Parameters : none
494 : *
495 : * Returns : Number of connections that are still alive.
496 : *
497 : *********************************************************************/
498 0 : int close_unusable_connections(void)
499 : {
500 0 : unsigned int slot = 0;
501 0 : int connections_alive = 0;
502 : //JOSHreturn 1;
503 0 : privoxy_mutex_lock(&connection_reuse_mutex);
504 :
505 0 : for (slot = 0; slot < SZ(reusable_connection); slot++)
506 : {
507 0 : if (!reusable_connection[slot].in_use
508 0 : && (JB_INVALID_SOCKET != reusable_connection[slot].sfd))
509 : {
510 0 : time_t time_open = time(NULL) - reusable_connection[slot].timestamp;
511 0 : time_t latency = (reusable_connection[slot].response_received -
512 0 : reusable_connection[slot].request_sent) / 2;
513 :
514 0 : if (reusable_connection[slot].keep_alive_timeout < time_open + latency)
515 : {
516 0 : log_error(LOG_LEVEL_CONNECT,
517 : "The connection to %s:%d in slot %d timed out. "
518 : "Closing socket %d. Timeout is: %d. Assumed latency: %ld.",
519 : reusable_connection[slot].host,
520 : reusable_connection[slot].port, slot,
521 : reusable_connection[slot].sfd,
522 : reusable_connection[slot].keep_alive_timeout,
523 : latency);
524 0 : close_socket(reusable_connection[slot].sfd);
525 0 : mark_connection_closed(&reusable_connection[slot]);
526 : }
527 0 : else if (!socket_is_still_alive(reusable_connection[slot].sfd))
528 : {
529 0 : log_error(LOG_LEVEL_CONNECT,
530 : "The connection to %s:%d in slot %d is no longer usable. "
531 : "Closing socket %d.", reusable_connection[slot].host,
532 : reusable_connection[slot].port, slot,
533 : reusable_connection[slot].sfd);
534 0 : close_socket(reusable_connection[slot].sfd);
535 0 : mark_connection_closed(&reusable_connection[slot]);
536 : }
537 : else
538 : {
539 0 : connections_alive++;
540 : }
541 : }
542 : }
543 :
544 0 : privoxy_mutex_unlock(&connection_reuse_mutex);
545 :
546 0 : return connections_alive;
547 :
548 : }
549 :
550 :
551 : /*********************************************************************
552 : *
553 : * Function : get_reusable_connection
554 : *
555 : * Description : Returns an open socket to a previously remembered
556 : * open connection (if there is one).
557 : *
558 : * Parameters :
559 : * 1 : http = The destination for the connection.
560 : * 2 : fwd = The forwarder settings.
561 : *
562 : * Returns : JB_INVALID_SOCKET => No reusable connection found,
563 : * otherwise a usable socket.
564 : *
565 : *********************************************************************/
566 0 : static jb_socket get_reusable_connection(const struct http_request *http,
567 : const struct forward_spec *fwd)
568 : {
569 0 : jb_socket sfd = JB_INVALID_SOCKET;
570 0 : unsigned int slot = 0;
571 :
572 0 : close_unusable_connections();
573 :
574 0 : privoxy_mutex_lock(&connection_reuse_mutex);
575 :
576 0 : for (slot = 0; slot < SZ(reusable_connection); slot++)
577 : {
578 0 : if (!reusable_connection[slot].in_use
579 0 : && (JB_INVALID_SOCKET != reusable_connection[slot].sfd))
580 : {
581 0 : if (connection_destination_matches(&reusable_connection[slot], http, fwd))
582 : {
583 0 : reusable_connection[slot].in_use = TRUE;
584 0 : sfd = reusable_connection[slot].sfd;
585 0 : log_error(LOG_LEVEL_CONNECT,
586 : "Found reusable socket %d for %s:%d in slot %d. Timestamp made %ld "
587 : "seconds ago. Timeout: %d. Latency: %d. Requests served: %d",
588 : sfd, reusable_connection[slot].host, reusable_connection[slot].port,
589 0 : slot, time(NULL) - reusable_connection[slot].timestamp,
590 : reusable_connection[slot].keep_alive_timeout,
591 0 : (int)(reusable_connection[slot].response_received -
592 0 : reusable_connection[slot].request_sent),
593 : reusable_connection[slot].requests_sent_total);
594 0 : break;
595 : }
596 : }
597 : }
598 :
599 0 : privoxy_mutex_unlock(&connection_reuse_mutex);
600 :
601 0 : return sfd;
602 :
603 : }
604 :
605 :
606 : /*********************************************************************
607 : *
608 : * Function : mark_connection_unused
609 : *
610 : * Description : Gives a remembered connection free for reuse.
611 : *
612 : * Parameters :
613 : * 1 : connection = The connection in question.
614 : *
615 : * Returns : TRUE => Socket found and marked as unused.
616 : * FALSE => Socket not found.
617 : *
618 : *********************************************************************/
619 0 : static int mark_connection_unused(const struct reusable_connection *connection)
620 : {
621 0 : unsigned int slot = 0;
622 0 : int socket_found = FALSE;
623 :
624 0 : assert(connection->sfd != JB_INVALID_SOCKET);
625 :
626 0 : privoxy_mutex_lock(&connection_reuse_mutex);
627 :
628 0 : for (slot = 0; slot < SZ(reusable_connection); slot++)
629 : {
630 0 : if (reusable_connection[slot].sfd == connection->sfd)
631 : {
632 0 : assert(reusable_connection[slot].in_use);
633 0 : socket_found = TRUE;
634 0 : log_error(LOG_LEVEL_CONNECT,
635 : "Marking open socket %d for %s:%d in slot %d as unused.",
636 : connection->sfd, reusable_connection[slot].host,
637 : reusable_connection[slot].port, slot);
638 0 : reusable_connection[slot].in_use = 0;
639 0 : reusable_connection[slot].timestamp = connection->timestamp;
640 0 : break;
641 : }
642 : }
643 :
644 0 : privoxy_mutex_unlock(&connection_reuse_mutex);
645 :
646 0 : return socket_found;
647 :
648 : }
649 : #endif /* def FEATURE_CONNECTION_SHARING */
650 :
651 :
652 : /*********************************************************************
653 : *
654 : * Function : forwarded_connect
655 : *
656 : * Description : Connect to a specified web server, possibly via
657 : * a HTTP proxy and/or a SOCKS proxy.
658 : *
659 : * Parameters :
660 : * 1 : fwd = the proxies to use when connecting.
661 : * 2 : http = the http request and apropos headers
662 : * 3 : csp = Current client state (buffers, headers, etc...)
663 : *
664 : * Returns : JB_INVALID_SOCKET => failure, else it is the socket file descriptor.
665 : *
666 : *********************************************************************/
667 6474 : jb_socket forwarded_connect(const struct forward_spec *fwd,
668 : struct http_request *http,
669 : struct client_state *csp)
670 : {
671 : const char *dest_host;
672 : int dest_port;
673 6474 : jb_socket sfd = JB_INVALID_SOCKET;
674 :
675 : #ifdef FEATURE_CONNECTION_SHARING
676 6474 : if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)
677 0 : && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED))
678 : {
679 0 : sfd = get_reusable_connection(http, fwd);
680 0 : if (JB_INVALID_SOCKET != sfd)
681 : {
682 0 : return sfd;
683 : }
684 : }
685 : #endif /* def FEATURE_CONNECTION_SHARING */
686 :
687 : /* Figure out if we need to connect to the web server or a HTTP proxy. */
688 6474 : if (fwd->forward_host)
689 : {
690 : /* HTTP proxy */
691 0 : dest_host = fwd->forward_host;
692 0 : dest_port = fwd->forward_port;
693 : }
694 : else
695 : {
696 : /* Web server */
697 6474 : dest_host = http->host;
698 6474 : dest_port = http->port;
699 : }
700 :
701 : /* Connect, maybe using a SOCKS proxy */
702 6474 : switch (fwd->type)
703 : {
704 6474 : case SOCKS_NONE:
705 : case FORWARD_WEBSERVER:
706 6474 : sfd = connect_to(dest_host, dest_port, csp);
707 6474 : break;
708 0 : case SOCKS_4:
709 : case SOCKS_4A:
710 0 : sfd = socks4_connect(fwd, dest_host, dest_port, csp);
711 0 : break;
712 0 : case SOCKS_5:
713 : case SOCKS_5T:
714 0 : sfd = socks5_connect(fwd, dest_host, dest_port, csp);
715 0 : break;
716 0 : default:
717 : /* Should never get here */
718 0 : log_error(LOG_LEVEL_FATAL,
719 0 : "Internal error in forwarded_connect(). Bad proxy type: %d", fwd->type);
720 : }
721 :
722 6474 : if (JB_INVALID_SOCKET != sfd)
723 : {
724 6474 : log_error(LOG_LEVEL_CONNECT,
725 : "Created new connection to %s:%d on socket %d.",
726 : http->host, http->port, sfd);
727 : }
728 :
729 6474 : return sfd;
730 :
731 : }
732 :
733 :
734 : #ifdef FUZZ
735 : /*********************************************************************
736 : *
737 : * Function : socks_fuzz
738 : *
739 : * Description : Wrapper around socks[45]_connect() used for fuzzing.
740 : *
741 : * Parameters :
742 : * 1 : csp = Current client state (buffers, headers, etc...)
743 : *
744 : * Returns : JB_ERR_OK or JB_ERR_PARSE
745 : *
746 : *********************************************************************/
747 0 : extern jb_err socks_fuzz(struct client_state *csp)
748 : {
749 : jb_socket socket;
750 : static struct forward_spec fwd;
751 0 : char target_host[] = "127.0.0.1";
752 0 : int target_port = 12345;
753 :
754 0 : fwd.gateway_host = strdup_or_die("127.0.0.1");
755 0 : fwd.gateway_port = 12345;
756 :
757 0 : fwd.type = SOCKS_4A;
758 0 : socket = socks4_connect(&fwd, target_host, target_port, csp);
759 :
760 0 : if (JB_INVALID_SOCKET != socket)
761 : {
762 0 : fwd.type = SOCKS_5;
763 0 : socket = socks5_connect(&fwd, target_host, target_port, csp);
764 : }
765 :
766 0 : if (JB_INVALID_SOCKET == socket)
767 : {
768 0 : log_error(LOG_LEVEL_ERROR, "%s", csp->error_message);
769 0 : return JB_ERR_PARSE;
770 : }
771 :
772 0 : log_error(LOG_LEVEL_INFO, "Input looks like an acceptable socks response");
773 :
774 0 : return JB_ERR_OK;
775 :
776 : }
777 : #endif
778 :
779 : /*********************************************************************
780 : *
781 : * Function : socks4_connect
782 : *
783 : * Description : Connect to the SOCKS server, and connect through
784 : * it to the specified server. This handles
785 : * all the SOCKS negotiation, and returns a file
786 : * descriptor for a socket which can be treated as a
787 : * normal (non-SOCKS) socket.
788 : *
789 : * Logged error messages are saved to csp->error_message
790 : * and later reused by error_response() for the CGI
791 : * message. strdup allocation failures are handled there.
792 : *
793 : * Parameters :
794 : * 1 : fwd = Specifies the SOCKS proxy to use.
795 : * 2 : target_host = The final server to connect to.
796 : * 3 : target_port = The final port to connect to.
797 : * 4 : csp = Current client state (buffers, headers, etc...)
798 : *
799 : * Returns : JB_INVALID_SOCKET => failure, else a socket file descriptor.
800 : *
801 : *********************************************************************/
802 0 : static jb_socket socks4_connect(const struct forward_spec *fwd,
803 : const char *target_host,
804 : int target_port,
805 : struct client_state *csp)
806 : {
807 : unsigned long web_server_addr;
808 : char buf[BUFFER_SIZE];
809 0 : struct socks_op *c = (struct socks_op *)buf;
810 0 : struct socks_reply *s = (struct socks_reply *)buf;
811 : size_t n;
812 : size_t csiz;
813 : jb_socket sfd;
814 0 : int err = 0;
815 0 : char *errstr = NULL;
816 :
817 0 : if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
818 : {
819 : /* XXX: Shouldn't the config file parser prevent this? */
820 0 : errstr = "NULL gateway host specified.";
821 0 : err = 1;
822 : }
823 :
824 0 : if (fwd->gateway_port <= 0)
825 : {
826 0 : errstr = "invalid gateway port specified.";
827 0 : err = 1;
828 : }
829 :
830 0 : if (err)
831 : {
832 0 : log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
833 0 : csp->error_message = strdup(errstr);
834 0 : errno = EINVAL;
835 0 : return(JB_INVALID_SOCKET);
836 : }
837 :
838 : /* build a socks request for connection to the web server */
839 :
840 0 : strlcpy(&(c->userid), socks_userid, sizeof(buf) - sizeof(struct socks_op));
841 :
842 0 : csiz = sizeof(*c) + sizeof(socks_userid) - sizeof(c->userid) - sizeof(c->padding);
843 :
844 0 : switch (fwd->type)
845 : {
846 0 : case SOCKS_4:
847 0 : web_server_addr = resolve_hostname_to_ip(target_host);
848 0 : if (web_server_addr == INADDR_NONE)
849 : {
850 0 : errstr = "could not resolve target host";
851 0 : log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s %s", errstr, target_host);
852 0 : err = 1;
853 : }
854 : else
855 : {
856 0 : web_server_addr = htonl(web_server_addr);
857 : }
858 0 : break;
859 0 : case SOCKS_4A:
860 0 : web_server_addr = 0x00000001;
861 0 : n = csiz + strlen(target_host) + 1;
862 0 : if (n > sizeof(buf))
863 : {
864 0 : errno = EINVAL;
865 0 : errstr = "buffer cbuf too small.";
866 0 : log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
867 0 : err = 1;
868 : }
869 : else
870 : {
871 0 : strlcpy(buf + csiz, target_host, sizeof(buf) - sizeof(struct socks_op) - csiz);
872 : /*
873 : * What we forward to the socks4a server should have the
874 : * size of socks_op, plus the length of the userid plus
875 : * its \0 byte (which we don't have to add because the
876 : * first byte of the userid is counted twice as it's also
877 : * part of sock_op) minus the padding bytes (which are part
878 : * of the userid as well), plus the length of the target_host
879 : * (which is stored csiz bytes after the beginning of the buffer),
880 : * plus another \0 byte.
881 : */
882 0 : assert(n == sizeof(struct socks_op) + strlen(&(c->userid)) - sizeof(c->padding) + strlen(buf + csiz) + 1);
883 0 : csiz = n;
884 : }
885 0 : break;
886 0 : default:
887 : /* Should never get here */
888 0 : log_error(LOG_LEVEL_FATAL,
889 : "socks4_connect: SOCKS4 impossible internal error - bad SOCKS type.");
890 : /* Not reached */
891 0 : return(JB_INVALID_SOCKET);
892 : }
893 :
894 0 : if (err)
895 : {
896 0 : csp->error_message = strdup(errstr);
897 0 : return(JB_INVALID_SOCKET);
898 : }
899 :
900 0 : c->vn = 4;
901 0 : c->cd = 1;
902 0 : c->dstport[0] = (unsigned char)((target_port >> 8 ) & 0xff);
903 0 : c->dstport[1] = (unsigned char)((target_port ) & 0xff);
904 0 : c->dstip[0] = (unsigned char)((web_server_addr >> 24) & 0xff);
905 0 : c->dstip[1] = (unsigned char)((web_server_addr >> 16) & 0xff);
906 0 : c->dstip[2] = (unsigned char)((web_server_addr >> 8) & 0xff);
907 0 : c->dstip[3] = (unsigned char)((web_server_addr ) & 0xff);
908 :
909 : #ifdef FUZZ
910 0 : sfd = 0;
911 : #else
912 : /* pass the request to the socks server */
913 : sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
914 :
915 : if (sfd == JB_INVALID_SOCKET)
916 : {
917 : /* The error an its reason have already been logged by connect_to() */
918 : return(JB_INVALID_SOCKET);
919 : }
920 : else if (write_socket(sfd, (char *)c, csiz))
921 : {
922 : errstr = "SOCKS4 negotiation write failed.";
923 : log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
924 : err = 1;
925 : close_socket(sfd);
926 : }
927 : else if (!data_is_available(sfd, csp->config->socket_timeout))
928 : {
929 : if (socket_is_still_alive(sfd))
930 : {
931 : errstr = "SOCKS4 negotiation timed out";
932 : }
933 : else
934 : {
935 : errstr = "SOCKS4 negotiation got aborted by the server";
936 : }
937 : log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
938 : err = 1;
939 : close_socket(sfd);
940 : }
941 : else
942 : #endif
943 0 : if (read_socket(sfd, buf, sizeof(buf)) != sizeof(*s))
944 : {
945 0 : errstr = "SOCKS4 negotiation read failed.";
946 0 : log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
947 0 : err = 1;
948 0 : close_socket(sfd);
949 : }
950 :
951 0 : if (err)
952 : {
953 0 : csp->error_message = strdup(errstr);
954 0 : return(JB_INVALID_SOCKET);
955 : }
956 :
957 0 : switch (s->cd)
958 : {
959 0 : case SOCKS4_REQUEST_GRANTED:
960 0 : return(sfd);
961 0 : case SOCKS4_REQUEST_REJECT:
962 0 : errstr = "SOCKS request rejected or failed.";
963 0 : errno = EINVAL;
964 0 : break;
965 0 : case SOCKS4_REQUEST_IDENT_FAILED:
966 0 : errstr = "SOCKS request rejected because "
967 : "SOCKS server cannot connect to identd on the client.";
968 0 : errno = EACCES;
969 0 : break;
970 0 : case SOCKS4_REQUEST_IDENT_CONFLICT:
971 0 : errstr = "SOCKS request rejected because "
972 : "the client program and identd report "
973 : "different user-ids.";
974 0 : errno = EACCES;
975 0 : break;
976 0 : default:
977 0 : errno = ENOENT;
978 0 : snprintf(buf, sizeof(buf),
979 0 : "SOCKS request rejected for reason code %d.", s->cd);
980 0 : errstr = buf;
981 : }
982 :
983 0 : log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
984 0 : csp->error_message = strdup(errstr);
985 0 : close_socket(sfd);
986 :
987 0 : return(JB_INVALID_SOCKET);
988 :
989 : }
990 :
991 : /*********************************************************************
992 : *
993 : * Function : translate_socks5_error
994 : *
995 : * Description : Translates a SOCKS errors to a string.
996 : *
997 : * Parameters :
998 : * 1 : socks_error = The error code to translate.
999 : *
1000 : * Returns : The string translation.
1001 : *
1002 : *********************************************************************/
1003 0 : static const char *translate_socks5_error(int socks_error)
1004 : {
1005 0 : switch (socks_error)
1006 : {
1007 : /* XXX: these should be more descriptive */
1008 0 : case SOCKS5_REQUEST_FAILED:
1009 0 : return "SOCKS5 request failed";
1010 0 : case SOCKS5_REQUEST_DENIED:
1011 0 : return "SOCKS5 request denied";
1012 0 : case SOCKS5_REQUEST_NETWORK_UNREACHABLE:
1013 0 : return "SOCKS5 network unreachable";
1014 0 : case SOCKS5_REQUEST_HOST_UNREACHABLE:
1015 0 : return "SOCKS5 destination host unreachable";
1016 0 : case SOCKS5_REQUEST_CONNECTION_REFUSED:
1017 0 : return "SOCKS5 connection refused";
1018 0 : case SOCKS5_REQUEST_TTL_EXPIRED:
1019 0 : return "SOCKS5 TTL expired";
1020 0 : case SOCKS5_REQUEST_PROTOCOL_ERROR:
1021 0 : return "SOCKS5 client protocol error";
1022 0 : case SOCKS5_REQUEST_BAD_ADDRESS_TYPE:
1023 0 : return "SOCKS5 domain names unsupported";
1024 0 : case SOCKS5_REQUEST_GRANTED:
1025 0 : return "everything's peachy";
1026 0 : default:
1027 0 : return "SOCKS5 negotiation protocol error";
1028 : }
1029 : }
1030 :
1031 :
1032 : /*********************************************************************
1033 : *
1034 : * Function : socks5_connect
1035 : *
1036 : * Description : Connect to the SOCKS server, and connect through
1037 : * it to the specified server. This handles
1038 : * all the SOCKS negotiation, and returns a file
1039 : * descriptor for a socket which can be treated as a
1040 : * normal (non-SOCKS) socket.
1041 : *
1042 : * Parameters :
1043 : * 1 : fwd = Specifies the SOCKS proxy to use.
1044 : * 2 : target_host = The final server to connect to.
1045 : * 3 : target_port = The final port to connect to.
1046 : * 4 : csp = Current client state (buffers, headers, etc...)
1047 : *
1048 : * Returns : JB_INVALID_SOCKET => failure, else a socket file descriptor.
1049 : *
1050 : *********************************************************************/
1051 0 : static jb_socket socks5_connect(const struct forward_spec *fwd,
1052 : const char *target_host,
1053 : int target_port,
1054 : struct client_state *csp)
1055 : {
1056 : #define SIZE_SOCKS5_REPLY_IPV4 10
1057 : #define SIZE_SOCKS5_REPLY_IPV6 22
1058 : #define SOCKS5_REPLY_DIFFERENCE (SIZE_SOCKS5_REPLY_IPV6 - SIZE_SOCKS5_REPLY_IPV4)
1059 0 : int err = 0;
1060 : char cbuf[300];
1061 : char sbuf[SIZE_SOCKS5_REPLY_IPV6];
1062 0 : size_t client_pos = 0;
1063 0 : int server_size = 0;
1064 0 : size_t hostlen = 0;
1065 : jb_socket sfd;
1066 0 : const char *errstr = NULL;
1067 :
1068 0 : assert(fwd->gateway_host);
1069 0 : if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
1070 : {
1071 0 : errstr = "NULL gateway host specified";
1072 0 : err = 1;
1073 : }
1074 :
1075 0 : if (fwd->gateway_port <= 0)
1076 : {
1077 : /*
1078 : * XXX: currently this can't happen because in
1079 : * case of invalid gateway ports we use the defaults.
1080 : * Of course we really shouldn't do that.
1081 : */
1082 0 : errstr = "invalid gateway port specified";
1083 0 : err = 1;
1084 : }
1085 :
1086 0 : hostlen = strlen(target_host);
1087 0 : if (hostlen > (size_t)255)
1088 : {
1089 0 : errstr = "target host name is longer than 255 characters";
1090 0 : err = 1;
1091 : }
1092 :
1093 0 : if ((fwd->type != SOCKS_5) && (fwd->type != SOCKS_5T))
1094 : {
1095 : /* Should never get here */
1096 0 : log_error(LOG_LEVEL_FATAL,
1097 : "SOCKS5 impossible internal error - bad SOCKS type");
1098 0 : err = 1;
1099 : }
1100 :
1101 0 : if (err)
1102 : {
1103 0 : errno = EINVAL;
1104 0 : assert(errstr != NULL);
1105 0 : log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1106 0 : csp->error_message = strdup(errstr);
1107 0 : return(JB_INVALID_SOCKET);
1108 : }
1109 :
1110 : #ifdef FUZZ
1111 0 : sfd = 0;
1112 0 : if (!err && read_socket(sfd, sbuf, 2) != 2)
1113 : #else
1114 : /* pass the request to the socks server */
1115 : sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
1116 :
1117 : if (sfd == JB_INVALID_SOCKET)
1118 : {
1119 : errstr = "socks5 server unreachable";
1120 : log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1121 : /* Free the generic error message provided by connect_to() */
1122 : freez(csp->error_message);
1123 : csp->error_message = strdup(errstr);
1124 : return(JB_INVALID_SOCKET);
1125 : }
1126 :
1127 : client_pos = 0;
1128 : cbuf[client_pos++] = '\x05'; /* Version */
1129 :
1130 : if (fwd->auth_username && fwd->auth_password)
1131 : {
1132 : cbuf[client_pos++] = '\x02'; /* Two authentication methods supported */
1133 : cbuf[client_pos++] = '\x02'; /* Username/password */
1134 : }
1135 : else
1136 : {
1137 : cbuf[client_pos++] = '\x01'; /* One authentication method supported */
1138 : }
1139 : cbuf[client_pos++] = '\x00'; /* The no authentication authentication method */
1140 :
1141 : if (write_socket(sfd, cbuf, client_pos))
1142 : {
1143 : errstr = "SOCKS5 negotiation write failed";
1144 : csp->error_message = strdup(errstr);
1145 : log_error(LOG_LEVEL_CONNECT, "%s", errstr);
1146 : close_socket(sfd);
1147 : return(JB_INVALID_SOCKET);
1148 : }
1149 : if (!data_is_available(sfd, csp->config->socket_timeout))
1150 : {
1151 : if (socket_is_still_alive(sfd))
1152 : {
1153 : errstr = "SOCKS5 negotiation timed out";
1154 : }
1155 : else
1156 : {
1157 : errstr = "SOCKS5 negotiation got aborted by the server";
1158 : }
1159 : err = 1;
1160 : }
1161 :
1162 : if (!err && read_socket(sfd, sbuf, sizeof(sbuf)) != 2)
1163 : #endif
1164 : {
1165 0 : errstr = "SOCKS5 negotiation read failed";
1166 0 : err = 1;
1167 : }
1168 :
1169 0 : if (!err && (sbuf[0] != '\x05'))
1170 : {
1171 0 : errstr = "SOCKS5 negotiation protocol version error";
1172 0 : err = 1;
1173 : }
1174 :
1175 0 : if (!err && (sbuf[1] == '\xff'))
1176 : {
1177 0 : errstr = "SOCKS5 authentication required";
1178 0 : err = 1;
1179 : }
1180 :
1181 0 : if (!err && (sbuf[1] == '\x02'))
1182 : {
1183 0 : if (fwd->auth_username && fwd->auth_password)
1184 0 : {
1185 : /* check cbuf overflow */
1186 0 : size_t auth_len = strlen(fwd->auth_username) + strlen(fwd->auth_password) + 3;
1187 0 : if (auth_len > sizeof(cbuf))
1188 : {
1189 0 : errstr = "SOCKS5 username and/or password too long";
1190 0 : err = 1;
1191 : }
1192 : }
1193 : else
1194 : {
1195 0 : errstr = "SOCKS5 server requested authentication while "
1196 : "no credentials are configured";
1197 0 : err = 1;
1198 : }
1199 0 : if (!err)
1200 : {
1201 0 : client_pos = 0;
1202 0 : cbuf[client_pos++] = '\x01'; /* Version */
1203 0 : cbuf[client_pos++] = (char)strlen(fwd->auth_username);
1204 :
1205 0 : memcpy(cbuf + client_pos, fwd->auth_username, strlen(fwd->auth_username));
1206 0 : client_pos += strlen(fwd->auth_username);
1207 0 : cbuf[client_pos++] = (char)strlen(fwd->auth_password);
1208 0 : memcpy(cbuf + client_pos, fwd->auth_password, strlen(fwd->auth_password));
1209 0 : client_pos += strlen(fwd->auth_password);
1210 :
1211 0 : if (write_socket(sfd, cbuf, client_pos))
1212 : {
1213 0 : errstr = "SOCKS5 negotiation auth write failed";
1214 0 : csp->error_message = strdup(errstr);
1215 0 : log_error(LOG_LEVEL_CONNECT, "%s", errstr);
1216 0 : close_socket(sfd);
1217 0 : return(JB_INVALID_SOCKET);
1218 : }
1219 :
1220 0 : if (read_socket(sfd, sbuf, sizeof(sbuf)) != 2)
1221 : {
1222 0 : errstr = "SOCKS5 negotiation auth read failed";
1223 0 : err = 1;
1224 : }
1225 : }
1226 :
1227 0 : if (!err && (sbuf[1] != '\x00'))
1228 : {
1229 0 : errstr = "SOCKS5 authentication failed";
1230 0 : err = 1;
1231 : }
1232 : }
1233 0 : else if (!err && (sbuf[1] != '\x00'))
1234 : {
1235 0 : errstr = "SOCKS5 negotiation protocol error";
1236 0 : err = 1;
1237 : }
1238 :
1239 0 : if (err)
1240 : {
1241 0 : assert(errstr != NULL);
1242 0 : log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1243 0 : csp->error_message = strdup(errstr);
1244 0 : close_socket(sfd);
1245 0 : errno = EINVAL;
1246 0 : return(JB_INVALID_SOCKET);
1247 : }
1248 :
1249 0 : client_pos = 0;
1250 0 : cbuf[client_pos++] = '\x05'; /* Version */
1251 0 : cbuf[client_pos++] = '\x01'; /* TCP connect */
1252 0 : cbuf[client_pos++] = '\x00'; /* Reserved, must be 0x00 */
1253 0 : cbuf[client_pos++] = '\x03'; /* Address is domain name */
1254 0 : cbuf[client_pos++] = (char)(hostlen & 0xffu);
1255 0 : assert(sizeof(cbuf) - client_pos > (size_t)255);
1256 : /* Using strncpy because we really want the nul byte padding. */
1257 0 : strncpy(cbuf + client_pos, target_host, sizeof(cbuf) - client_pos - 1);
1258 0 : client_pos += (hostlen & 0xffu);
1259 0 : cbuf[client_pos++] = (char)((target_port >> 8) & 0xff);
1260 0 : cbuf[client_pos++] = (char)((target_port ) & 0xff);
1261 :
1262 : #ifndef FUZZ
1263 : if (write_socket(sfd, cbuf, client_pos))
1264 : {
1265 : errstr = "SOCKS5 negotiation write failed";
1266 : csp->error_message = strdup(errstr);
1267 : log_error(LOG_LEVEL_CONNECT, "%s", errstr);
1268 : close_socket(sfd);
1269 : errno = EINVAL;
1270 : return(JB_INVALID_SOCKET);
1271 : }
1272 :
1273 : /*
1274 : * Optimistically send the HTTP request with the initial
1275 : * SOCKS request if the user enabled the use of Tor extensions,
1276 : * the CONNECT method isn't being used (in which case the client
1277 : * doesn't send data until it gets our 200 response) and the
1278 : * client request has actually been completely read already.
1279 : */
1280 : if ((fwd->type == SOCKS_5T) && (csp->http->ssl == 0)
1281 : && (csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ))
1282 : {
1283 : char *client_headers = list_to_text(csp->headers);
1284 : size_t header_length;
1285 :
1286 : if (client_headers == NULL)
1287 : {
1288 : log_error(LOG_LEVEL_FATAL, "Out of memory rebuilding client headers");
1289 : }
1290 : list_remove_all(csp->headers);
1291 : header_length= strlen(client_headers);
1292 :
1293 : log_error(LOG_LEVEL_CONNECT,
1294 : "Optimistically sending %lu bytes of client headers intended for %s",
1295 : header_length, csp->http->hostport);
1296 :
1297 : if (write_socket(sfd, client_headers, header_length))
1298 : {
1299 : log_error(LOG_LEVEL_CONNECT,
1300 : "optimistically writing header to: %s failed: %E", csp->http->hostport);
1301 : freez(client_headers);
1302 : return(JB_INVALID_SOCKET);
1303 : }
1304 : freez(client_headers);
1305 : if (csp->expected_client_content_length != 0)
1306 : {
1307 : unsigned long long buffered_request_bytes =
1308 : (unsigned long long)(csp->client_iob->eod - csp->client_iob->cur);
1309 : log_error(LOG_LEVEL_CONNECT,
1310 : "Optimistically sending %llu bytes of client body. Expected %llu",
1311 : csp->expected_client_content_length, buffered_request_bytes);
1312 : assert(csp->expected_client_content_length == buffered_request_bytes);
1313 : if (write_socket(sfd, csp->client_iob->cur, buffered_request_bytes))
1314 : {
1315 : log_error(LOG_LEVEL_CONNECT,
1316 : "optimistically writing %llu bytes of client body to: %s failed: %E",
1317 : buffered_request_bytes, csp->http->hostport);
1318 : return(JB_INVALID_SOCKET);
1319 : }
1320 : clear_iob(csp->client_iob);
1321 : }
1322 : }
1323 : #endif
1324 :
1325 0 : server_size = read_socket(sfd, sbuf, SIZE_SOCKS5_REPLY_IPV4);
1326 0 : if (server_size != SIZE_SOCKS5_REPLY_IPV4)
1327 : {
1328 0 : errstr = "SOCKS5 negotiation read failed";
1329 : }
1330 : else
1331 : {
1332 0 : if (sbuf[0] != '\x05')
1333 : {
1334 0 : errstr = "SOCKS5 negotiation protocol version error";
1335 : }
1336 0 : else if (sbuf[2] != '\x00')
1337 : {
1338 0 : errstr = "SOCKS5 negotiation protocol error";
1339 : }
1340 0 : else if (sbuf[1] != SOCKS5_REQUEST_GRANTED)
1341 : {
1342 0 : errstr = translate_socks5_error(sbuf[1]);
1343 : }
1344 : else
1345 : {
1346 0 : if (sbuf[3] == '\x04')
1347 : {
1348 : /*
1349 : * The address field contains an IPv6 address
1350 : * which means we didn't get the whole reply
1351 : * yet. Read and discard the rest of it to make
1352 : * sure it isn't treated as HTTP data later on.
1353 : */
1354 0 : server_size = read_socket(sfd, sbuf, SOCKS5_REPLY_DIFFERENCE);
1355 0 : if (server_size != SOCKS5_REPLY_DIFFERENCE)
1356 : {
1357 0 : errstr = "SOCKS5 negotiation read failed (IPv6 address)";
1358 : }
1359 : }
1360 0 : else if (sbuf[3] != '\x01')
1361 : {
1362 0 : errstr = "SOCKS5 reply contains unsupported address type";
1363 : }
1364 0 : if (errstr == NULL)
1365 : {
1366 0 : return(sfd);
1367 : }
1368 : }
1369 : }
1370 :
1371 0 : assert(errstr != NULL);
1372 0 : csp->error_message = strdup(errstr);
1373 0 : log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1374 0 : close_socket(sfd);
1375 0 : errno = EINVAL;
1376 :
1377 0 : return(JB_INVALID_SOCKET);
1378 :
1379 : }
1380 :
1381 : /*
1382 : Local Variables:
1383 : tab-width: 3
1384 : end:
1385 : */
|