1 const char gateway_rcs[] = "$Id: gateway.c,v 1.58 2009/08/19 15:22:18 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 SourceForge
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_KEEP_ALIVE
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 connection for reuse later on.
175 * 1 : csp = Current client state (buffers, headers, etc...)
176 * 2 : fwd = The forwarder settings used.
180 *********************************************************************/
181 void remember_connection(const struct client_state *csp, const struct forward_spec *fwd)
183 unsigned int slot = 0;
184 int free_slot_found = FALSE;
185 const struct reusable_connection *connection = &csp->server_connection;
186 const struct http_request *http = csp->http;
188 assert(connection->sfd != JB_INVALID_SOCKET);
190 if (mark_connection_unused(connection))
195 privoxy_mutex_lock(&connection_reuse_mutex);
197 /* Find free socket slot. */
198 for (slot = 0; slot < SZ(reusable_connection); slot++)
200 if (reusable_connection[slot].sfd == JB_INVALID_SOCKET)
202 assert(reusable_connection[slot].in_use == 0);
203 log_error(LOG_LEVEL_CONNECT,
204 "Remembering socket %d for %s:%d in slot %d.",
205 connection->sfd, http->host, http->port, slot);
206 free_slot_found = TRUE;
211 if (!free_slot_found)
213 log_error(LOG_LEVEL_CONNECT,
214 "No free slots found to remembering socket for %s:%d. Last slot %d.",
215 http->host, http->port, slot);
216 privoxy_mutex_unlock(&connection_reuse_mutex);
217 close_socket(connection->sfd);
221 assert(NULL != http->host);
222 reusable_connection[slot].host = strdup(http->host);
223 if (NULL == reusable_connection[slot].host)
225 log_error(LOG_LEVEL_FATAL, "Out of memory saving socket.");
227 reusable_connection[slot].sfd = connection->sfd;
228 reusable_connection[slot].port = http->port;
229 reusable_connection[slot].in_use = 0;
230 reusable_connection[slot].timestamp = connection->timestamp;
231 reusable_connection->request_sent = connection->request_sent;
232 reusable_connection->response_received = connection->response_received;
233 reusable_connection[slot].keep_alive_timeout = connection->keep_alive_timeout;
236 assert(reusable_connection[slot].gateway_host == NULL);
237 assert(reusable_connection[slot].gateway_port == 0);
238 assert(reusable_connection[slot].forwarder_type == SOCKS_NONE);
239 assert(reusable_connection[slot].forward_host == NULL);
240 assert(reusable_connection[slot].forward_port == 0);
242 reusable_connection[slot].forwarder_type = fwd->type;
243 if (NULL != fwd->gateway_host)
245 reusable_connection[slot].gateway_host = strdup(fwd->gateway_host);
246 if (NULL == reusable_connection[slot].gateway_host)
248 log_error(LOG_LEVEL_FATAL, "Out of memory saving gateway_host.");
253 reusable_connection[slot].gateway_host = NULL;
255 reusable_connection[slot].gateway_port = fwd->gateway_port;
257 if (NULL != fwd->forward_host)
259 reusable_connection[slot].forward_host = strdup(fwd->forward_host);
260 if (NULL == reusable_connection[slot].forward_host)
262 log_error(LOG_LEVEL_FATAL, "Out of memory saving forward_host.");
267 reusable_connection[slot].forward_host = NULL;
269 reusable_connection[slot].forward_port = fwd->forward_port;
271 privoxy_mutex_unlock(&connection_reuse_mutex);
275 /*********************************************************************
277 * Function : mark_connection_closed
279 * Description : Marks a reused connection closed.
282 * 1 : closed_connection = The connection to mark as closed.
286 *********************************************************************/
287 void mark_connection_closed(struct reusable_connection *closed_connection)
289 closed_connection->in_use = FALSE;
290 closed_connection->sfd = JB_INVALID_SOCKET;
291 freez(closed_connection->host);
292 closed_connection->port = 0;
293 closed_connection->timestamp = 0;
294 closed_connection->request_sent = 0;
295 closed_connection->response_received = 0;
296 closed_connection->keep_alive_timeout = 0;
297 closed_connection->forwarder_type = SOCKS_NONE;
298 freez(closed_connection->gateway_host);
299 closed_connection->gateway_port = 0;
300 freez(closed_connection->forward_host);
301 closed_connection->forward_port = 0;
305 /*********************************************************************
307 * Function : forget_connection
309 * Description : Removes a previously remembered connection from
310 * the list of reusable connections.
313 * 1 : sfd = The socket belonging to the connection in question.
317 *********************************************************************/
318 void forget_connection(jb_socket sfd)
320 unsigned int slot = 0;
322 assert(sfd != JB_INVALID_SOCKET);
324 privoxy_mutex_lock(&connection_reuse_mutex);
326 for (slot = 0; slot < SZ(reusable_connection); slot++)
328 if (reusable_connection[slot].sfd == sfd)
330 assert(reusable_connection[slot].in_use);
332 log_error(LOG_LEVEL_CONNECT,
333 "Forgetting socket %d for %s:%d in slot %d.",
334 sfd, reusable_connection[slot].host,
335 reusable_connection[slot].port, slot);
336 mark_connection_closed(&reusable_connection[slot]);
337 privoxy_mutex_unlock(&connection_reuse_mutex);
343 log_error(LOG_LEVEL_CONNECT,
344 "Socket %d already forgotten or never remembered.", sfd);
346 privoxy_mutex_unlock(&connection_reuse_mutex);
350 /*********************************************************************
352 * Function : connection_destination_matches
354 * Description : Determines whether a remembered connection can
355 * be reused. That is, whether the destination and
356 * the forwarding settings match.
359 * 1 : connection = The connection to check.
360 * 2 : http = The destination for the connection.
361 * 3 : fwd = The forwarder settings.
363 * Returns : TRUE for yes, FALSE otherwise.
365 *********************************************************************/
366 int connection_destination_matches(const struct reusable_connection *connection,
367 const struct http_request *http,
368 const struct forward_spec *fwd)
370 if ((connection->forwarder_type != fwd->type)
371 || (connection->gateway_port != fwd->gateway_port)
372 || (connection->forward_port != fwd->forward_port)
373 || (connection->port != http->port))
378 if (( (NULL != connection->gateway_host)
379 && (NULL != fwd->gateway_host)
380 && strcmpic(connection->gateway_host, fwd->gateway_host))
381 && (connection->gateway_host != fwd->gateway_host))
383 log_error(LOG_LEVEL_CONNECT, "Gateway mismatch.");
387 if (( (NULL != connection->forward_host)
388 && (NULL != fwd->forward_host)
389 && strcmpic(connection->forward_host, fwd->forward_host))
390 && (connection->forward_host != fwd->forward_host))
392 log_error(LOG_LEVEL_CONNECT, "Forwarding proxy mismatch.");
396 return (!strcmpic(connection->host, http->host));
401 /*********************************************************************
403 * Function : close_unusable_connections
405 * Description : Closes remembered connections that have timed
406 * out or have been closed on the other side.
410 * Returns : Number of connections that are still alive.
412 *********************************************************************/
413 int close_unusable_connections(void)
415 unsigned int slot = 0;
416 int connections_alive = 0;
418 privoxy_mutex_lock(&connection_reuse_mutex);
420 for (slot = 0; slot < SZ(reusable_connection); slot++)
422 if (!reusable_connection[slot].in_use
423 && (JB_INVALID_SOCKET != reusable_connection[slot].sfd))
425 time_t time_open = time(NULL) - reusable_connection[slot].timestamp;
426 time_t latency = reusable_connection[slot].response_received -
427 reusable_connection[slot].request_sent;
429 if (reusable_connection[slot].keep_alive_timeout < time_open + latency)
431 log_error(LOG_LEVEL_CONNECT,
432 "The connection to %s:%d in slot %d timed out. "
433 "Closing socket %d. Timeout is: %d. Assumed latency: %d",
434 reusable_connection[slot].host,
435 reusable_connection[slot].port, slot,
436 reusable_connection[slot].sfd,
437 reusable_connection[slot].keep_alive_timeout,
439 close_socket(reusable_connection[slot].sfd);
440 mark_connection_closed(&reusable_connection[slot]);
442 else if (!socket_is_still_usable(reusable_connection[slot].sfd))
444 log_error(LOG_LEVEL_CONNECT,
445 "The connection to %s:%d in slot %d is no longer usable. "
446 "Closing socket %d.", reusable_connection[slot].host,
447 reusable_connection[slot].port, slot,
448 reusable_connection[slot].sfd);
449 close_socket(reusable_connection[slot].sfd);
450 mark_connection_closed(&reusable_connection[slot]);
459 privoxy_mutex_unlock(&connection_reuse_mutex);
461 return connections_alive;
466 /*********************************************************************
468 * Function : get_reusable_connection
470 * Description : Returns an open socket to a previously remembered
471 * open connection (if there is one).
474 * 1 : http = The destination for the connection.
475 * 2 : fwd = The forwarder settings.
477 * Returns : JB_INVALID_SOCKET => No reusable connection found,
478 * otherwise a usable socket.
480 *********************************************************************/
481 static jb_socket get_reusable_connection(const struct http_request *http,
482 const struct forward_spec *fwd)
484 jb_socket sfd = JB_INVALID_SOCKET;
485 unsigned int slot = 0;
487 close_unusable_connections();
489 privoxy_mutex_lock(&connection_reuse_mutex);
491 for (slot = 0; slot < SZ(reusable_connection); slot++)
493 if (!reusable_connection[slot].in_use
494 && (JB_INVALID_SOCKET != reusable_connection[slot].sfd))
496 if (connection_destination_matches(&reusable_connection[slot], http, fwd))
498 reusable_connection[slot].in_use = TRUE;
499 sfd = reusable_connection[slot].sfd;
500 log_error(LOG_LEVEL_CONNECT,
501 "Found reusable socket %d for %s:%d in slot %d."
502 "Timestamp made %d seconds ago. Timeout: %d. Latency: %d.",
503 sfd, reusable_connection[slot].host, reusable_connection[slot].port,
504 slot, time(NULL) - reusable_connection[slot].timestamp,
505 reusable_connection[slot].keep_alive_timeout,
506 (int)(reusable_connection[slot].response_received -
507 reusable_connection[slot].request_sent));
513 privoxy_mutex_unlock(&connection_reuse_mutex);
520 /*********************************************************************
522 * Function : mark_connection_unused
524 * Description : Gives a remembered connection free for reuse.
527 * 1 : connection = The connection in question.
529 * Returns : TRUE => Socket found and marked as unused.
530 * FALSE => Socket not found.
532 *********************************************************************/
533 static int mark_connection_unused(const struct reusable_connection *connection)
535 unsigned int slot = 0;
536 int socket_found = FALSE;
538 assert(connection->sfd != JB_INVALID_SOCKET);
540 privoxy_mutex_lock(&connection_reuse_mutex);
542 for (slot = 0; slot < SZ(reusable_connection); slot++)
544 if (reusable_connection[slot].sfd == connection->sfd)
546 assert(reusable_connection[slot].in_use);
548 log_error(LOG_LEVEL_CONNECT,
549 "Marking open socket %d for %s:%d in slot %d as unused.",
550 connection->sfd, reusable_connection[slot].host,
551 reusable_connection[slot].port, slot);
552 reusable_connection[slot].in_use = 0;
553 reusable_connection[slot].timestamp = connection->timestamp;
558 privoxy_mutex_unlock(&connection_reuse_mutex);
565 /*********************************************************************
567 * Function : set_keep_alive_timeout
569 * Description : Sets the timeout after which open
570 * connections will no longer be reused.
573 * 1 : timeout = The timeout in seconds.
577 *********************************************************************/
578 void set_keep_alive_timeout(unsigned int timeout)
580 keep_alive_timeout = timeout;
582 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
585 /*********************************************************************
587 * Function : forwarded_connect
589 * Description : Connect to a specified web server, possibly via
590 * a HTTP proxy and/or a SOCKS proxy.
593 * 1 : fwd = the proxies to use when connecting.
594 * 2 : http = the http request and apropos headers
595 * 3 : csp = Current client state (buffers, headers, etc...)
597 * Returns : JB_INVALID_SOCKET => failure, else it is the socket file descriptor.
599 *********************************************************************/
600 jb_socket forwarded_connect(const struct forward_spec * fwd,
601 struct http_request *http,
602 struct client_state *csp)
604 const char * dest_host;
606 jb_socket sfd = JB_INVALID_SOCKET;
608 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
609 if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)
610 && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED))
612 sfd = get_reusable_connection(http, fwd);
613 if (JB_INVALID_SOCKET != sfd)
618 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
620 /* Figure out if we need to connect to the web server or a HTTP proxy. */
621 if (fwd->forward_host)
624 dest_host = fwd->forward_host;
625 dest_port = fwd->forward_port;
630 dest_host = http->host;
631 dest_port = http->port;
634 /* Connect, maybe using a SOCKS proxy */
638 sfd = connect_to(dest_host, dest_port, csp);
642 sfd = socks4_connect(fwd, dest_host, dest_port, csp);
645 sfd = socks5_connect(fwd, dest_host, dest_port, csp);
648 /* Should never get here */
649 log_error(LOG_LEVEL_FATAL,
650 "SOCKS4 impossible internal error - bad SOCKS type.");
653 if (JB_INVALID_SOCKET != sfd)
655 log_error(LOG_LEVEL_CONNECT,
656 "Created new connection to %s:%d on socket %d.",
657 http->host, http->port, sfd);
665 /*********************************************************************
667 * Function : socks4_connect
669 * Description : Connect to the SOCKS server, and connect through
670 * it to the specified server. This handles
671 * all the SOCKS negotiation, and returns a file
672 * descriptor for a socket which can be treated as a
673 * normal (non-SOCKS) socket.
675 * Logged error messages are saved to csp->error_message
676 * and later reused by error_response() for the CGI
677 * message. strdup allocation failures are handled there.
680 * 1 : fwd = Specifies the SOCKS proxy to use.
681 * 2 : target_host = The final server to connect to.
682 * 3 : target_port = The final port to connect to.
683 * 4 : csp = Current client state (buffers, headers, etc...)
685 * Returns : JB_INVALID_SOCKET => failure, else a socket file descriptor.
687 *********************************************************************/
688 static jb_socket socks4_connect(const struct forward_spec * fwd,
689 const char * target_host,
691 struct client_state *csp)
693 unsigned long web_server_addr;
694 char buf[BUFFER_SIZE];
695 struct socks_op *c = (struct socks_op *)buf;
696 struct socks_reply *s = (struct socks_reply *)buf;
703 if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
705 /* XXX: Shouldn't the config file parser prevent this? */
706 errstr = "NULL gateway host specified.";
710 if (fwd->gateway_port <= 0)
712 errstr = "invalid gateway port specified.";
718 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
719 csp->error_message = strdup(errstr);
721 return(JB_INVALID_SOCKET);
724 /* build a socks request for connection to the web server */
726 strlcpy(&(c->userid), socks_userid, sizeof(buf) - sizeof(struct socks_op));
728 csiz = sizeof(*c) + sizeof(socks_userid) - sizeof(c->userid) - sizeof(c->padding);
733 web_server_addr = resolve_hostname_to_ip(target_host);
734 if (web_server_addr == INADDR_NONE)
736 errstr = "could not resolve target host";
737 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s %s", errstr, target_host);
742 web_server_addr = htonl(web_server_addr);
746 web_server_addr = 0x00000001;
747 n = csiz + strlen(target_host) + 1;
751 errstr = "buffer cbuf too small.";
752 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
757 strlcpy(buf + csiz, target_host, sizeof(buf) - sizeof(struct socks_op) - csiz);
759 * What we forward to the socks4a server should have the
760 * size of socks_op, plus the length of the userid plus
761 * its \0 byte (which we don't have to add because the
762 * first byte of the userid is counted twice as it's also
763 * part of sock_op) minus the padding bytes (which are part
764 * of the userid as well), plus the length of the target_host
765 * (which is stored csiz bytes after the beginning of the buffer),
766 * plus another \0 byte.
768 assert(n == sizeof(struct socks_op) + strlen(&(c->userid)) - sizeof(c->padding) + strlen(buf + csiz) + 1);
773 /* Should never get here */
774 log_error(LOG_LEVEL_FATAL,
775 "socks4_connect: SOCKS4 impossible internal error - bad SOCKS type.");
777 return(JB_INVALID_SOCKET);
782 csp->error_message = strdup(errstr);
783 return(JB_INVALID_SOCKET);
788 c->dstport[0] = (unsigned char)((target_port >> 8 ) & 0xff);
789 c->dstport[1] = (unsigned char)((target_port ) & 0xff);
790 c->dstip[0] = (unsigned char)((web_server_addr >> 24 ) & 0xff);
791 c->dstip[1] = (unsigned char)((web_server_addr >> 16 ) & 0xff);
792 c->dstip[2] = (unsigned char)((web_server_addr >> 8 ) & 0xff);
793 c->dstip[3] = (unsigned char)((web_server_addr ) & 0xff);
795 /* pass the request to the socks server */
796 sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
798 if (sfd == JB_INVALID_SOCKET)
801 * XXX: connect_to should fill in the exact reason.
802 * Most likely resolving the IP of the forwarder failed.
804 errstr = "connect_to failed: see logfile for details";
807 else if (write_socket(sfd, (char *)c, csiz))
809 errstr = "SOCKS4 negotiation write failed.";
810 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
814 else if (read_socket(sfd, buf, sizeof(buf)) != sizeof(*s))
816 errstr = "SOCKS4 negotiation read failed.";
817 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
824 csp->error_message = strdup(errstr);
825 return(JB_INVALID_SOCKET);
830 case SOCKS_REQUEST_GRANTED:
832 case SOCKS_REQUEST_REJECT:
833 errstr = "SOCKS request rejected or failed.";
836 case SOCKS_REQUEST_IDENT_FAILED:
837 errstr = "SOCKS request rejected because "
838 "SOCKS server cannot connect to identd on the client.";
841 case SOCKS_REQUEST_IDENT_CONFLICT:
842 errstr = "SOCKS request rejected because "
843 "the client program and identd report "
844 "different user-ids.";
849 snprintf(buf, sizeof(buf),
850 "SOCKS request rejected for reason code %d.", s->cd);
854 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
855 csp->error_message = strdup(errstr);
858 return(JB_INVALID_SOCKET);
862 /*********************************************************************
864 * Function : translate_socks5_error
866 * Description : Translates a SOCKS errors to a string.
869 * 1 : socks_error = The error code to translate.
871 * Returns : The string translation.
873 *********************************************************************/
874 static const char *translate_socks5_error(int socks_error)
878 /* XXX: these should be more descriptive */
879 case SOCKS5_REQUEST_FAILED:
880 return "SOCKS5 request failed";
881 case SOCKS5_REQUEST_DENIED:
882 return "SOCKS5 request denied";
883 case SOCKS5_REQUEST_NETWORK_UNREACHABLE:
884 return "SOCKS5 network unreachable";
885 case SOCKS5_REQUEST_HOST_UNREACHABLE:
886 return "SOCKS5 host unreachable";
887 case SOCKS5_REQUEST_CONNECTION_REFUSED:
888 return "SOCKS5 connection refused";
889 case SOCKS5_REQUEST_TTL_EXPIRED:
890 return "SOCKS5 TTL expired";
891 case SOCKS5_REQUEST_PROTOCOL_ERROR:
892 return "SOCKS5 client protocol error";
893 case SOCKS5_REQUEST_BAD_ADDRESS_TYPE:
894 return "SOCKS5 domain names unsupported";
895 case SOCKS5_REQUEST_GRANTED:
896 return "everything's peachy";
898 return "SOCKS5 negotiation protocol error";
902 /*********************************************************************
904 * Function : socks5_connect
906 * Description : Connect to the SOCKS server, and connect through
907 * it to the specified server. This handles
908 * all the SOCKS negotiation, and returns a file
909 * descriptor for a socket which can be treated as a
910 * normal (non-SOCKS) socket.
913 * 1 : fwd = Specifies the SOCKS proxy to use.
914 * 2 : target_host = The final server to connect to.
915 * 3 : target_port = The final port to connect to.
916 * 4 : csp = Current client state (buffers, headers, etc...)
918 * Returns : JB_INVALID_SOCKET => failure, else a socket file descriptor.
920 *********************************************************************/
921 static jb_socket socks5_connect(const struct forward_spec *fwd,
922 const char *target_host,
924 struct client_state *csp)
929 size_t client_pos = 0;
933 const char *errstr = NULL;
935 assert(fwd->gateway_host);
936 if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
938 errstr = "NULL gateway host specified";
942 if (fwd->gateway_port <= 0)
945 * XXX: currently this can't happen because in
946 * case of invalid gateway ports we use the defaults.
947 * Of course we really shouldn't do that.
949 errstr = "invalid gateway port specified";
953 hostlen = strlen(target_host);
954 if (hostlen > (size_t)255)
956 errstr = "target host name is longer than 255 characters";
960 if (fwd->type != SOCKS_5)
962 /* Should never get here */
963 log_error(LOG_LEVEL_FATAL,
964 "SOCKS5 impossible internal error - bad SOCKS type");
971 assert(errstr != NULL);
972 log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
973 csp->error_message = strdup(errstr);
974 return(JB_INVALID_SOCKET);
977 /* pass the request to the socks server */
978 sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
980 if (sfd == JB_INVALID_SOCKET)
982 errstr = "socks5 server unreachable";
983 log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
984 csp->error_message = strdup(errstr);
985 return(JB_INVALID_SOCKET);
989 cbuf[client_pos++] = '\x05'; /* Version */
990 cbuf[client_pos++] = '\x01'; /* One authentication method supported */
991 cbuf[client_pos++] = '\x00'; /* The no authentication authentication method */
993 if (write_socket(sfd, cbuf, client_pos))
995 errstr = "SOCKS5 negotiation write failed";
996 csp->error_message = strdup(errstr);
997 log_error(LOG_LEVEL_CONNECT, "%s", errstr);
999 return(JB_INVALID_SOCKET);
1002 if (read_socket(sfd, sbuf, sizeof(sbuf)) != 2)
1004 errstr = "SOCKS5 negotiation read failed";
1008 if (!err && (sbuf[0] != '\x05'))
1010 errstr = "SOCKS5 negotiation protocol version error";
1014 if (!err && (sbuf[1] == '\xff'))
1016 errstr = "SOCKS5 authentication required";
1020 if (!err && (sbuf[1] != '\x00'))
1022 errstr = "SOCKS5 negotiation protocol error";
1028 assert(errstr != NULL);
1029 log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1030 csp->error_message = strdup(errstr);
1033 return(JB_INVALID_SOCKET);
1037 cbuf[client_pos++] = '\x05'; /* Version */
1038 cbuf[client_pos++] = '\x01'; /* TCP connect */
1039 cbuf[client_pos++] = '\x00'; /* Reserved, must be 0x00 */
1040 cbuf[client_pos++] = '\x03'; /* Address is domain name */
1041 cbuf[client_pos++] = (char)(hostlen & 0xffu);
1042 assert(sizeof(cbuf) - client_pos > (size_t)255);
1043 /* Using strncpy because we really want the nul byte padding. */
1044 strncpy(cbuf + client_pos, target_host, sizeof(cbuf) - client_pos);
1045 client_pos += (hostlen & 0xffu);
1046 cbuf[client_pos++] = (char)((target_port >> 8) & 0xff);
1047 cbuf[client_pos++] = (char)((target_port ) & 0xff);
1049 if (write_socket(sfd, cbuf, client_pos))
1051 errstr = "SOCKS5 negotiation read failed";
1052 csp->error_message = strdup(errstr);
1053 log_error(LOG_LEVEL_CONNECT, "%s", errstr);
1056 return(JB_INVALID_SOCKET);
1059 server_size = read_socket(sfd, sbuf, sizeof(sbuf));
1060 if (server_size < 3)
1062 errstr = "SOCKS5 negotiation read failed";
1065 else if (server_size > 20)
1067 /* This is somewhat unexpected but doesn't realy matter. */
1068 log_error(LOG_LEVEL_CONNECT, "socks5_connect: read %d bytes "
1069 "from socks server. Would have accepted up to %d.",
1070 server_size, sizeof(sbuf));
1073 if (!err && (sbuf[0] != '\x05'))
1075 errstr = "SOCKS5 negotiation protocol version error";
1079 if (!err && (sbuf[2] != '\x00'))
1081 errstr = "SOCKS5 negotiation protocol error";
1087 if (sbuf[1] == SOCKS5_REQUEST_GRANTED)
1091 errstr = translate_socks5_error(sbuf[1]);
1094 assert(errstr != NULL);
1095 csp->error_message = strdup(errstr);
1096 log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1100 return(JB_INVALID_SOCKET);