From: Fabian Keil Date: Sun, 3 Feb 2008 13:46:15 +0000 (+0000) Subject: Add SOCKS5 support. Patch #1862863 by Eric M. Hopper with minor changes. X-Git-Tag: v_3_0_9~260 X-Git-Url: http://www.privoxy.org/gitweb/templates.html?a=commitdiff_plain;h=1a096731122473db56caec3c5170be99fcf8aa06;p=privoxy.git Add SOCKS5 support. Patch #1862863 by Eric M. Hopper with minor changes. --- diff --git a/gateway.c b/gateway.c index 5a0eac15..2eec9956 100644 --- a/gateway.c +++ b/gateway.c @@ -1,4 +1,4 @@ -const char gateway_rcs[] = "$Id: gateway.c,v 1.20 2007/05/14 10:23:48 fabiankeil Exp $"; +const char gateway_rcs[] = "$Id: gateway.c,v 1.21 2007/07/28 12:30:03 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/gateway.c,v $ @@ -34,6 +34,10 @@ const char gateway_rcs[] = "$Id: gateway.c,v 1.20 2007/05/14 10:23:48 fabiankeil * * Revisions : * $Log: gateway.c,v $ + * Revision 1.21 2007/07/28 12:30:03 fabiankeil + * Modified patch from Song Weijia (#1762559) to + * fix socks requests on big-endian platforms. + * * Revision 1.20 2007/05/14 10:23:48 fabiankeil * - Use strlcpy() instead of strcpy(). * - Use the same buffer for socks requests and socks responses. @@ -172,12 +176,27 @@ static jb_socket socks4_connect(const struct forward_spec * fwd, int target_port, struct client_state *csp); +static jb_socket socks5_connect(const struct forward_spec *fwd, + const char *target_host, + int target_port, + struct client_state *csp); + #define SOCKS_REQUEST_GRANTED 90 #define SOCKS_REQUEST_REJECT 91 #define SOCKS_REQUEST_IDENT_FAILED 92 #define SOCKS_REQUEST_IDENT_CONFLICT 93 +#define SOCKS5_REQUEST_GRANTED 0 +#define SOCKS5_REQUEST_FAILED 1 +#define SOCKS5_REQUEST_DENIED 2 +#define SOCKS5_REQUEST_NETWORK_UNREACHABLE 3 +#define SOCKS5_REQUEST_HOST_UNREACHABLE 4 +#define SOCKS5_REQUEST_CONNECTION_REFUSEDD 5 +#define SOCKS5_REQUEST_TTL_EXPIRED 6 +#define SOCKS5_REQUEST_PROTOCOL_ERROR 7 +#define SOCKS5_REQUEST_BAD_ADDRESS_TYPE 8 + /* structure of a socks client operation */ struct socks_op { unsigned char vn; /* socks version number */ @@ -246,6 +265,9 @@ jb_socket forwarded_connect(const struct forward_spec * fwd, case SOCKS_4A: return (socks4_connect(fwd, dest_host, dest_port, csp)); + case SOCKS_5: + return (socks5_connect(fwd, dest_host, dest_port, csp)); + default: /* Should never get here */ log_error(LOG_LEVEL_FATAL, "SOCKS4 impossible internal error - bad SOCKS type."); @@ -450,6 +472,202 @@ static jb_socket socks4_connect(const struct forward_spec * fwd, } +/********************************************************************* + * + * Function : socks5_connect + * + * Description : Connect to the SOCKS server, and connect through + * it to the specified server. This handles + * all the SOCKS negotiation, and returns a file + * descriptor for a socket which can be treated as a + * normal (non-SOCKS) socket. + * + * Parameters : + * 1 : fwd = Specifies the SOCKS proxy to use. + * 2 : target_host = The final server to connect to. + * 3 : target_port = The final port to connect to. + * 4 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_INVALID_SOCKET => failure, else a socket file descriptor. + * + *********************************************************************/ +static jb_socket socks5_connect(const struct forward_spec *fwd, + const char *target_host, + int target_port, + struct client_state *csp) +{ + int err = 0; + char cbuf[BUFFER_SIZE]; + char sbuf[BUFFER_SIZE]; + size_t client_pos = 0; + int server_size = 0; + size_t hostlen = 0; + jb_socket sfd; + + if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0')) + { + log_error(LOG_LEVEL_CONNECT, "socks5_connect: NULL gateway host specified"); + err = 1; + } + + if (fwd->gateway_port <= 0) + { + log_error(LOG_LEVEL_CONNECT, "socks5_connect: invalid gateway port specified"); + err = 1; + } + + hostlen = strlen(target_host); + if (hostlen > 255) + { + log_error(LOG_LEVEL_CONNECT, "socks5_connect: target host name is longer than 255 characters."); + err = 1; + } + + if (fwd->type != SOCKS_5) + { + /* Should never get here */ + log_error(LOG_LEVEL_FATAL, "SOCKS5 impossible internal error - bad SOCKS type."); + err = 1; + } + + if (err) + { + errno = EINVAL; + return(JB_INVALID_SOCKET); + } + + /* pass the request to the socks server */ + sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp); + + if (sfd == JB_INVALID_SOCKET) + { + return(JB_INVALID_SOCKET); + } + + client_pos = 0; + cbuf[client_pos++] = '\x05'; /* Version */ + cbuf[client_pos++] = '\x01'; /* One authentication method supported */ + cbuf[client_pos++] = '\x00'; /* The no authentication authentication method */ + + if (write_socket(sfd, cbuf, client_pos)) + { + log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation write failed..."); + close_socket(sfd); + return(JB_INVALID_SOCKET); + } + + if (read_socket(sfd, sbuf, sizeof(sbuf)) != 2) + { + log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation read failed..."); + err = 1; + } + + if (!err && (sbuf[0] != '\x05')) + { + log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation protocol version error"); + err = 1; + } + + if (!err && (sbuf[1] == '\xff')) + { + log_error(LOG_LEVEL_CONNECT, "SOCKS5 authentication required"); + err = 1; + } + + if (!err && (sbuf[1] != '\x00')) + { + log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation protocol error"); + err = 1; + } + + if (err) + { + close_socket(sfd); + errno = EINVAL; + return(JB_INVALID_SOCKET); + } + + client_pos = 0; + cbuf[client_pos++] = '\x05'; /* Version */ + cbuf[client_pos++] = '\x01'; /* TCP connect */ + cbuf[client_pos++] = '\x00'; /* Reserved, must be 0x00 */ + cbuf[client_pos++] = '\x03'; /* Address is domain name */ + cbuf[client_pos++] = (char)(hostlen & 0xffu); + strncpy(cbuf + client_pos, target_host, 0xffu); + client_pos += (hostlen & 0xffu); + cbuf[client_pos++] = (char)((target_port >> 8) & 0xffu); + cbuf[client_pos++] = (char)((target_port ) & 0xffu); + + if (write_socket(sfd, cbuf, client_pos)) + { + log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation write failed..."); + close_socket(sfd); + errno = EINVAL; + return(JB_INVALID_SOCKET); + } + + server_size = read_socket(sfd, sbuf, sizeof(sbuf)); + if (server_size < 3) + { + log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation read failed..."); + err = 1; + } + + if (!err && (sbuf[0] != '\x05')) + { + log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation protocol version error"); + err = 1; + } + + if (!err && (sbuf[2] != '\x00')) + { + log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation protocol error"); + err = 1; + } + + if (!err) + { + switch (sbuf[1]) + { + case SOCKS5_REQUEST_GRANTED: + return(sfd); + break; + case SOCKS5_REQUEST_FAILED: + log_error(LOG_LEVEL_CONNECT, "SOCKS5 request failed"); + break; + case SOCKS5_REQUEST_DENIED: + log_error(LOG_LEVEL_CONNECT, "SOCKS5 request denied"); + break; + case SOCKS5_REQUEST_NETWORK_UNREACHABLE: + log_error(LOG_LEVEL_CONNECT, "SOCKS5 request - network unreachable"); + break; + case SOCKS5_REQUEST_HOST_UNREACHABLE: + log_error(LOG_LEVEL_CONNECT, "SOCKS5 request - host unreachable"); + break; + case SOCKS5_REQUEST_CONNECTION_REFUSEDD: + log_error(LOG_LEVEL_CONNECT, "SOCKS5 request - connection refused"); + break; + case SOCKS5_REQUEST_TTL_EXPIRED: + log_error(LOG_LEVEL_CONNECT, "SOCKS5 request - TTL expired"); + break; + case SOCKS5_REQUEST_PROTOCOL_ERROR: + log_error(LOG_LEVEL_CONNECT, "SOCKS5 request - client protocol error"); + break; + case SOCKS5_REQUEST_BAD_ADDRESS_TYPE: + log_error(LOG_LEVEL_CONNECT, "SOCKS5 request - domain names unsupported"); + break; + default: + log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation protocol error"); + break; + } + err = 1; + } + + close_socket(sfd); + errno = EINVAL; + return(JB_INVALID_SOCKET); +} + /* Local Variables: tab-width: 3 diff --git a/loadcfg.c b/loadcfg.c index 672f0415..44446e42 100644 --- a/loadcfg.c +++ b/loadcfg.c @@ -1,4 +1,4 @@ -const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.70 2007/12/15 14:24:05 fabiankeil Exp $"; +const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.71 2007/12/23 15:24:56 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/loadcfg.c,v $ @@ -35,6 +35,10 @@ const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.70 2007/12/15 14:24:05 fabiankeil * * Revisions : * $Log: loadcfg.c,v $ + * Revision 1.71 2007/12/23 15:24:56 fabiankeil + * Reword "unrecognized directive" warning, use better + * mark up and add a
. Fixes parts of #1856559. + * * Revision 1.70 2007/12/15 14:24:05 fabiankeil * Plug memory leak if listen-address only specifies the port. * @@ -531,6 +535,7 @@ static struct file_list *current_configfile = NULL; #define hash_forward 2029845ul /* "forward" */ #define hash_forward_socks4 3963965521ul /* "forward-socks4" */ #define hash_forward_socks4a 2639958518ul /* "forward-socks4a" */ +#define hash_forward_socks5 3963965522ul /* "forward-socks5" */ #define hash_forwarded_connect_retries 101465292ul /* "forwarded-connect-retries" */ #define hash_jarfile 2046641ul /* "jarfile" */ #define hash_listen_address 1255650842ul /* "listen-address" */ @@ -770,6 +775,7 @@ struct configuration_spec * load_config(void) struct forward_spec *cur_fwd; int vec_count; char *vec[3]; + unsigned long directive_hash; strlcpy(tmp, buf, sizeof(tmp)); @@ -809,8 +815,8 @@ struct configuration_spec * load_config(void) /* Save the argument for show-proxy-args */ savearg(cmd, arg, config); - - switch( hash_string( cmd ) ) + directive_hash = hash_string(cmd); + switch (directive_hash) { /* ************************************************************************* * actionsfile actions-file-name @@ -1199,6 +1205,7 @@ struct configuration_spec * load_config(void) * forward-socks4a url-pattern socks-proxy[:port] (.|http-proxy[:port]) * *************************************************************************/ case hash_forward_socks4a: + case hash_forward_socks5: vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1); if (vec_count != 3) @@ -1220,7 +1227,14 @@ struct configuration_spec * load_config(void) continue; } - cur_fwd->type = SOCKS_4A; + if (directive_hash == hash_forward_socks4a) + { + cur_fwd->type = SOCKS_4A; + } + else + { + cur_fwd->type = SOCKS_5; + } /* Save the URL pattern */ if (create_url_spec(cur_fwd->url, vec[0])) diff --git a/project.h b/project.h index 9c24d57e..fb067e72 100644 --- a/project.h +++ b/project.h @@ -1,7 +1,7 @@ #ifndef PROJECT_H_INCLUDED #define PROJECT_H_INCLUDED /** Version string. */ -#define PROJECT_H_VERSION "$Id: project.h,v 1.100 2007/09/02 13:42:11 fabiankeil Exp $" +#define PROJECT_H_VERSION "$Id: project.h,v 1.101 2007/12/07 18:29:23 fabiankeil Exp $" /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/project.h,v $ @@ -37,6 +37,9 @@ * * Revisions : * $Log: project.h,v $ + * Revision 1.101 2007/12/07 18:29:23 fabiankeil + * Remove now-obsolete csp member x_forwarded. + * * Revision 1.100 2007/09/02 13:42:11 fabiankeil * - Allow port lists in url patterns. * - Ditch unused url_spec member pathlen. @@ -1471,6 +1474,7 @@ struct block_spec #define SOCKS_NONE 0 /**< Don't use a SOCKS server */ #define SOCKS_4 40 /**< original SOCKS 4 protocol */ #define SOCKS_4A 41 /**< as modified for hosts w/o external DNS */ +#define SOCKS_5 50 /**< as modified for hosts w/o external DNS */ /** @@ -1481,7 +1485,7 @@ struct forward_spec /** URL pattern that this forward_spec is for. */ struct url_spec url[1]; - /** Connection type. Must be SOCKS_NONE, SOCKS_4, or SOCKS_4A. */ + /** Connection type. Must be SOCKS_NONE, SOCKS_4, SOCKS_4A or SOCKS_5. */ int type; /** SOCKS server hostname. Only valid if "type" is SOCKS_4 or SOCKS_4A. */