1 const char gateway_rcs[] = "$Id: gateway.c,v 1.67 2011/01/09 12:08:52 fabiankeil Exp $";
2 /*********************************************************************
4 * File : $Source: /cvsroot/ijbswa/current/gateway.c,v $
6 * Purpose : Contains functions to connect to a server, possibly
7 * using a "forwarder" (i.e. HTTP proxy and/or a SOCKS4
10 * Copyright : Written by and Copyright (C) 2001-2009 the
11 * Privoxy team. http://www.privoxy.org/
13 * Based on the Internet Junkbuster originally written
14 * by and Copyright (C) 1997 Anonymous Coders and
15 * Junkbusters Corporation. http://www.junkbusters.com
17 * This program is free software; you can redistribute it
18 * and/or modify it under the terms of the GNU General
19 * Public License as published by the Free Software
20 * Foundation; either version 2 of the License, or (at
21 * your option) any later version.
23 * This program is distributed in the hope that it will
24 * be useful, but WITHOUT ANY WARRANTY; without even the
25 * implied warranty of MERCHANTABILITY or FITNESS FOR A
26 * PARTICULAR PURPOSE. See the GNU General Public
27 * License for more details.
29 * The GNU General Public License should be included with
30 * this file. If not, you can view it at
31 * http://www.gnu.org/copyleft/gpl.html
32 * or write to the Free Software Foundation, Inc., 59
33 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
35 *********************************************************************/
41 #include <sys/types.h>
44 #include <netinet/in.h>
53 #endif /* def _WIN32 */
57 #endif /* def __BEOS__ */
61 #endif /* def __OS2__ */
66 #include "jbsockets.h"
69 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
75 #endif /* def __GLIBC__ */
76 #endif /* HAVE_POLL */
77 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
79 const char gateway_h_rcs[] = GATEWAY_H_VERSION;
81 static jb_socket socks4_connect(const struct forward_spec * fwd,
82 const char * target_host,
84 struct client_state *csp);
86 static jb_socket socks5_connect(const struct forward_spec *fwd,
87 const char *target_host,
89 struct client_state *csp);
92 #define SOCKS_REQUEST_GRANTED 90
93 #define SOCKS_REQUEST_REJECT 91
94 #define SOCKS_REQUEST_IDENT_FAILED 92
95 #define SOCKS_REQUEST_IDENT_CONFLICT 93
97 #define SOCKS5_REQUEST_GRANTED 0
98 #define SOCKS5_REQUEST_FAILED 1
99 #define SOCKS5_REQUEST_DENIED 2
100 #define SOCKS5_REQUEST_NETWORK_UNREACHABLE 3
101 #define SOCKS5_REQUEST_HOST_UNREACHABLE 4
102 #define SOCKS5_REQUEST_CONNECTION_REFUSED 5
103 #define SOCKS5_REQUEST_TTL_EXPIRED 6
104 #define SOCKS5_REQUEST_PROTOCOL_ERROR 7
105 #define SOCKS5_REQUEST_BAD_ADDRESS_TYPE 8
107 /* structure of a socks client operation */
109 unsigned char vn; /* socks version number */
110 unsigned char cd; /* command code */
111 unsigned char dstport[2]; /* destination port */
112 unsigned char dstip[4]; /* destination address */
113 char userid; /* first byte of userid */
114 char padding[3]; /* make sure sizeof(struct socks_op) is endian-independent. */
115 /* more bytes of the userid follow, terminated by a NULL */
118 /* structure of a socks server reply */
120 unsigned char vn; /* socks version number */
121 unsigned char cd; /* command code */
122 unsigned char dstport[2]; /* destination port */
123 unsigned char dstip[4]; /* destination address */
126 static const char socks_userid[] = "anonymous";
128 #ifdef FEATURE_CONNECTION_SHARING
130 #define MAX_REUSABLE_CONNECTIONS 100
131 static unsigned int keep_alive_timeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
133 static struct reusable_connection reusable_connection[MAX_REUSABLE_CONNECTIONS];
134 static int mark_connection_unused(const struct reusable_connection *connection);
136 /*********************************************************************
138 * Function : initialize_reusable_connections
140 * Description : Initializes the reusable_connection structures.
141 * Must be called with connection_reuse_mutex locked.
147 *********************************************************************/
148 extern void initialize_reusable_connections(void)
150 unsigned int slot = 0;
152 #if !defined(HAVE_POLL) && !defined(_WIN32)
153 log_error(LOG_LEVEL_INFO,
154 "Detecting already dead connections might not work "
155 "correctly on your platform. In case of problems, "
156 "unset the keep-alive-timeout option.");
159 for (slot = 0; slot < SZ(reusable_connection); slot++)
161 mark_connection_closed(&reusable_connection[slot]);
164 log_error(LOG_LEVEL_CONNECT, "Initialized %d socket slots.", slot);
168 /*********************************************************************
170 * Function : remember_connection
172 * Description : Remembers a server connection for reuse later on.
175 * 1 : connection = The server connection to remember.
179 *********************************************************************/
180 void remember_connection(const struct reusable_connection *connection)
182 unsigned int slot = 0;
183 int free_slot_found = FALSE;
185 assert(NULL != connection);
186 assert(connection->sfd != JB_INVALID_SOCKET);
188 if (mark_connection_unused(connection))
193 privoxy_mutex_lock(&connection_reuse_mutex);
195 /* Find free socket slot. */
196 for (slot = 0; slot < SZ(reusable_connection); slot++)
198 if (reusable_connection[slot].sfd == JB_INVALID_SOCKET)
200 assert(reusable_connection[slot].in_use == 0);
201 log_error(LOG_LEVEL_CONNECT,
202 "Remembering socket %d for %s:%d in slot %d.",
203 connection->sfd, connection->host, connection->port, slot);
204 free_slot_found = TRUE;
209 if (!free_slot_found)
211 log_error(LOG_LEVEL_CONNECT,
212 "No free slots found to remembering socket for %s:%d. Last slot %d.",
213 connection->host, connection->port, slot);
214 privoxy_mutex_unlock(&connection_reuse_mutex);
215 close_socket(connection->sfd);
219 assert(NULL != connection->host);
220 reusable_connection[slot].host = strdup(connection->host);
221 if (NULL == reusable_connection[slot].host)
223 log_error(LOG_LEVEL_FATAL, "Out of memory saving socket.");
225 reusable_connection[slot].sfd = connection->sfd;
226 reusable_connection[slot].port = connection->port;
227 reusable_connection[slot].in_use = 0;
228 reusable_connection[slot].timestamp = connection->timestamp;
229 reusable_connection->request_sent = connection->request_sent;
230 reusable_connection->response_received = connection->response_received;
231 reusable_connection[slot].keep_alive_timeout = connection->keep_alive_timeout;
233 assert(reusable_connection[slot].gateway_host == NULL);
234 assert(reusable_connection[slot].gateway_port == 0);
235 assert(reusable_connection[slot].forwarder_type == SOCKS_NONE);
236 assert(reusable_connection[slot].forward_host == NULL);
237 assert(reusable_connection[slot].forward_port == 0);
239 reusable_connection[slot].forwarder_type = connection->forwarder_type;
240 if (NULL != connection->gateway_host)
242 reusable_connection[slot].gateway_host = strdup(connection->gateway_host);
243 if (NULL == reusable_connection[slot].gateway_host)
245 log_error(LOG_LEVEL_FATAL, "Out of memory saving gateway_host.");
250 reusable_connection[slot].gateway_host = NULL;
252 reusable_connection[slot].gateway_port = connection->gateway_port;
254 if (NULL != connection->forward_host)
256 reusable_connection[slot].forward_host = strdup(connection->forward_host);
257 if (NULL == reusable_connection[slot].forward_host)
259 log_error(LOG_LEVEL_FATAL, "Out of memory saving forward_host.");
264 reusable_connection[slot].forward_host = NULL;
266 reusable_connection[slot].forward_port = connection->forward_port;
268 privoxy_mutex_unlock(&connection_reuse_mutex);
270 #endif /* def FEATURE_CONNECTION_SHARING */
273 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
274 /*********************************************************************
276 * Function : mark_connection_closed
278 * Description : Marks a reused connection closed.
281 * 1 : closed_connection = The connection to mark as closed.
285 *********************************************************************/
286 void mark_connection_closed(struct reusable_connection *closed_connection)
288 closed_connection->in_use = FALSE;
289 closed_connection->sfd = JB_INVALID_SOCKET;
290 freez(closed_connection->host);
291 closed_connection->port = 0;
292 closed_connection->timestamp = 0;
293 closed_connection->request_sent = 0;
294 closed_connection->response_received = 0;
295 closed_connection->keep_alive_timeout = 0;
296 closed_connection->forwarder_type = SOCKS_NONE;
297 freez(closed_connection->gateway_host);
298 closed_connection->gateway_port = 0;
299 freez(closed_connection->forward_host);
300 closed_connection->forward_port = 0;
302 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
305 #ifdef FEATURE_CONNECTION_SHARING
306 /*********************************************************************
308 * Function : forget_connection
310 * Description : Removes a previously remembered connection from
311 * the list of reusable connections.
314 * 1 : sfd = The socket belonging to the connection in question.
318 *********************************************************************/
319 void forget_connection(jb_socket sfd)
321 unsigned int slot = 0;
323 assert(sfd != JB_INVALID_SOCKET);
325 privoxy_mutex_lock(&connection_reuse_mutex);
327 for (slot = 0; slot < SZ(reusable_connection); slot++)
329 if (reusable_connection[slot].sfd == sfd)
331 assert(reusable_connection[slot].in_use);
333 log_error(LOG_LEVEL_CONNECT,
334 "Forgetting socket %d for %s:%d in slot %d.",
335 sfd, reusable_connection[slot].host,
336 reusable_connection[slot].port, slot);
337 mark_connection_closed(&reusable_connection[slot]);
338 privoxy_mutex_unlock(&connection_reuse_mutex);
344 log_error(LOG_LEVEL_CONNECT,
345 "Socket %d already forgotten or never remembered.", sfd);
347 privoxy_mutex_unlock(&connection_reuse_mutex);
349 #endif /* def FEATURE_CONNECTION_SHARING */
352 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
353 /*********************************************************************
355 * Function : connection_destination_matches
357 * Description : Determines whether a remembered connection can
358 * be reused. That is, whether the destination and
359 * the forwarding settings match.
362 * 1 : connection = The connection to check.
363 * 2 : http = The destination for the connection.
364 * 3 : fwd = The forwarder settings.
366 * Returns : TRUE for yes, FALSE otherwise.
368 *********************************************************************/
369 int connection_destination_matches(const struct reusable_connection *connection,
370 const struct http_request *http,
371 const struct forward_spec *fwd)
373 if ((connection->forwarder_type != fwd->type)
374 || (connection->gateway_port != fwd->gateway_port)
375 || (connection->forward_port != fwd->forward_port)
376 || (connection->port != http->port))
381 if (( (NULL != connection->gateway_host)
382 && (NULL != fwd->gateway_host)
383 && strcmpic(connection->gateway_host, fwd->gateway_host))
384 && (connection->gateway_host != fwd->gateway_host))
386 log_error(LOG_LEVEL_CONNECT,
387 "Gateway mismatch. Previous gateway: %s. Current gateway: %s",
388 connection->gateway_host, fwd->gateway_host);
392 if (( (NULL != connection->forward_host)
393 && (NULL != fwd->forward_host)
394 && strcmpic(connection->forward_host, fwd->forward_host))
395 && (connection->forward_host != fwd->forward_host))
397 log_error(LOG_LEVEL_CONNECT,
398 "Forwarding proxy mismatch. Previous proxy: %s. Current proxy: %s",
399 connection->forward_host, fwd->forward_host);
403 return (!strcmpic(connection->host, http->host));
406 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
409 #ifdef FEATURE_CONNECTION_SHARING
410 /*********************************************************************
412 * Function : close_unusable_connections
414 * Description : Closes remembered connections that have timed
415 * out or have been closed on the other side.
419 * Returns : Number of connections that are still alive.
421 *********************************************************************/
422 int close_unusable_connections(void)
424 unsigned int slot = 0;
425 int connections_alive = 0;
427 privoxy_mutex_lock(&connection_reuse_mutex);
429 for (slot = 0; slot < SZ(reusable_connection); slot++)
431 if (!reusable_connection[slot].in_use
432 && (JB_INVALID_SOCKET != reusable_connection[slot].sfd))
434 time_t time_open = time(NULL) - reusable_connection[slot].timestamp;
435 time_t latency = (reusable_connection[slot].response_received -
436 reusable_connection[slot].request_sent) / 2;
438 if (reusable_connection[slot].keep_alive_timeout < time_open + latency)
440 log_error(LOG_LEVEL_CONNECT,
441 "The connection to %s:%d in slot %d timed out. "
442 "Closing socket %d. Timeout is: %d. Assumed latency: %d.",
443 reusable_connection[slot].host,
444 reusable_connection[slot].port, slot,
445 reusable_connection[slot].sfd,
446 reusable_connection[slot].keep_alive_timeout,
448 close_socket(reusable_connection[slot].sfd);
449 mark_connection_closed(&reusable_connection[slot]);
451 else if (!socket_is_still_alive(reusable_connection[slot].sfd))
453 log_error(LOG_LEVEL_CONNECT,
454 "The connection to %s:%d in slot %d is no longer usable. "
455 "Closing socket %d.", reusable_connection[slot].host,
456 reusable_connection[slot].port, slot,
457 reusable_connection[slot].sfd);
458 close_socket(reusable_connection[slot].sfd);
459 mark_connection_closed(&reusable_connection[slot]);
468 privoxy_mutex_unlock(&connection_reuse_mutex);
470 return connections_alive;
475 /*********************************************************************
477 * Function : get_reusable_connection
479 * Description : Returns an open socket to a previously remembered
480 * open connection (if there is one).
483 * 1 : http = The destination for the connection.
484 * 2 : fwd = The forwarder settings.
486 * Returns : JB_INVALID_SOCKET => No reusable connection found,
487 * otherwise a usable socket.
489 *********************************************************************/
490 static jb_socket get_reusable_connection(const struct http_request *http,
491 const struct forward_spec *fwd)
493 jb_socket sfd = JB_INVALID_SOCKET;
494 unsigned int slot = 0;
496 close_unusable_connections();
498 privoxy_mutex_lock(&connection_reuse_mutex);
500 for (slot = 0; slot < SZ(reusable_connection); slot++)
502 if (!reusable_connection[slot].in_use
503 && (JB_INVALID_SOCKET != reusable_connection[slot].sfd))
505 if (connection_destination_matches(&reusable_connection[slot], http, fwd))
507 reusable_connection[slot].in_use = TRUE;
508 sfd = reusable_connection[slot].sfd;
509 log_error(LOG_LEVEL_CONNECT,
510 "Found reusable socket %d for %s:%d in slot %d. "
511 "Timestamp made %d seconds ago. Timeout: %d. Latency: %d.",
512 sfd, reusable_connection[slot].host, reusable_connection[slot].port,
513 slot, time(NULL) - reusable_connection[slot].timestamp,
514 reusable_connection[slot].keep_alive_timeout,
515 (int)(reusable_connection[slot].response_received -
516 reusable_connection[slot].request_sent));
522 privoxy_mutex_unlock(&connection_reuse_mutex);
529 /*********************************************************************
531 * Function : mark_connection_unused
533 * Description : Gives a remembered connection free for reuse.
536 * 1 : connection = The connection in question.
538 * Returns : TRUE => Socket found and marked as unused.
539 * FALSE => Socket not found.
541 *********************************************************************/
542 static int mark_connection_unused(const struct reusable_connection *connection)
544 unsigned int slot = 0;
545 int socket_found = FALSE;
547 assert(connection->sfd != JB_INVALID_SOCKET);
549 privoxy_mutex_lock(&connection_reuse_mutex);
551 for (slot = 0; slot < SZ(reusable_connection); slot++)
553 if (reusable_connection[slot].sfd == connection->sfd)
555 assert(reusable_connection[slot].in_use);
557 log_error(LOG_LEVEL_CONNECT,
558 "Marking open socket %d for %s:%d in slot %d as unused.",
559 connection->sfd, reusable_connection[slot].host,
560 reusable_connection[slot].port, slot);
561 reusable_connection[slot].in_use = 0;
562 reusable_connection[slot].timestamp = connection->timestamp;
567 privoxy_mutex_unlock(&connection_reuse_mutex);
574 /*********************************************************************
576 * Function : set_keep_alive_timeout
578 * Description : Sets the timeout after which open
579 * connections will no longer be reused.
582 * 1 : timeout = The timeout in seconds.
586 *********************************************************************/
587 void set_keep_alive_timeout(unsigned int timeout)
589 keep_alive_timeout = timeout;
591 #endif /* def FEATURE_CONNECTION_SHARING */
594 /*********************************************************************
596 * Function : forwarded_connect
598 * Description : Connect to a specified web server, possibly via
599 * a HTTP proxy and/or a SOCKS proxy.
602 * 1 : fwd = the proxies to use when connecting.
603 * 2 : http = the http request and apropos headers
604 * 3 : csp = Current client state (buffers, headers, etc...)
606 * Returns : JB_INVALID_SOCKET => failure, else it is the socket file descriptor.
608 *********************************************************************/
609 jb_socket forwarded_connect(const struct forward_spec * fwd,
610 struct http_request *http,
611 struct client_state *csp)
613 const char * dest_host;
615 jb_socket sfd = JB_INVALID_SOCKET;
617 #ifdef FEATURE_CONNECTION_SHARING
618 if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)
619 && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED))
621 sfd = get_reusable_connection(http, fwd);
622 if (JB_INVALID_SOCKET != sfd)
627 #endif /* def FEATURE_CONNECTION_SHARING */
629 /* Figure out if we need to connect to the web server or a HTTP proxy. */
630 if (fwd->forward_host)
633 dest_host = fwd->forward_host;
634 dest_port = fwd->forward_port;
639 dest_host = http->host;
640 dest_port = http->port;
643 /* Connect, maybe using a SOCKS proxy */
647 sfd = connect_to(dest_host, dest_port, csp);
651 sfd = socks4_connect(fwd, dest_host, dest_port, csp);
654 sfd = socks5_connect(fwd, dest_host, dest_port, csp);
657 /* Should never get here */
658 log_error(LOG_LEVEL_FATAL,
659 "SOCKS4 impossible internal error - bad SOCKS type.");
662 if (JB_INVALID_SOCKET != sfd)
664 log_error(LOG_LEVEL_CONNECT,
665 "Created new connection to %s:%d on socket %d.",
666 http->host, http->port, sfd);
674 /*********************************************************************
676 * Function : socks4_connect
678 * Description : Connect to the SOCKS server, and connect through
679 * it to the specified server. This handles
680 * all the SOCKS negotiation, and returns a file
681 * descriptor for a socket which can be treated as a
682 * normal (non-SOCKS) socket.
684 * Logged error messages are saved to csp->error_message
685 * and later reused by error_response() for the CGI
686 * message. strdup allocation failures are handled there.
689 * 1 : fwd = Specifies the SOCKS proxy to use.
690 * 2 : target_host = The final server to connect to.
691 * 3 : target_port = The final port to connect to.
692 * 4 : csp = Current client state (buffers, headers, etc...)
694 * Returns : JB_INVALID_SOCKET => failure, else a socket file descriptor.
696 *********************************************************************/
697 static jb_socket socks4_connect(const struct forward_spec * fwd,
698 const char * target_host,
700 struct client_state *csp)
702 unsigned long web_server_addr;
703 char buf[BUFFER_SIZE];
704 struct socks_op *c = (struct socks_op *)buf;
705 struct socks_reply *s = (struct socks_reply *)buf;
712 if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
714 /* XXX: Shouldn't the config file parser prevent this? */
715 errstr = "NULL gateway host specified.";
719 if (fwd->gateway_port <= 0)
721 errstr = "invalid gateway port specified.";
727 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
728 csp->error_message = strdup(errstr);
730 return(JB_INVALID_SOCKET);
733 /* build a socks request for connection to the web server */
735 strlcpy(&(c->userid), socks_userid, sizeof(buf) - sizeof(struct socks_op));
737 csiz = sizeof(*c) + sizeof(socks_userid) - sizeof(c->userid) - sizeof(c->padding);
742 web_server_addr = resolve_hostname_to_ip(target_host);
743 if (web_server_addr == INADDR_NONE)
745 errstr = "could not resolve target host";
746 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s %s", errstr, target_host);
751 web_server_addr = htonl(web_server_addr);
755 web_server_addr = 0x00000001;
756 n = csiz + strlen(target_host) + 1;
760 errstr = "buffer cbuf too small.";
761 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
766 strlcpy(buf + csiz, target_host, sizeof(buf) - sizeof(struct socks_op) - csiz);
768 * What we forward to the socks4a server should have the
769 * size of socks_op, plus the length of the userid plus
770 * its \0 byte (which we don't have to add because the
771 * first byte of the userid is counted twice as it's also
772 * part of sock_op) minus the padding bytes (which are part
773 * of the userid as well), plus the length of the target_host
774 * (which is stored csiz bytes after the beginning of the buffer),
775 * plus another \0 byte.
777 assert(n == sizeof(struct socks_op) + strlen(&(c->userid)) - sizeof(c->padding) + strlen(buf + csiz) + 1);
782 /* Should never get here */
783 log_error(LOG_LEVEL_FATAL,
784 "socks4_connect: SOCKS4 impossible internal error - bad SOCKS type.");
786 return(JB_INVALID_SOCKET);
791 csp->error_message = strdup(errstr);
792 return(JB_INVALID_SOCKET);
797 c->dstport[0] = (unsigned char)((target_port >> 8 ) & 0xff);
798 c->dstport[1] = (unsigned char)((target_port ) & 0xff);
799 c->dstip[0] = (unsigned char)((web_server_addr >> 24 ) & 0xff);
800 c->dstip[1] = (unsigned char)((web_server_addr >> 16 ) & 0xff);
801 c->dstip[2] = (unsigned char)((web_server_addr >> 8 ) & 0xff);
802 c->dstip[3] = (unsigned char)((web_server_addr ) & 0xff);
804 /* pass the request to the socks server */
805 sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
807 if (sfd == JB_INVALID_SOCKET)
810 * XXX: connect_to should fill in the exact reason.
811 * Most likely resolving the IP of the forwarder failed.
813 errstr = "connect_to failed: see logfile for details";
816 else if (!data_is_available(sfd, csp->config->socket_timeout))
818 if (socket_is_still_alive(sfd))
820 errstr = "SOCKS4 negotiation timed out";
824 errstr = "SOCKS4 negotiation got aborted by the server";
826 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
830 else if (write_socket(sfd, (char *)c, csiz))
832 errstr = "SOCKS4 negotiation write failed.";
833 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
837 else if (read_socket(sfd, buf, sizeof(buf)) != sizeof(*s))
839 errstr = "SOCKS4 negotiation read failed.";
840 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
847 csp->error_message = strdup(errstr);
848 return(JB_INVALID_SOCKET);
853 case SOCKS_REQUEST_GRANTED:
855 case SOCKS_REQUEST_REJECT:
856 errstr = "SOCKS request rejected or failed.";
859 case SOCKS_REQUEST_IDENT_FAILED:
860 errstr = "SOCKS request rejected because "
861 "SOCKS server cannot connect to identd on the client.";
864 case SOCKS_REQUEST_IDENT_CONFLICT:
865 errstr = "SOCKS request rejected because "
866 "the client program and identd report "
867 "different user-ids.";
872 snprintf(buf, sizeof(buf),
873 "SOCKS request rejected for reason code %d.", s->cd);
877 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
878 csp->error_message = strdup(errstr);
881 return(JB_INVALID_SOCKET);
885 /*********************************************************************
887 * Function : translate_socks5_error
889 * Description : Translates a SOCKS errors to a string.
892 * 1 : socks_error = The error code to translate.
894 * Returns : The string translation.
896 *********************************************************************/
897 static const char *translate_socks5_error(int socks_error)
901 /* XXX: these should be more descriptive */
902 case SOCKS5_REQUEST_FAILED:
903 return "SOCKS5 request failed";
904 case SOCKS5_REQUEST_DENIED:
905 return "SOCKS5 request denied";
906 case SOCKS5_REQUEST_NETWORK_UNREACHABLE:
907 return "SOCKS5 network unreachable";
908 case SOCKS5_REQUEST_HOST_UNREACHABLE:
909 return "SOCKS5 host unreachable";
910 case SOCKS5_REQUEST_CONNECTION_REFUSED:
911 return "SOCKS5 connection refused";
912 case SOCKS5_REQUEST_TTL_EXPIRED:
913 return "SOCKS5 TTL expired";
914 case SOCKS5_REQUEST_PROTOCOL_ERROR:
915 return "SOCKS5 client protocol error";
916 case SOCKS5_REQUEST_BAD_ADDRESS_TYPE:
917 return "SOCKS5 domain names unsupported";
918 case SOCKS5_REQUEST_GRANTED:
919 return "everything's peachy";
921 return "SOCKS5 negotiation protocol error";
925 /*********************************************************************
927 * Function : socks5_connect
929 * Description : Connect to the SOCKS server, and connect through
930 * it to the specified server. This handles
931 * all the SOCKS negotiation, and returns a file
932 * descriptor for a socket which can be treated as a
933 * normal (non-SOCKS) socket.
936 * 1 : fwd = Specifies the SOCKS proxy to use.
937 * 2 : target_host = The final server to connect to.
938 * 3 : target_port = The final port to connect to.
939 * 4 : csp = Current client state (buffers, headers, etc...)
941 * Returns : JB_INVALID_SOCKET => failure, else a socket file descriptor.
943 *********************************************************************/
944 static jb_socket socks5_connect(const struct forward_spec *fwd,
945 const char *target_host,
947 struct client_state *csp)
952 size_t client_pos = 0;
956 const char *errstr = NULL;
958 assert(fwd->gateway_host);
959 if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
961 errstr = "NULL gateway host specified";
965 if (fwd->gateway_port <= 0)
968 * XXX: currently this can't happen because in
969 * case of invalid gateway ports we use the defaults.
970 * Of course we really shouldn't do that.
972 errstr = "invalid gateway port specified";
976 hostlen = strlen(target_host);
977 if (hostlen > (size_t)255)
979 errstr = "target host name is longer than 255 characters";
983 if (fwd->type != SOCKS_5)
985 /* Should never get here */
986 log_error(LOG_LEVEL_FATAL,
987 "SOCKS5 impossible internal error - bad SOCKS type");
994 assert(errstr != NULL);
995 log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
996 csp->error_message = strdup(errstr);
997 return(JB_INVALID_SOCKET);
1000 /* pass the request to the socks server */
1001 sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
1003 if (sfd == JB_INVALID_SOCKET)
1005 errstr = "socks5 server unreachable";
1006 log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1007 csp->error_message = strdup(errstr);
1008 return(JB_INVALID_SOCKET);
1012 cbuf[client_pos++] = '\x05'; /* Version */
1013 cbuf[client_pos++] = '\x01'; /* One authentication method supported */
1014 cbuf[client_pos++] = '\x00'; /* The no authentication authentication method */
1016 if (write_socket(sfd, cbuf, client_pos))
1018 errstr = "SOCKS5 negotiation write failed";
1019 csp->error_message = strdup(errstr);
1020 log_error(LOG_LEVEL_CONNECT, "%s", errstr);
1022 return(JB_INVALID_SOCKET);
1025 if (!data_is_available(sfd, csp->config->socket_timeout))
1027 if (socket_is_still_alive(sfd))
1029 errstr = "SOCKS5 negotiation timed out";
1033 errstr = "SOCKS5 negotiation got aborted by the server";
1038 if (!err && read_socket(sfd, sbuf, sizeof(sbuf)) != 2)
1040 errstr = "SOCKS5 negotiation read failed";
1044 if (!err && (sbuf[0] != '\x05'))
1046 errstr = "SOCKS5 negotiation protocol version error";
1050 if (!err && (sbuf[1] == '\xff'))
1052 errstr = "SOCKS5 authentication required";
1056 if (!err && (sbuf[1] != '\x00'))
1058 errstr = "SOCKS5 negotiation protocol error";
1064 assert(errstr != NULL);
1065 log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1066 csp->error_message = strdup(errstr);
1069 return(JB_INVALID_SOCKET);
1073 cbuf[client_pos++] = '\x05'; /* Version */
1074 cbuf[client_pos++] = '\x01'; /* TCP connect */
1075 cbuf[client_pos++] = '\x00'; /* Reserved, must be 0x00 */
1076 cbuf[client_pos++] = '\x03'; /* Address is domain name */
1077 cbuf[client_pos++] = (char)(hostlen & 0xffu);
1078 assert(sizeof(cbuf) - client_pos > (size_t)255);
1079 /* Using strncpy because we really want the nul byte padding. */
1080 strncpy(cbuf + client_pos, target_host, sizeof(cbuf) - client_pos);
1081 client_pos += (hostlen & 0xffu);
1082 cbuf[client_pos++] = (char)((target_port >> 8) & 0xff);
1083 cbuf[client_pos++] = (char)((target_port ) & 0xff);
1085 if (write_socket(sfd, cbuf, client_pos))
1087 errstr = "SOCKS5 negotiation read failed";
1088 csp->error_message = strdup(errstr);
1089 log_error(LOG_LEVEL_CONNECT, "%s", errstr);
1092 return(JB_INVALID_SOCKET);
1095 server_size = read_socket(sfd, sbuf, sizeof(sbuf));
1096 if (server_size < 3)
1098 errstr = "SOCKS5 negotiation read failed";
1101 else if (server_size > 20)
1103 /* This is somewhat unexpected but doesn't realy matter. */
1104 log_error(LOG_LEVEL_CONNECT, "socks5_connect: read %d bytes "
1105 "from socks server. Would have accepted up to %d.",
1106 server_size, sizeof(sbuf));
1109 if (!err && (sbuf[0] != '\x05'))
1111 errstr = "SOCKS5 negotiation protocol version error";
1115 if (!err && (sbuf[2] != '\x00'))
1117 errstr = "SOCKS5 negotiation protocol error";
1123 if (sbuf[1] == SOCKS5_REQUEST_GRANTED)
1127 errstr = translate_socks5_error(sbuf[1]);
1130 assert(errstr != NULL);
1131 csp->error_message = strdup(errstr);
1132 log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1136 return(JB_INVALID_SOCKET);