X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=gateway.c;h=2eec9956e9f5f5b3345e867acf78220a471f5901;hp=5a0eac15eef06856915204c1deb5fc535e872d9b;hb=1a096731122473db56caec3c5170be99fcf8aa06;hpb=85edc9e4e4208e7008630c72bff56d9e30d40e49 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