1 const char gateway_rcs[] = "$Id: gateway.c,v 1.53 2009/05/16 13:27:20 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(jb_socket sfd);
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 : sfd = Open socket to remember.
176 * 2 : http = The destination for the connection.
177 * 3 : fwd = The forwarder settings used.
178 * 4 : timeout = Number of seconds after which the
179 * connection shouldn't be reused.
183 *********************************************************************/
184 void remember_connection(jb_socket sfd,
185 const struct http_request *http,
186 const struct forward_spec *fwd,
187 unsigned int timeout)
189 unsigned int slot = 0;
190 int free_slot_found = FALSE;
192 assert(sfd != JB_INVALID_SOCKET);
194 if (mark_connection_unused(sfd))
199 privoxy_mutex_lock(&connection_reuse_mutex);
201 /* Find free socket slot. */
202 for (slot = 0; slot < SZ(reusable_connection); slot++)
204 if (reusable_connection[slot].sfd == JB_INVALID_SOCKET)
206 assert(reusable_connection[slot].in_use == 0);
207 log_error(LOG_LEVEL_CONNECT,
208 "Remembering socket %d for %s:%d in slot %d.",
209 sfd, http->host, http->port, slot);
210 free_slot_found = TRUE;
215 if (!free_slot_found)
217 log_error(LOG_LEVEL_CONNECT,
218 "No free slots found to remembering socket for %s:%d. Last slot %d.",
219 http->host, http->port, slot);
220 privoxy_mutex_unlock(&connection_reuse_mutex);
225 assert(NULL != http->host);
226 reusable_connection[slot].host = strdup(http->host);
227 if (NULL == reusable_connection[slot].host)
229 log_error(LOG_LEVEL_FATAL, "Out of memory saving socket.");
231 reusable_connection[slot].sfd = sfd;
232 reusable_connection[slot].port = http->port;
233 reusable_connection[slot].in_use = 0;
234 reusable_connection[slot].timestamp = time(NULL);
235 reusable_connection[slot].keep_alive_timeout = timeout;
238 assert(reusable_connection[slot].gateway_host == NULL);
239 assert(reusable_connection[slot].gateway_port == 0);
240 assert(reusable_connection[slot].forwarder_type == SOCKS_NONE);
241 assert(reusable_connection[slot].forward_host == NULL);
242 assert(reusable_connection[slot].forward_port == 0);
244 reusable_connection[slot].forwarder_type = fwd->type;
245 if (NULL != fwd->gateway_host)
247 reusable_connection[slot].gateway_host = strdup(fwd->gateway_host);
248 if (NULL == reusable_connection[slot].gateway_host)
250 log_error(LOG_LEVEL_FATAL, "Out of memory saving gateway_host.");
255 reusable_connection[slot].gateway_host = NULL;
257 reusable_connection[slot].gateway_port = fwd->gateway_port;
259 if (NULL != fwd->forward_host)
261 reusable_connection[slot].forward_host = strdup(fwd->forward_host);
262 if (NULL == reusable_connection[slot].forward_host)
264 log_error(LOG_LEVEL_FATAL, "Out of memory saving forward_host.");
269 reusable_connection[slot].forward_host = NULL;
271 reusable_connection[slot].forward_port = fwd->forward_port;
273 privoxy_mutex_unlock(&connection_reuse_mutex);
277 /*********************************************************************
279 * Function : mark_connection_closed
281 * Description : Marks a reused connection closed.
284 * 1 : closed_connection = The connection to mark as closed.
288 *********************************************************************/
289 void mark_connection_closed(struct reusable_connection *closed_connection)
291 closed_connection->in_use = FALSE;
292 closed_connection->sfd = JB_INVALID_SOCKET;
293 freez(closed_connection->host);
294 closed_connection->port = 0;
295 closed_connection->timestamp = 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;
427 if (reusable_connection[slot].keep_alive_timeout < time_open)
429 log_error(LOG_LEVEL_CONNECT,
430 "The connection to %s:%d in slot %d timed out. "
431 "Closing socket %d. Timeout is: %d.",
432 reusable_connection[slot].host,
433 reusable_connection[slot].port, slot,
434 reusable_connection[slot].sfd,
435 reusable_connection[slot].keep_alive_timeout);
436 close_socket(reusable_connection[slot].sfd);
437 mark_connection_closed(&reusable_connection[slot]);
439 else if (!socket_is_still_usable(reusable_connection[slot].sfd))
441 log_error(LOG_LEVEL_CONNECT,
442 "The connection to %s:%d in slot %d is no longer usable. "
443 "Closing socket %d.", reusable_connection[slot].host,
444 reusable_connection[slot].port, slot,
445 reusable_connection[slot].sfd);
446 close_socket(reusable_connection[slot].sfd);
447 mark_connection_closed(&reusable_connection[slot]);
456 privoxy_mutex_unlock(&connection_reuse_mutex);
458 return connections_alive;
463 /*********************************************************************
465 * Function : get_reusable_connection
467 * Description : Returns an open socket to a previously remembered
468 * open connection (if there is one).
471 * 1 : http = The destination for the connection.
472 * 2 : fwd = The forwarder settings.
474 * Returns : JB_INVALID_SOCKET => No reusable connection found,
475 * otherwise a usable socket.
477 *********************************************************************/
478 static jb_socket get_reusable_connection(const struct http_request *http,
479 const struct forward_spec *fwd)
481 jb_socket sfd = JB_INVALID_SOCKET;
482 unsigned int slot = 0;
484 close_unusable_connections();
486 privoxy_mutex_lock(&connection_reuse_mutex);
488 for (slot = 0; slot < SZ(reusable_connection); slot++)
490 if (!reusable_connection[slot].in_use
491 && (JB_INVALID_SOCKET != reusable_connection[slot].sfd))
493 if (connection_destination_matches(&reusable_connection[slot], http, fwd))
495 reusable_connection[slot].in_use = TRUE;
496 sfd = reusable_connection[slot].sfd;
497 log_error(LOG_LEVEL_CONNECT,
498 "Found reusable socket %d for %s:%d in slot %d.",
499 sfd, reusable_connection[slot].host, reusable_connection[slot].port, slot);
505 privoxy_mutex_unlock(&connection_reuse_mutex);
512 /*********************************************************************
514 * Function : mark_connection_unused
516 * Description : Gives a remembered connection free for reuse.
519 * 1 : sfd = The socket belonging to the connection in question.
521 * Returns : TRUE => Socket found and marked as unused.
522 * FALSE => Socket not found.
524 *********************************************************************/
525 static int mark_connection_unused(jb_socket sfd)
527 unsigned int slot = 0;
528 int socket_found = FALSE;
530 assert(sfd != JB_INVALID_SOCKET);
532 privoxy_mutex_lock(&connection_reuse_mutex);
534 for (slot = 0; slot < SZ(reusable_connection); slot++)
536 if (reusable_connection[slot].sfd == sfd)
538 assert(reusable_connection[slot].in_use);
540 log_error(LOG_LEVEL_CONNECT,
541 "Marking open socket %d for %s:%d in slot %d as unused.",
542 sfd, reusable_connection[slot].host,
543 reusable_connection[slot].port, slot);
544 reusable_connection[slot].in_use = 0;
545 reusable_connection[slot].timestamp = time(NULL);
550 privoxy_mutex_unlock(&connection_reuse_mutex);
557 /*********************************************************************
559 * Function : set_keep_alive_timeout
561 * Description : Sets the timeout after which open
562 * connections will no longer be reused.
565 * 1 : timeout = The timeout in seconds.
569 *********************************************************************/
570 void set_keep_alive_timeout(unsigned int timeout)
572 keep_alive_timeout = timeout;
574 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
577 /*********************************************************************
579 * Function : forwarded_connect
581 * Description : Connect to a specified web server, possibly via
582 * a HTTP proxy and/or a SOCKS proxy.
585 * 1 : fwd = the proxies to use when connecting.
586 * 2 : http = the http request and apropos headers
587 * 3 : csp = Current client state (buffers, headers, etc...)
589 * Returns : JB_INVALID_SOCKET => failure, else it is the socket file descriptor.
591 *********************************************************************/
592 jb_socket forwarded_connect(const struct forward_spec * fwd,
593 struct http_request *http,
594 struct client_state *csp)
596 const char * dest_host;
598 jb_socket sfd = JB_INVALID_SOCKET;
600 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
601 if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING))
603 sfd = get_reusable_connection(http, fwd);
604 if (JB_INVALID_SOCKET != sfd)
609 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
611 /* Figure out if we need to connect to the web server or a HTTP proxy. */
612 if (fwd->forward_host)
615 dest_host = fwd->forward_host;
616 dest_port = fwd->forward_port;
621 dest_host = http->host;
622 dest_port = http->port;
625 /* Connect, maybe using a SOCKS proxy */
629 sfd = connect_to(dest_host, dest_port, csp);
633 sfd = socks4_connect(fwd, dest_host, dest_port, csp);
636 sfd = socks5_connect(fwd, dest_host, dest_port, csp);
639 /* Should never get here */
640 log_error(LOG_LEVEL_FATAL,
641 "SOCKS4 impossible internal error - bad SOCKS type.");
644 if (JB_INVALID_SOCKET != sfd)
646 log_error(LOG_LEVEL_CONNECT,
647 "Created new connection to %s:%d on socket %d.",
648 http->host, http->port, sfd);
656 /*********************************************************************
658 * Function : socks4_connect
660 * Description : Connect to the SOCKS server, and connect through
661 * it to the specified server. This handles
662 * all the SOCKS negotiation, and returns a file
663 * descriptor for a socket which can be treated as a
664 * normal (non-SOCKS) socket.
666 * Logged error messages are saved to csp->error_message
667 * and later reused by error_response() for the CGI
668 * message. strdup allocation failures are handled there.
671 * 1 : fwd = Specifies the SOCKS proxy to use.
672 * 2 : target_host = The final server to connect to.
673 * 3 : target_port = The final port to connect to.
674 * 4 : csp = Current client state (buffers, headers, etc...)
676 * Returns : JB_INVALID_SOCKET => failure, else a socket file descriptor.
678 *********************************************************************/
679 static jb_socket socks4_connect(const struct forward_spec * fwd,
680 const char * target_host,
682 struct client_state *csp)
684 unsigned int web_server_addr;
685 char buf[BUFFER_SIZE];
686 struct socks_op *c = (struct socks_op *)buf;
687 struct socks_reply *s = (struct socks_reply *)buf;
694 if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
696 /* XXX: Shouldn't the config file parser prevent this? */
697 errstr = "NULL gateway host specified.";
701 if (fwd->gateway_port <= 0)
703 errstr = "invalid gateway port specified.";
709 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
710 csp->error_message = strdup(errstr);
712 return(JB_INVALID_SOCKET);
715 /* build a socks request for connection to the web server */
717 strlcpy(&(c->userid), socks_userid, sizeof(buf) - sizeof(struct socks_op));
719 csiz = sizeof(*c) + sizeof(socks_userid) - sizeof(c->userid) - sizeof(c->padding);
724 web_server_addr = resolve_hostname_to_ip(target_host);
725 if (web_server_addr == INADDR_NONE)
727 errstr = "could not resolve target host";
728 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s %s", errstr, target_host);
733 web_server_addr = htonl(web_server_addr);
737 web_server_addr = 0x00000001;
738 n = csiz + strlen(target_host) + 1;
742 errstr = "buffer cbuf too small.";
743 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
748 strlcpy(buf + csiz, target_host, sizeof(buf) - sizeof(struct socks_op) - csiz);
750 * What we forward to the socks4a server should have the
751 * size of socks_op, plus the length of the userid plus
752 * its \0 byte (which we don't have to add because the
753 * first byte of the userid is counted twice as it's also
754 * part of sock_op) minus the padding bytes (which are part
755 * of the userid as well), plus the length of the target_host
756 * (which is stored csiz bytes after the beginning of the buffer),
757 * plus another \0 byte.
759 assert(n == sizeof(struct socks_op) + strlen(&(c->userid)) - sizeof(c->padding) + strlen(buf + csiz) + 1);
764 /* Should never get here */
765 log_error(LOG_LEVEL_FATAL,
766 "socks4_connect: SOCKS4 impossible internal error - bad SOCKS type.");
768 return(JB_INVALID_SOCKET);
773 csp->error_message = strdup(errstr);
774 return(JB_INVALID_SOCKET);
779 c->dstport[0] = (unsigned char)((target_port >> 8 ) & 0xff);
780 c->dstport[1] = (unsigned char)((target_port ) & 0xff);
781 c->dstip[0] = (unsigned char)((web_server_addr >> 24 ) & 0xff);
782 c->dstip[1] = (unsigned char)((web_server_addr >> 16 ) & 0xff);
783 c->dstip[2] = (unsigned char)((web_server_addr >> 8 ) & 0xff);
784 c->dstip[3] = (unsigned char)((web_server_addr ) & 0xff);
786 /* pass the request to the socks server */
787 sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
789 if (sfd == JB_INVALID_SOCKET)
792 * XXX: connect_to should fill in the exact reason.
793 * Most likely resolving the IP of the forwarder failed.
795 errstr = "connect_to failed: see logfile for details";
798 else if (write_socket(sfd, (char *)c, csiz))
800 errstr = "SOCKS4 negotiation write failed.";
801 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
805 else if (read_socket(sfd, buf, sizeof(buf)) != sizeof(*s))
807 errstr = "SOCKS4 negotiation read failed.";
808 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
815 csp->error_message = strdup(errstr);
816 return(JB_INVALID_SOCKET);
821 case SOCKS_REQUEST_GRANTED:
823 case SOCKS_REQUEST_REJECT:
824 errstr = "SOCKS request rejected or failed.";
827 case SOCKS_REQUEST_IDENT_FAILED:
828 errstr = "SOCKS request rejected because "
829 "SOCKS server cannot connect to identd on the client.";
832 case SOCKS_REQUEST_IDENT_CONFLICT:
833 errstr = "SOCKS request rejected because "
834 "the client program and identd report "
835 "different user-ids.";
840 snprintf(buf, sizeof(buf),
841 "SOCKS request rejected for reason code %d.", s->cd);
845 log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
846 csp->error_message = strdup(errstr);
849 return(JB_INVALID_SOCKET);
853 /*********************************************************************
855 * Function : translate_socks5_error
857 * Description : Translates a SOCKS errors to a string.
860 * 1 : socks_error = The error code to translate.
862 * Returns : The string translation.
864 *********************************************************************/
865 static const char *translate_socks5_error(int socks_error)
869 /* XXX: these should be more descriptive */
870 case SOCKS5_REQUEST_FAILED:
871 return "SOCKS5 request failed";
872 case SOCKS5_REQUEST_DENIED:
873 return "SOCKS5 request denied";
874 case SOCKS5_REQUEST_NETWORK_UNREACHABLE:
875 return "SOCKS5 network unreachable";
876 case SOCKS5_REQUEST_HOST_UNREACHABLE:
877 return "SOCKS5 host unreachable";
878 case SOCKS5_REQUEST_CONNECTION_REFUSED:
879 return "SOCKS5 connection refused";
880 case SOCKS5_REQUEST_TTL_EXPIRED:
881 return "SOCKS5 TTL expired";
882 case SOCKS5_REQUEST_PROTOCOL_ERROR:
883 return "SOCKS5 client protocol error";
884 case SOCKS5_REQUEST_BAD_ADDRESS_TYPE:
885 return "SOCKS5 domain names unsupported";
886 case SOCKS5_REQUEST_GRANTED:
887 return "everything's peachy";
889 return "SOCKS5 negotiation protocol error";
893 /*********************************************************************
895 * Function : socks5_connect
897 * Description : Connect to the SOCKS server, and connect through
898 * it to the specified server. This handles
899 * all the SOCKS negotiation, and returns a file
900 * descriptor for a socket which can be treated as a
901 * normal (non-SOCKS) socket.
904 * 1 : fwd = Specifies the SOCKS proxy to use.
905 * 2 : target_host = The final server to connect to.
906 * 3 : target_port = The final port to connect to.
907 * 4 : csp = Current client state (buffers, headers, etc...)
909 * Returns : JB_INVALID_SOCKET => failure, else a socket file descriptor.
911 *********************************************************************/
912 static jb_socket socks5_connect(const struct forward_spec *fwd,
913 const char *target_host,
915 struct client_state *csp)
920 size_t client_pos = 0;
924 const char *errstr = NULL;
926 assert(fwd->gateway_host);
927 if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
929 errstr = "NULL gateway host specified";
933 if (fwd->gateway_port <= 0)
936 * XXX: currently this can't happen because in
937 * case of invalid gateway ports we use the defaults.
938 * Of course we really shouldn't do that.
940 errstr = "invalid gateway port specified";
944 hostlen = strlen(target_host);
945 if (hostlen > (size_t)255)
947 errstr = "target host name is longer than 255 characters";
951 if (fwd->type != SOCKS_5)
953 /* Should never get here */
954 log_error(LOG_LEVEL_FATAL,
955 "SOCKS5 impossible internal error - bad SOCKS type");
962 assert(errstr != NULL);
963 log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
964 csp->error_message = strdup(errstr);
965 return(JB_INVALID_SOCKET);
968 /* pass the request to the socks server */
969 sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
971 if (sfd == JB_INVALID_SOCKET)
973 errstr = "socks5 server unreachable";
974 log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
975 csp->error_message = strdup(errstr);
976 return(JB_INVALID_SOCKET);
980 cbuf[client_pos++] = '\x05'; /* Version */
981 cbuf[client_pos++] = '\x01'; /* One authentication method supported */
982 cbuf[client_pos++] = '\x00'; /* The no authentication authentication method */
984 if (write_socket(sfd, cbuf, client_pos))
986 errstr = "SOCKS5 negotiation write failed";
987 csp->error_message = strdup(errstr);
988 log_error(LOG_LEVEL_CONNECT, "%s", errstr);
990 return(JB_INVALID_SOCKET);
993 if (read_socket(sfd, sbuf, sizeof(sbuf)) != 2)
995 errstr = "SOCKS5 negotiation read failed";
999 if (!err && (sbuf[0] != '\x05'))
1001 errstr = "SOCKS5 negotiation protocol version error";
1005 if (!err && (sbuf[1] == '\xff'))
1007 errstr = "SOCKS5 authentication required";
1011 if (!err && (sbuf[1] != '\x00'))
1013 errstr = "SOCKS5 negotiation protocol error";
1019 assert(errstr != NULL);
1020 log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1021 csp->error_message = strdup(errstr);
1024 return(JB_INVALID_SOCKET);
1028 cbuf[client_pos++] = '\x05'; /* Version */
1029 cbuf[client_pos++] = '\x01'; /* TCP connect */
1030 cbuf[client_pos++] = '\x00'; /* Reserved, must be 0x00 */
1031 cbuf[client_pos++] = '\x03'; /* Address is domain name */
1032 cbuf[client_pos++] = (char)(hostlen & 0xffu);
1033 assert(sizeof(cbuf) - client_pos > (size_t)255);
1034 /* Using strncpy because we really want the nul byte padding. */
1035 strncpy(cbuf + client_pos, target_host, sizeof(cbuf) - client_pos);
1036 client_pos += (hostlen & 0xffu);
1037 cbuf[client_pos++] = (char)((target_port >> 8) & 0xff);
1038 cbuf[client_pos++] = (char)((target_port ) & 0xff);
1040 if (write_socket(sfd, cbuf, client_pos))
1042 errstr = "SOCKS5 negotiation read failed";
1043 csp->error_message = strdup(errstr);
1044 log_error(LOG_LEVEL_CONNECT, "%s", errstr);
1047 return(JB_INVALID_SOCKET);
1050 server_size = read_socket(sfd, sbuf, sizeof(sbuf));
1051 if (server_size < 3)
1053 errstr = "SOCKS5 negotiation read failed";
1056 else if (server_size > 20)
1058 /* This is somewhat unexpected but doesn't realy matter. */
1059 log_error(LOG_LEVEL_CONNECT, "socks5_connect: read %d bytes "
1060 "from socks server. Would have accepted up to %d.",
1061 server_size, sizeof(sbuf));
1064 if (!err && (sbuf[0] != '\x05'))
1066 errstr = "SOCKS5 negotiation protocol version error";
1070 if (!err && (sbuf[2] != '\x00'))
1072 errstr = "SOCKS5 negotiation protocol error";
1078 if (sbuf[1] == SOCKS5_REQUEST_GRANTED)
1082 errstr = translate_socks5_error(sbuf[1]);
1085 assert(errstr != NULL);
1086 csp->error_message = strdup(errstr);
1087 log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1091 return(JB_INVALID_SOCKET);