diff --git a/other/apidsl/tox.in.h b/other/apidsl/tox.in.h index 577515b123..cce5d51734 100644 --- a/other/apidsl/tox.in.h +++ b/other/apidsl/tox.in.h @@ -277,6 +277,15 @@ const FILE_ID_LENGTH = 32; */ const MAX_FILENAME_LENGTH = 255; +/** + * Maximum username/password length for proxy authentication. + */ +const MAX_PROXY_USER_PASS_LENGTH = 255; + +/** + * Minimum username/password length for proxy authentication. + */ +const MIN_PROXY_USER_PASS_LENGTH = 1; /******************************************************************************* * @@ -417,6 +426,30 @@ static class options { * proxy_type is TOX_PROXY_TYPE_NONE. */ uint16_t port; + + /** + * The username to be used to authenticate. + * + * If used, this must be non-NULL. The username must be at least MIN_PROXY_USER_PASS_LENGTH characters + * long but it must not exceed MAX_PROXY_USER_PASS_LENGTH characters. It must also be in + * a NUL-terminated C string format (MAX_PROXY_USER_PASS_LENGTH chars + 1 NUL byte). + * + * This member and password must be non-NULL in order to use username/password authentication. + * If either of these members are NULL, they are ignored and connecting to the proxy will be attempted without authentication. + */ + string username; + + /** + * The password to be used to authenticate. + * + * If used, this must be non-NULL. The password must be at least MIN_PROXY_USER_PASS_LENGTH characters + * long but it must not exceed MAX_PROXY_USER_PASS_LENGTH characters. It must also be in + * a NUL-terminated C string format (MAX_PROXY_USER_PASS_LENGTH chars + 1 NUL byte). + * + * This member and username must be non-NULL in order to use username/password authentication. + * If either of these members are NULL, they are ignored and connecting to the proxy will be attempted without authentication. + */ + string password; } /** @@ -563,6 +596,14 @@ static this new(const options_t *options) { * proxy_type was valid, but the proxy_port was invalid. */ BAD_PORT, + /** + * The username had an invalid length. + */ + BAD_USERNAME, + /** + * The password had an invalid length. + */ + BAD_PASSWORD, /** * The proxy address passed could not be resolved. */ diff --git a/toxcore/TCP_client.c b/toxcore/TCP_client.c index 752deecf31..2365487336 100644 --- a/toxcore/TCP_client.c +++ b/toxcore/TCP_client.c @@ -128,12 +128,22 @@ static int proxy_http_read_connection_response(TCP_Client_Connection *TCP_conn) return -1; } - static void proxy_socks5_generate_handshake(TCP_Client_Connection *TCP_conn) { TCP_conn->last_packet[0] = 5; /* SOCKSv5 */ TCP_conn->last_packet[1] = 1; /* number of authentication methods supported */ - TCP_conn->last_packet[2] = 0; /* No authentication */ + + /* + if the client specified a username and password, we should only try to auth with that + + this can be important because tor (for example) makes sure circuits aren't shared + between streams for which different credentials were provided + */ + if (TCP_conn->proxy_info.enable_auth) { + TCP_conn->last_packet[2] = 2; /* Username/password authentication */ + } else { + TCP_conn->last_packet[2] = 0; /* No authentication */ + } TCP_conn->last_packet_length = 3; TCP_conn->last_packet_sent = 0; @@ -151,7 +161,7 @@ static int socks5_read_handshake_response(TCP_Client_Connection *TCP_conn) if (ret == -1) return 0; - if (data[0] == 5 && data[1] == 0) // FIXME magic numbers + if (data[0] == 5 && (data[1] == 0 || data[1] == 2)) // FIXME magic numbers return 1; return -1; @@ -183,6 +193,25 @@ static void proxy_socks5_generate_connection_request(TCP_Client_Connection *TCP_ TCP_conn->last_packet_sent = 0; } +static void proxy_socks5_generate_auth_request(TCP_Client_Connection *TCP_conn) +{ + TCP_conn->last_packet[0] = 1; //version of the subnegotiation + TCP_conn->last_packet[1] = TCP_conn->proxy_info.username_len; + uint16_t length = 2; + + memcpy(TCP_conn->last_packet + length, TCP_conn->proxy_info.username, TCP_conn->proxy_info.username_len); + length += TCP_conn->proxy_info.username_len; + + TCP_conn->last_packet[length] = TCP_conn->proxy_info.password_len; + length++; + + memcpy(TCP_conn->last_packet + length, TCP_conn->proxy_info.password, TCP_conn->proxy_info.password_len); + length += TCP_conn->proxy_info.password_len; + + TCP_conn->last_packet_length = length; + TCP_conn->last_packet_sent = 0; +} + /* return 1 on success. * return 0 if no data received. * return -1 on failure (connection refused). @@ -213,6 +242,24 @@ static int proxy_socks5_read_connection_response(TCP_Client_Connection *TCP_conn return -1; } +/* return 1 on success. + * return 0 if no data received. + * return -1 on failure (connection refused). + */ +static int proxy_socks5_read_auth_response(TCP_Client_Connection *TCP_conn) +{ + uint8_t data[2]; + int ret = read_TCP_packet(TCP_conn->sock, data, sizeof(data)); + + if (ret == -1) + return 0; + + if (data[0] == 1 && data[1] == 0) + return 1; + + return -1; +} + /* return 0 on success. * return -1 on failure. */ @@ -901,8 +948,13 @@ void do_TCP_connection(TCP_Client_Connection *TCP_connection) } if (ret == 1) { - proxy_socks5_generate_connection_request(TCP_connection); - TCP_connection->status = TCP_CLIENT_PROXY_SOCKS5_UNCONFIRMED; + if (TCP_connection->proxy_info.enable_auth) { + proxy_socks5_generate_auth_request(TCP_connection); + TCP_connection->status = TCP_CLIENT_PROXY_SOCKS5_AUTH; + } else { + proxy_socks5_generate_connection_request(TCP_connection); + TCP_connection->status = TCP_CLIENT_PROXY_SOCKS5_UNCONFIRMED; + } } } } @@ -923,6 +975,22 @@ void do_TCP_connection(TCP_Client_Connection *TCP_connection) } } + if (TCP_connection->status == TCP_CLIENT_PROXY_SOCKS5_AUTH) { + if (send_pending_data(TCP_connection) ==0) { + int ret = proxy_socks5_read_auth_response(TCP_connection); + + if (ret == -1) { + TCP_connection->kill_at = 0; + TCP_connection->status = TCP_CLIENT_DISCONNECTED; + } + + if (ret == 1) { + proxy_socks5_generate_connection_request(TCP_connection); + TCP_connection->status = TCP_CLIENT_PROXY_SOCKS5_UNCONFIRMED; + } + } + } + if (TCP_connection->status == TCP_CLIENT_CONNECTING) { if (send_pending_data(TCP_connection) == 0) { TCP_connection->status = TCP_CLIENT_UNCONFIRMED; diff --git a/toxcore/TCP_client.h b/toxcore/TCP_client.h index 722430e0f3..1c7dcd3c73 100644 --- a/toxcore/TCP_client.h +++ b/toxcore/TCP_client.h @@ -29,6 +29,9 @@ #define TCP_CONNECTION_TIMEOUT 10 +#define MAX_PROXY_USER_PASS_LENGTH 255 +#define MIN_PROXY_USER_PASS_LENGTH 1 + typedef enum { TCP_PROXY_NONE, TCP_PROXY_HTTP, @@ -38,6 +41,11 @@ typedef enum { typedef struct { IP_Port ip_port; uint8_t proxy_type; // a value from TCP_PROXY_TYPE + _Bool enable_auth; + char username[MAX_PROXY_USER_PASS_LENGTH]; + uint8_t username_len; + char password[MAX_PROXY_USER_PASS_LENGTH]; + uint8_t password_len; } TCP_Proxy_Info; enum { @@ -45,6 +53,7 @@ enum { TCP_CLIENT_PROXY_HTTP_CONNECTING, TCP_CLIENT_PROXY_SOCKS5_CONNECTING, TCP_CLIENT_PROXY_SOCKS5_UNCONFIRMED, + TCP_CLIENT_PROXY_SOCKS5_AUTH, TCP_CLIENT_CONNECTING, TCP_CLIENT_UNCONFIRMED, TCP_CLIENT_CONFIRMED, diff --git a/toxcore/tox.c b/toxcore/tox.c index 23d0d3e40e..04f3e925c2 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -70,6 +70,14 @@ typedef struct Messenger Tox; #error TOX_MAX_STATUS_MESSAGE_LENGTH is assumed to be equal to MAX_STATUSMESSAGE_LENGTH #endif +#if TOX_MAX_PROXY_USER_PASS_LENGTH != MAX_PROXY_USER_PASS_LENGTH +#error TOX_MAX_PROXY_USER_PASS_LENGTH is assumed to be equal to MAX_PROXY_USER_PASS_LENGTH +#endif + +#if TOX_MIN_PROXY_USER_PASS_LENGTH != MIN_PROXY_USER_PASS_LENGTH +#error TOX_MIN_PROXY_USER_PASS_LENGTH is assumed to be equal to MIN_PROXY_USERPASS_LENGTH +#endif + uint32_t tox_version_major(void) { return TOX_VERSION_MAJOR; @@ -194,6 +202,28 @@ Tox *tox_new(const struct Tox_Options *options, TOX_ERR_NEW *error) return NULL; } + if (options->proxy_username != NULL && options->proxy_password != NULL) { + size_t username_len = strlen(options->proxy_username); + + if (username_len > TOX_MAX_PROXY_USER_PASS_LENGTH || username_len < TOX_MIN_PROXY_USER_PASS_LENGTH) { + SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_USERNAME); + return NULL; + } + + size_t password_len = strlen(options->proxy_password); + + if (password_len > TOX_MAX_PROXY_USER_PASS_LENGTH || password_len < TOX_MIN_PROXY_USER_PASS_LENGTH) { + SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_PASSWORD); + return NULL; + } + + memcpy(m_options.proxy_info.username, options->proxy_username, username_len); + memcpy(m_options.proxy_info.password, options->proxy_password, password_len); + m_options.proxy_info.username_len = username_len; + m_options.proxy_info.password_len = password_len; + m_options.proxy_info.enable_auth = true; + } + ip_init(&m_options.proxy_info.ip_port.ip, m_options.ipv6enabled); if (m_options.ipv6enabled) diff --git a/toxcore/tox.h b/toxcore/tox.h index 1655d9c106..1efdb08296 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h @@ -274,6 +274,16 @@ bool tox_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch); */ #define TOX_MAX_FILENAME_LENGTH 255 +/** + * Maximum username/password length for proxy authentication. + */ +#define TOX_MAX_PROXY_USER_PASS_LENGTH 255 + +/** + * Minimum username/password length for proxy authentication. + */ +#define TOX_MIN_PROXY_USER_PASS_LENGTH 1 + /******************************************************************************* * @@ -438,6 +448,32 @@ struct Tox_Options { uint16_t proxy_port; + /** + * The username to be used to authenticate. + * + * If used, this must be non-NULL. The username must be at least MIN_PROXY_USER_PASS_LENGTH characters + * long but it must not exceed MAX_PROXY_USER_PASS_LENGTH characters. It must also be in + * a NUL-terminated C string format (MAX_PROXY_USER_PASS_LENGTH chars + 1 NUL byte). + * + * This member and password must be non-NULL in order to use username/password authentication. + * If either of these members are NULL, they are ignored and connecting to the proxy will be attempted without authentication. + */ + const char *proxy_username; + + + /** + * The password to be used to authenticate. + * + * If used, this must be non-NULL. The password must be at least MIN_PROXY_USER_PASS_LENGTH characters + * long but it must not exceed MAX_PROXY_USER_PASS_LENGTH characters. It must also be in + * a NUL-terminated C string format (MAX_PROXY_USER_PASS_LENGTH chars + 1 NUL byte). + * + * This member and username must be non-NULL in order to use username/password authentication. + * If either of these members are NULL, they are ignored and connecting to the proxy will be attempted without authentication. + */ + const char *proxy_password; + + /** * The start port of the inclusive port range to attempt to use. * @@ -591,6 +627,16 @@ typedef enum TOX_ERR_NEW { */ TOX_ERR_NEW_PROXY_BAD_PORT, + /** + * The username had an invalid length. + */ + TOX_ERR_NEW_PROXY_BAD_USERNAME, + + /** + * The password had an invalid length. + */ + TOX_ERR_NEW_PROXY_BAD_PASSWORD, + /** * The proxy address passed could not be resolved. */