From 9b86155b0b8f56a55f9e8851afa6993ed6d262a3 Mon Sep 17 00:00:00 2001 From: Maxim Biro Date: Mon, 5 Feb 2024 01:45:18 -0500 Subject: [PATCH] feat: Add username/password SOCKS5 auth --- third_party/cmp | 2 +- toxcore/TCP_client.c | 106 ++++++++++++++++++++++++++++++----- toxcore/TCP_client.h | 5 ++ toxcore/tox.c | 26 +++++++++ toxcore/tox.h | 28 ++++++++++ toxcore/tox_api.c | 14 +++++ toxcore/tox_options.c | 125 ++++++++++++++++++++++++++++-------------- toxcore/tox_options.h | 64 +++++++++++++++++++++ 8 files changed, 314 insertions(+), 56 deletions(-) diff --git a/third_party/cmp b/third_party/cmp index 52bfcfa17d..643e6a62d4 160000 --- a/third_party/cmp +++ b/third_party/cmp @@ -1 +1 @@ -Subproject commit 52bfcfa17d2eb4322da2037ad625f5575129cece +Subproject commit 643e6a62d4eb0ec2277de269cda33da02cba2756 diff --git a/toxcore/TCP_client.c b/toxcore/TCP_client.c index 1982412cda..1ae9e1013e 100644 --- a/toxcore/TCP_client.c +++ b/toxcore/TCP_client.c @@ -204,29 +204,38 @@ static int proxy_http_read_connection_response(const Logger *logger, const TCP_C } enum Tcp_Socks5_Proxy_Hs { - TCP_SOCKS5_PROXY_HS_VERSION_SOCKS5 = 0x05, - TCP_SOCKS5_PROXY_HS_COMM_ESTABLISH_REQUEST = 0x01, - TCP_SOCKS5_PROXY_HS_COMM_REQUEST_GRANTED = 0x00, - TCP_SOCKS5_PROXY_HS_AUTH_METHODS_SUPPORTED = 0x01, - TCP_SOCKS5_PROXY_HS_NO_AUTH = 0x00, - TCP_SOCKS5_PROXY_HS_RESERVED = 0x00, - TCP_SOCKS5_PROXY_HS_ADDR_TYPE_IPV4 = 0x01, - TCP_SOCKS5_PROXY_HS_ADDR_TYPE_IPV6 = 0x04, + TCP_SOCKS5_PROXY_HS_VERSION_SOCKS5 = 0x05, + TCP_SOCKS5_PROXY_HS_COMM_ESTABLISH_REQUEST = 0x01, + TCP_SOCKS5_PROXY_HS_COMM_REQUEST_GRANTED = 0x00, + TCP_SOCKS5_PROXY_HS_AUTH_METHODS_SUPPORTED = 0x01, + TCP_SOCKS5_PROXY_HS_NO_AUTH = 0x00, + TCP_SOCKS5_PROXY_HS_USERNAME_PASSWORD_AUTH = 0x02, + TCP_SOCKS5_PROXY_HS_USERNAME_PASSWORD_AUTH_VERSION = 0x01, + TCP_SOCKS5_PROXY_HS_USERNAME_PASSWORD_AUTH_SUCCESS = 0x00, + TCP_SOCKS5_PROXY_HS_RESERVED = 0x00, + TCP_SOCKS5_PROXY_HS_ADDR_TYPE_IPV4 = 0x01, + TCP_SOCKS5_PROXY_HS_ADDR_TYPE_IPV6 = 0x04, }; non_null() -static void proxy_socks5_generate_greetings(TCP_Client_Connection *tcp_conn) +static void proxy_socks5_generate_handshake(TCP_Client_Connection *tcp_conn) { tcp_conn->con.last_packet[0] = TCP_SOCKS5_PROXY_HS_VERSION_SOCKS5; tcp_conn->con.last_packet[1] = TCP_SOCKS5_PROXY_HS_AUTH_METHODS_SUPPORTED; - tcp_conn->con.last_packet[2] = TCP_SOCKS5_PROXY_HS_NO_AUTH; + + if (tcp_conn->proxy_info.socks5_username == nullptr || tcp_conn->proxy_info.socks5_password == nullptr) { + tcp_conn->con.last_packet[2] = TCP_SOCKS5_PROXY_HS_NO_AUTH; + } else { + tcp_conn->con.last_packet[2] = TCP_SOCKS5_PROXY_HS_USERNAME_PASSWORD_AUTH; + } tcp_conn->con.last_packet_length = 3; tcp_conn->con.last_packet_sent = 0; } /** - * @retval 1 on success. + * @retval 2 on success, username/password auth. + * @retval 1 on success, no auth. * @retval 0 if no data received. * @retval -1 on failure (connection refused). */ @@ -241,7 +250,57 @@ static int socks5_read_handshake_response(const Logger *logger, const TCP_Client return 0; } - if (data[0] == TCP_SOCKS5_PROXY_HS_VERSION_SOCKS5 && data[1] == TCP_SOCKS5_PROXY_HS_COMM_REQUEST_GRANTED) { + if (data[0] == TCP_SOCKS5_PROXY_HS_VERSION_SOCKS5) { + if (tcp_conn->proxy_info.socks5_username == nullptr || tcp_conn->proxy_info.socks5_password == nullptr) { + if (data[1] == TCP_SOCKS5_PROXY_HS_NO_AUTH) { + return 1; + } + } else { + if (data[1] == TCP_SOCKS5_PROXY_HS_USERNAME_PASSWORD_AUTH) { + return 2; + } + } + } + + return -1; +} + +non_null() +static void proxy_socks5_generate_authentication_request(TCP_Client_Connection *tcp_conn) +{ + tcp_conn->con.last_packet[0] = TCP_SOCKS5_PROXY_HS_USERNAME_PASSWORD_AUTH_VERSION; + tcp_conn->con.last_packet[1] = tcp_conn->proxy_info.socks5_username_length; + uint16_t length = 2; + memcpy(tcp_conn->con.last_packet + length, tcp_conn->proxy_info.socks5_username, + tcp_conn->proxy_info.socks5_username_length); + length += tcp_conn->proxy_info.socks5_username_length; + tcp_conn->con.last_packet[length] = tcp_conn->proxy_info.socks5_password_length; + ++length; + memcpy(tcp_conn->con.last_packet + length, tcp_conn->proxy_info.socks5_password, + tcp_conn->proxy_info.socks5_password_length); + length += tcp_conn->proxy_info.socks5_password_length; + + tcp_conn->con.last_packet_length = length; + tcp_conn->con.last_packet_sent = 0; +} + +/* return 1 on success. + * return 0 if no data received. + * return -1 on failure (connection refused). + */ +non_null() +static int proxy_socks5_read_authentication_response(const Logger *logger, const TCP_Client_Connection *tcp_conn) +{ + uint8_t data[2]; + const TCP_Connection *con = &tcp_conn->con; + const int ret = read_tcp_packet(logger, con->mem, con->ns, con->sock, data, sizeof(data), &con->ip_port); + + if (ret == -1) { + return 0; + } + + if (data[0] == TCP_SOCKS5_PROXY_HS_USERNAME_PASSWORD_AUTH_VERSION + && data[1] == TCP_SOCKS5_PROXY_HS_USERNAME_PASSWORD_AUTH_SUCCESS) { return 1; } @@ -676,7 +735,7 @@ TCP_Client_Connection *new_tcp_connection( case TCP_PROXY_SOCKS5: { temp->status = TCP_CLIENT_PROXY_SOCKS5_CONNECTING; - proxy_socks5_generate_greetings(temp); + proxy_socks5_generate_handshake(temp); break; } @@ -989,6 +1048,27 @@ void do_tcp_connection(const Logger *logger, const Mono_Time *mono_time, tcp_connection->status = TCP_CLIENT_DISCONNECTED; } + if (ret == 1) { /* no auth */ + proxy_socks5_generate_connection_request(tcp_connection); + tcp_connection->status = TCP_CLIENT_PROXY_SOCKS5_UNCONFIRMED; + } + + if (ret == 2) { /* username/password */ + proxy_socks5_generate_authentication_request(tcp_connection); + tcp_connection->status = TCP_CLIENT_PROXY_SOCKS5_AUTHENTICATING; + } + } + } + + if (tcp_connection->status == TCP_CLIENT_PROXY_SOCKS5_AUTHENTICATING) { + if (send_pending_data(logger, &tcp_connection->con) == 0) { + const int ret = proxy_socks5_read_authentication_response(logger, 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; diff --git a/toxcore/TCP_client.h b/toxcore/TCP_client.h index 3ed6e8c683..0547110f87 100644 --- a/toxcore/TCP_client.h +++ b/toxcore/TCP_client.h @@ -29,12 +29,17 @@ typedef enum TCP_Proxy_Type { typedef struct TCP_Proxy_Info { IP_Port ip_port; uint8_t proxy_type; // a value from TCP_PROXY_TYPE + const uint8_t *socks5_username; + size_t socks5_username_length; + const uint8_t *socks5_password; + size_t socks5_password_length; } TCP_Proxy_Info; typedef enum TCP_Client_Status { TCP_CLIENT_NO_STATUS, TCP_CLIENT_PROXY_HTTP_CONNECTING, TCP_CLIENT_PROXY_SOCKS5_CONNECTING, + TCP_CLIENT_PROXY_SOCKS5_AUTHENTICATING, TCP_CLIENT_PROXY_SOCKS5_UNCONFIRMED, TCP_CLIENT_CONNECTING, TCP_CLIENT_UNCONFIRMED, diff --git a/toxcore/tox.c b/toxcore/tox.c index a2d499d1bf..ce10367aa0 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -871,6 +871,32 @@ static Tox *tox_new_system(const struct Tox_Options *options, Tox_Err_New *error } m_options.proxy_info.ip_port.port = net_htons(tox_options_get_proxy_port(opts)); + + if (m_options.proxy_info.proxy_type == TCP_PROXY_SOCKS5) { + if (tox_options_get_proxy_socks5_username(opts) != nullptr && + (tox_options_get_proxy_socks5_username_length(opts) < 1 || + tox_options_get_proxy_socks5_username_length(opts) > TOX_MAX_PROXY_SOCKS5_USERNAME_LENGTH)) { + SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_SOCKS5_BAD_USERNAME_LENGTH); + mem_delete(sys->mem, tox); + tox_options_free(default_options); + return nullptr; + } + + m_options.proxy_info.socks5_username = tox_options_get_proxy_socks5_username(opts); + m_options.proxy_info.socks5_username_length = tox_options_get_proxy_socks5_username_length(opts); + + if (tox_options_get_proxy_socks5_password(opts) != nullptr && + (tox_options_get_proxy_socks5_password_length(opts) < 1 || + tox_options_get_proxy_socks5_password_length(opts) > TOX_MAX_PROXY_SOCKS5_PASSWORD_LENGTH)) { + SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_SOCKS5_BAD_PASSWORD_LENGTH); + mem_delete(sys->mem, tox); + tox_options_free(default_options); + return nullptr; + } + + m_options.proxy_info.socks5_password = tox_options_get_proxy_socks5_password(opts); + m_options.proxy_info.socks5_password_length = tox_options_get_proxy_socks5_password_length(opts); + } } tox->mono_time = mono_time_new(tox->sys.mem, sys->mono_time_callback, sys->mono_time_user_data); diff --git a/toxcore/tox.h b/toxcore/tox.h index b95b1903bb..9c5c7e8e3d 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h @@ -337,6 +337,24 @@ uint32_t tox_max_filename_length(void); uint32_t tox_max_hostname_length(void); +/** + * Maximum length of a SOCKS5 proxy username in bytes. + * + * @deprecated The macro will be removed in 0.3.0. Use the function instead. + */ +#define TOX_MAX_PROXY_SOCKS5_USERNAME_LENGTH 255 + +uint32_t tox_max_proxy_socks5_username_length(void); + +/** + * Maximum length of a SOCKS5 proxy password in bytes. + * + * @deprecated The macro will be removed in 0.3.0. Use the function instead. + */ +#define TOX_MAX_PROXY_SOCKS5_PASSWORD_LENGTH 255 + +uint32_t tox_max_proxy_socks5_password_length(void); + /** @} */ /** @{ @@ -457,6 +475,16 @@ typedef enum Tox_Err_New { */ TOX_ERR_NEW_LOAD_BAD_FORMAT, + /** + * The proxy_socks5_username_length is zero or too long. + */ + TOX_ERR_NEW_PROXY_SOCKS5_BAD_USERNAME_LENGTH, + + /** + * The proxy_socks5_password_length is zero or too long. + */ + TOX_ERR_NEW_PROXY_SOCKS5_BAD_PASSWORD_LENGTH, + } Tox_Err_New; const char *tox_err_new_to_string(Tox_Err_New value); diff --git a/toxcore/tox_api.c b/toxcore/tox_api.c index 808cd473f5..3cbca660a9 100644 --- a/toxcore/tox_api.c +++ b/toxcore/tox_api.c @@ -78,6 +78,14 @@ uint32_t tox_max_hostname_length(void) { return TOX_MAX_HOSTNAME_LENGTH; } +uint32_t tox_max_proxy_socks5_username_length(void) +{ + return TOX_MAX_PROXY_SOCKS5_USERNAME_LENGTH; +} +uint32_t tox_max_proxy_socks5_password_length(void) +{ + return TOX_MAX_PROXY_SOCKS5_PASSWORD_LENGTH; +} uint32_t tox_group_max_topic_length(void) { return TOX_GROUP_MAX_TOPIC_LENGTH; @@ -228,6 +236,12 @@ const char *tox_err_new_to_string(Tox_Err_New value) case TOX_ERR_NEW_LOAD_BAD_FORMAT: return "TOX_ERR_NEW_LOAD_BAD_FORMAT"; + + case TOX_ERR_NEW_PROXY_SOCKS5_BAD_USERNAME_LENGTH: + return "TOX_ERR_NEW_PROXY_SOCKS5_BAD_USERNAME_LENGTH"; + + case TOX_ERR_NEW_PROXY_SOCKS5_BAD_PASSWORD_LENGTH: + return "TOX_ERR_NEW_PROXY_SOCKS5_BAD_PASSWORD_LENGTH"; } return ""; diff --git a/toxcore/tox_options.c b/toxcore/tox_options.c index d67a8aebd0..7260ac017c 100644 --- a/toxcore/tox_options.c +++ b/toxcore/tox_options.c @@ -42,39 +42,44 @@ void tox_options_set_proxy_type(Tox_Options *options, Tox_Proxy_Type proxy_type) { options->proxy_type = proxy_type; } -const char *tox_options_get_proxy_host(const Tox_Options *options) -{ - return options->proxy_host; -} -bool tox_options_set_proxy_host(Tox_Options *options, const char *proxy_host) +static bool options_set_string(bool experimental_owned_data, const char **options_str, char **options_owned_str, const char *user_str) { - if (!options->experimental_owned_data) { - options->proxy_host = proxy_host; + if (!experimental_owned_data) { + *options_str = user_str; return true; } - if (options->owned_proxy_host != nullptr) { - free(options->owned_proxy_host); - options->owned_proxy_host = nullptr; + if (*options_owned_str != nullptr) { + free(*options_owned_str); + *options_owned_str = nullptr; } - if (proxy_host == nullptr) { - options->proxy_host = nullptr; + if (user_str == nullptr) { + *options_str = nullptr; return true; } - const size_t proxy_host_length = strlen(proxy_host) + 1; - char *owned_ptr = (char *)malloc(proxy_host_length); + const size_t user_str_length = strlen(user_str) + 1; + char *owned_ptr = (char *)malloc(user_str_length); if (owned_ptr == nullptr) { - options->proxy_host = proxy_host; - options->owned_proxy_host = nullptr; + *options_str = user_str; + *options_owned_str = nullptr; return false; } - memcpy(owned_ptr, proxy_host, proxy_host_length); - options->proxy_host = owned_ptr; - options->owned_proxy_host = owned_ptr; + memcpy(owned_ptr, user_str, user_str_length); + *options_str = owned_ptr; + *options_owned_str = owned_ptr; return true; } + +const char *tox_options_get_proxy_host(const Tox_Options *options) +{ + return options->proxy_host; +} +bool tox_options_set_proxy_host(Tox_Options *options, const char *proxy_host) +{ + return options_set_string(options->experimental_owned_data, &options->proxy_host, &options->owned_proxy_host, proxy_host); +} uint16_t tox_options_get_proxy_port(const Tox_Options *options) { return options->proxy_port; @@ -83,6 +88,14 @@ void tox_options_set_proxy_port(Tox_Options *options, uint16_t proxy_port) { options->proxy_port = proxy_port; } +size_t tox_options_get_proxy_socks5_username_length(const Tox_Options *options) +{ + return options->proxy_socks5_username_length; +} +size_t tox_options_get_proxy_socks5_password_length(const Tox_Options *options) +{ + return options->proxy_socks5_password_length; +} uint16_t tox_options_get_start_port(const Tox_Options *options) { return options->start_port; @@ -199,49 +212,77 @@ void tox_options_set_experimental_owned_data( options->experimental_owned_data = experimental_owned_data; } -const uint8_t *tox_options_get_savedata_data(const Tox_Options *options) -{ - return options->savedata_data; -} - -bool tox_options_set_savedata_data(Tox_Options *options, const uint8_t *savedata_data, size_t length) +static bool options_set_array(bool experimental_owned_data, const uint8_t **options_data, size_t *options_length, uint8_t **options_owned_data, const uint8_t *user_data, size_t user_length) { - if (!options->experimental_owned_data) { - options->savedata_data = savedata_data; - options->savedata_length = length; + if (!experimental_owned_data) { + *options_data = user_data; + *options_length = user_length; return true; } - if (options->owned_savedata_data != nullptr) { - free(options->owned_savedata_data); - options->owned_savedata_data = nullptr; + if (*options_owned_data != nullptr) { + free(*options_owned_data); + *options_owned_data = nullptr; } - if (savedata_data == nullptr) { - options->savedata_data = nullptr; - options->savedata_length = 0; + + if (user_data == nullptr) { + *options_data = nullptr; + *options_length = 0; return true; } - uint8_t *owned_ptr = (uint8_t *)malloc(length); + uint8_t *owned_ptr = (uint8_t *)malloc(user_length); if (owned_ptr == nullptr) { - options->savedata_data = savedata_data; - options->savedata_length = length; - options->owned_savedata_data = nullptr; + *options_data = user_data; + *options_length = user_length; + *options_owned_data = nullptr; return false; } - memcpy(owned_ptr, savedata_data, length); - options->savedata_data = owned_ptr; - options->savedata_length = length; - options->owned_savedata_data = owned_ptr; + memcpy(owned_ptr, user_data, user_length); + *options_data = owned_ptr; + *options_length = user_length; + *options_owned_data = owned_ptr; return true; } +const uint8_t *tox_options_get_proxy_socks5_username(const Tox_Options *options) +{ + return options->proxy_socks5_username; +} + +bool tox_options_set_proxy_socks5_username(Tox_Options *options, const uint8_t username[], size_t length) +{ + return options_set_array(options->experimental_owned_data, &options->proxy_socks5_username, &options->proxy_socks5_username_length, &options->owned_proxy_socks5_username, username, length); +} + +const uint8_t *tox_options_get_proxy_socks5_password(const Tox_Options *options) +{ + return options->proxy_socks5_password; +} + +bool tox_options_set_proxy_socks5_password(Tox_Options *options, const uint8_t password[], size_t length) +{ + return options_set_array(options->experimental_owned_data, &options->proxy_socks5_password, &options->proxy_socks5_password_length, &options->owned_proxy_socks5_password, password, length); +} + +const uint8_t *tox_options_get_savedata_data(const Tox_Options *options) +{ + return options->savedata_data; +} + +bool tox_options_set_savedata_data(Tox_Options *options, const uint8_t *savedata_data, size_t length) +{ + return options_set_array(options->experimental_owned_data, &options->savedata_data, &options->savedata_length, &options->owned_savedata_data, savedata_data, length); +} + void tox_options_default(Tox_Options *options) { if (options != nullptr) { // Free any owned data. tox_options_set_proxy_host(options, nullptr); + tox_options_set_proxy_socks5_username(options, nullptr, 0); + tox_options_set_proxy_socks5_password(options, nullptr, 0); tox_options_set_savedata_data(options, nullptr, 0); // Set the rest to default values. diff --git a/toxcore/tox_options.h b/toxcore/tox_options.h index 7d8b7aafaa..c370fe6bb9 100644 --- a/toxcore/tox_options.h +++ b/toxcore/tox_options.h @@ -174,6 +174,46 @@ struct Tox_Options { */ uint16_t proxy_port; + /** + * The username to use to connect to a SOCKS5 proxy. + * + * If set to NULL, the username/password authentication is disabled. + * + * This member is ignored (it can be NULL) if proxy_type is not + * TOX_PROXY_TYPE_SOCKS5. + * + * The data pointed at by this member is owned by the user, so must + * outlive the options object (unless experimental_owned_data is set). + */ + const uint8_t *proxy_socks5_username; + + /** + * The length of the username. + * + * Must be at most TOX_MAX_PROXY_SOCKS5_USERNAME_LENGTH. + */ + size_t proxy_socks5_username_length; + + /** + * The password to use to connect to a SOCKS5 proxy. + * + * If set to NULL, the username/password authentication is disabled. + * + * This member is ignored (it can be NULL) if proxy_type is not + * TOX_PROXY_TYPE_SOCKS5. + * + * The data pointed at by this member is owned by the user, so must + * outlive the options object (unless experimental_owned_data is set). + */ + const uint8_t *proxy_socks5_password; + + /** + * The length of the password. + * + * Must be at most TOX_MAX_PROXY_SOCKS5_PASSWORD_LENGTH. + */ + size_t proxy_socks5_password_length; + /** * The start port of the inclusive port range to attempt to use. * @@ -310,6 +350,18 @@ struct Tox_Options { * @private */ char *owned_proxy_host; + + /** + * @brief Owned pointer to the SOCKS5 proxy username. + * @private + */ + uint8_t *owned_proxy_socks5_username; + + /** + * @brief Owned pointer to the SOCKS5 proxy password. + * @private + */ + uint8_t *owned_proxy_socks5_password; }; #endif /* TOX_HIDE_DEPRECATED */ @@ -342,6 +394,18 @@ uint16_t tox_options_get_proxy_port(const Tox_Options *options); void tox_options_set_proxy_port(Tox_Options *options, uint16_t proxy_port); +const uint8_t *tox_options_get_proxy_socks5_username(const Tox_Options *options); + +bool tox_options_set_proxy_socks5_username(Tox_Options *options, const uint8_t username[], size_t length); + +size_t tox_options_get_proxy_socks5_username_length(const Tox_Options *options); + +const uint8_t *tox_options_get_proxy_socks5_password(const Tox_Options *options); + +bool tox_options_set_proxy_socks5_password(Tox_Options *options, const uint8_t password[], size_t length); + +size_t tox_options_get_proxy_socks5_password_length(const Tox_Options *options); + uint16_t tox_options_get_start_port(const Tox_Options *options); void tox_options_set_start_port(Tox_Options *options, uint16_t start_port);