X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=gateway.c;h=3890b015c1946e5c7cdba9c4dcf67d2957a5fd71;hp=66ec64ae469816befaf80adf2e11269745079eeb;hb=a6df6730b85c3456f90915996bec385f157ba82d;hpb=900f06d038b3879e4e254b4517b0b6f6ffdb17e4 diff --git a/gateway.c b/gateway.c index 66ec64ae..3890b015 100644 --- a/gateway.c +++ b/gateway.c @@ -6,7 +6,7 @@ * using a "forwarder" (i.e. HTTP proxy and/or a SOCKS4 * or SOCKS5 proxy). * - * Copyright : Written by and Copyright (C) 2001-2017 the + * Copyright : Written by and Copyright (C) 2001-2021 the * Privoxy team. https://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -231,6 +231,8 @@ void remember_connection(const struct reusable_connection *connection) assert(reusable_connection[slot].gateway_host == NULL); assert(reusable_connection[slot].gateway_port == 0); + assert(reusable_connection[slot].auth_username == NULL); + assert(reusable_connection[slot].auth_password == NULL); assert(reusable_connection[slot].forwarder_type == SOCKS_NONE); assert(reusable_connection[slot].forward_host == NULL); assert(reusable_connection[slot].forward_port == 0); @@ -245,6 +247,22 @@ void remember_connection(const struct reusable_connection *connection) reusable_connection[slot].gateway_host = NULL; } reusable_connection[slot].gateway_port = connection->gateway_port; + if (NULL != connection->auth_username) + { + reusable_connection[slot].auth_username = strdup_or_die(connection->auth_username); + } + else + { + reusable_connection[slot].auth_username = NULL; + } + if (NULL != connection->auth_password) + { + reusable_connection[slot].auth_password = strdup_or_die(connection->auth_password); + } + else + { + reusable_connection[slot].auth_password = NULL; + } if (NULL != connection->forward_host) { @@ -338,6 +356,26 @@ void forget_connection(jb_socket sfd) #ifdef FEATURE_CONNECTION_KEEP_ALIVE +/********************************************************************* + * + * Function : string_or_none + * + * Description : Returns a given string or "none" if a NULL pointer + * is given. + * Helper function for connection_destination_matches(). + * + * Parameters : + * 1 : string = The string to check. + * + * Returns : The string if non-NULL, "none" otherwise. + * + *********************************************************************/ +static const char *string_or_none(const char *string) +{ + return(string != NULL ? string : "none"); +} + + /********************************************************************* * * Function : connection_detail_matches @@ -406,8 +444,8 @@ int connection_destination_matches(const struct reusable_connection *connection, { log_error(LOG_LEVEL_CONNECT, "Gateway mismatch. Previous gateway: %s. Current gateway: %s", - connection->gateway_host != NULL ? connection->gateway_host : "none", - fwd->gateway_host != NULL ? fwd->gateway_host : "none"); + string_or_none(connection->gateway_host), + string_or_none(fwd->gateway_host)); return FALSE; } @@ -415,8 +453,8 @@ int connection_destination_matches(const struct reusable_connection *connection, { log_error(LOG_LEVEL_CONNECT, "Socks user name mismatch. " "Previous user name: %s. Current user name: %s", - connection->auth_username != NULL ? connection->auth_username : "none", - fwd->auth_username != NULL ? fwd->auth_username : "none"); + string_or_none(connection->auth_username), + string_or_none(fwd->auth_username)); return FALSE; } @@ -424,8 +462,8 @@ int connection_destination_matches(const struct reusable_connection *connection, { log_error(LOG_LEVEL_CONNECT, "Socks user name mismatch. " "Previous password: %s. Current password: %s", - connection->auth_password != NULL ? connection->auth_password : "none", - fwd->auth_password != NULL ? fwd->auth_password : "none"); + string_or_none(connection->auth_password), + string_or_none(fwd->auth_password)); return FALSE; } @@ -433,8 +471,8 @@ int connection_destination_matches(const struct reusable_connection *connection, { log_error(LOG_LEVEL_CONNECT, "Forwarding proxy mismatch. Previous proxy: %s. Current proxy: %s", - connection->forward_host != NULL ? connection->forward_host : "none", - fwd->forward_host != NULL ? fwd->forward_host : "none"); + string_or_none(connection->forward_host), + string_or_none(fwd->forward_host)); return FALSE; } @@ -991,6 +1029,101 @@ static const char *translate_socks5_error(int socks_error) } +/********************************************************************* + * + * Function : convert_ipv4_address_to_bytes + * + * Description : Converts an IPv4 address from string to bytes. + * + * Parameters : + * 1 : address = The IPv4 address string to convert. + * 2 : buf = The buffer to write the bytes to. + * Must be at least four bytes long. + * + * Returns : JB_ERR_OK on success, JB_ERR_PARSE otherwise. + * + *********************************************************************/ +static jb_err convert_ipv4_address_to_bytes(const char *address, char *buf) +{ + int i; + const char *p = address; + + for (i = 0; i < 4; i++) + { + unsigned byte; + if (1 != sscanf(p, "%u", &byte)) + { + return JB_ERR_PARSE; + } + if (byte > 255) + { + return JB_ERR_PARSE; + } + buf[i] = (char)byte; + if (i < 3) + { + p = strstr(p, "."); + if (p == NULL) + { + return JB_ERR_PARSE; + } + p++; + } + } + + return JB_ERR_OK; + +} + + +/********************************************************************* + * + * Function : read_socks_reply + * + * Description : Read from a socket connected to a socks server. + * + * Parameters : + * 1 : sfd = file descriptor of the socket to read + * 2 : buf = pointer to buffer where data will be written + * Must be >= len bytes long. + * 3 : len = maximum number of bytes to read + * 4 : timeout = Number of seconds to wait. + * + * Returns : On success, the number of bytes read is returned (zero + * indicates end of file), and the file position is advanced + * by this number. It is not an error if this number is + * smaller than the number of bytes requested; this may hap- + * pen for example because fewer bytes are actually available + * right now (maybe because we were close to end-of-file, or + * because we are reading from a pipe, or from a terminal, + * or because read() was interrupted by a signal). On error, + * -1 is returned, and errno is set appropriately. In this + * case it is left unspecified whether the file position (if + * any) changes. + * + *********************************************************************/ +static int read_socks_reply(jb_socket sfd, char *buf, int len, int timeout) +{ + if (!data_is_available(sfd, timeout)) + { + if (socket_is_still_alive(sfd)) + { + log_error(LOG_LEVEL_ERROR, + "The socks connection timed out after %d seconds.", timeout); + } + else + { + log_error(LOG_LEVEL_ERROR, "The socks server hung " + "up the connection without sending a response."); + } + return -1; + } + + return read_socket(sfd, buf, len); + +} + + /********************************************************************* * * Function : socks5_connect @@ -1017,10 +1150,11 @@ static jb_socket socks5_connect(const struct forward_spec *fwd, { #define SIZE_SOCKS5_REPLY_IPV4 10 #define SIZE_SOCKS5_REPLY_IPV6 22 +#define SIZE_SOCKS5_REPLY_DOMAIN 300 #define SOCKS5_REPLY_DIFFERENCE (SIZE_SOCKS5_REPLY_IPV6 - SIZE_SOCKS5_REPLY_IPV4) int err = 0; char cbuf[300]; - char sbuf[SIZE_SOCKS5_REPLY_IPV6]; + char sbuf[SIZE_SOCKS5_REPLY_DOMAIN]; size_t client_pos = 0; int server_size = 0; size_t hostlen = 0; @@ -1108,20 +1242,8 @@ static jb_socket socks5_connect(const struct forward_spec *fwd, close_socket(sfd); return(JB_INVALID_SOCKET); } - if (!data_is_available(sfd, csp->config->socket_timeout)) - { - if (socket_is_still_alive(sfd)) - { - errstr = "SOCKS5 negotiation timed out"; - } - else - { - errstr = "SOCKS5 negotiation got aborted by the server"; - } - err = 1; - } - - if (!err && read_socket(sfd, sbuf, sizeof(sbuf)) != 2) + if (read_socks_reply(sfd, sbuf, sizeof(sbuf), + csp->config->socket_timeout) != 2) #endif { errstr = "SOCKS5 negotiation read failed"; @@ -1142,11 +1264,20 @@ static jb_socket socks5_connect(const struct forward_spec *fwd, if (!err && (sbuf[1] == '\x02')) { - /* check cbuf overflow */ - size_t auth_len = strlen(fwd->auth_username) + strlen(fwd->auth_password) + 3; - if (auth_len > sizeof(cbuf)) + if (fwd->auth_username && fwd->auth_password) { - errstr = "SOCKS5 username and/or password too long"; + /* check cbuf overflow */ + size_t auth_len = strlen(fwd->auth_username) + strlen(fwd->auth_password) + 3; + if (auth_len > sizeof(cbuf)) + { + errstr = "SOCKS5 username and/or password too long"; + err = 1; + } + } + else + { + errstr = "SOCKS5 server requested authentication while " + "no credentials are configured"; err = 1; } @@ -1171,7 +1302,8 @@ static jb_socket socks5_connect(const struct forward_spec *fwd, return(JB_INVALID_SOCKET); } - if (read_socket(sfd, sbuf, sizeof(sbuf)) != 2) + if (read_socks_reply(sfd, sbuf, sizeof(sbuf), + csp->config->socket_timeout) != 2) { errstr = "SOCKS5 negotiation auth read failed"; err = 1; @@ -1204,12 +1336,32 @@ static jb_socket socks5_connect(const struct forward_spec *fwd, 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); - assert(sizeof(cbuf) - client_pos > (size_t)255); - /* Using strncpy because we really want the nul byte padding. */ - strncpy(cbuf + client_pos, target_host, sizeof(cbuf) - client_pos); - client_pos += (hostlen & 0xffu); + if (host_is_ip_address(target_host) && NULL == strstr(target_host, ":")) + { + cbuf[client_pos++] = '\x01'; /* Address is IPv4 address. */ + if (JB_ERR_OK != convert_ipv4_address_to_bytes(target_host, &cbuf[client_pos])) + { + errstr = "SOCKS5 error. Failed to convert target address to IP address"; + csp->error_message = strdup(errstr); + log_error(LOG_LEVEL_CONNECT, "%s", errstr); + close_socket(sfd); + errno = EINVAL; + return(JB_INVALID_SOCKET); + } + client_pos += 4; + } + else + { + /* + * XXX: This branch is currently also used for IPv6 addresses + */ + cbuf[client_pos++] = '\x03'; /* Address is domain name. */ + cbuf[client_pos++] = (char)(hostlen & 0xffu); + assert(sizeof(cbuf) - client_pos > (size_t)255); + /* Using strncpy because we really want the nul byte padding. */ + strncpy(cbuf + client_pos, target_host, sizeof(cbuf) - client_pos - 1); + client_pos += (hostlen & 0xffu); + } cbuf[client_pos++] = (char)((target_port >> 8) & 0xff); cbuf[client_pos++] = (char)((target_port ) & 0xff); @@ -1276,7 +1428,8 @@ static jb_socket socks5_connect(const struct forward_spec *fwd, } #endif - server_size = read_socket(sfd, sbuf, SIZE_SOCKS5_REPLY_IPV4); + server_size = read_socks_reply(sfd, sbuf, SIZE_SOCKS5_REPLY_IPV4, + csp->config->socket_timeout); if (server_size != SIZE_SOCKS5_REPLY_IPV4) { errstr = "SOCKS5 negotiation read failed"; @@ -1305,15 +1458,40 @@ static jb_socket socks5_connect(const struct forward_spec *fwd, * yet. Read and discard the rest of it to make * sure it isn't treated as HTTP data later on. */ - server_size = read_socket(sfd, sbuf, SOCKS5_REPLY_DIFFERENCE); + server_size = read_socks_reply(sfd, sbuf, SOCKS5_REPLY_DIFFERENCE, + csp->config->socket_timeout); if (server_size != SOCKS5_REPLY_DIFFERENCE) { errstr = "SOCKS5 negotiation read failed (IPv6 address)"; } } + else if (sbuf[3] == '\x03') + { + /* + * The address field contains a domain name + * which means we didn't get the whole reply + * yet. Read and discard the rest of it to make + * sure it isn't treated as HTTP data later on. + */ + unsigned domain_length = (unsigned)sbuf[4]; + int bytes_left_to_read = 5 + (int)domain_length + 2 - SIZE_SOCKS5_REPLY_IPV4; + if (bytes_left_to_read <= 0 || sizeof(sbuf) < bytes_left_to_read) + { + errstr = "SOCKS5 negotiation read failed (Invalid domain length)"; + } + else + { + server_size = read_socks_reply(sfd, sbuf, bytes_left_to_read, + csp->config->socket_timeout); + if (server_size != bytes_left_to_read) + { + errstr = "SOCKS5 negotiation read failed (Domain name)"; + } + } + } else if (sbuf[3] != '\x01') { - errstr = "SOCKS5 reply contains unsupported address type"; + errstr = "SOCKS5 reply contains unsupported address type"; } if (errstr == NULL) {