Skip to content

Commit

Permalink
feat: Add username/password SOCKS5 auth
Browse files Browse the repository at this point in the history
  • Loading branch information
nurupo committed Feb 4, 2025
1 parent f1991aa commit 9b86155
Show file tree
Hide file tree
Showing 8 changed files with 314 additions and 56 deletions.
2 changes: 1 addition & 1 deletion third_party/cmp
Submodule cmp updated 2 files
+1 −3 .github/workflows/ci.yml
+3 −3 cmp.c
106 changes: 93 additions & 13 deletions toxcore/TCP_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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).
*/
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions toxcore/TCP_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
26 changes: 26 additions & 0 deletions toxcore/tox.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
28 changes: 28 additions & 0 deletions toxcore/tox.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/** @} */

/** @{
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 14 additions & 0 deletions toxcore/tox_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 "<invalid Tox_Err_New>";
Expand Down
Loading

0 comments on commit 9b86155

Please sign in to comment.