diff --git a/auto_tests/invalid_udp_proxy_test.c b/auto_tests/invalid_udp_proxy_test.c index 431ec59ff6..2236f1dbed 100644 --- a/auto_tests/invalid_udp_proxy_test.c +++ b/auto_tests/invalid_udp_proxy_test.c @@ -24,7 +24,7 @@ int main(void) bootstrap_tox_live_network(tox, true); - printf("Waiting for connection..."); + printf("Waiting for connection...\n"); for (uint16_t i = 0; i < NUM_ITERATIONS; i++) { tox_iterate(tox, nullptr); diff --git a/auto_tests/proxy_test.c b/auto_tests/proxy_test.c index 79e3997fdd..73f76e1126 100644 --- a/auto_tests/proxy_test.c +++ b/auto_tests/proxy_test.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "auto_test_support.h" @@ -16,18 +17,20 @@ static void *proxy_routine(void *arg) return nullptr; } -static bool try_bootstrap(Tox *tox1, Tox *tox2, Tox *tox3, Tox *tox4) +static bool try_bootstrap(Tox *tox1, Tox *tox2, Tox *tox3, Tox *tox4, Tox *tox5) { for (uint32_t i = 0; i < 400; ++i) { if (tox_self_get_connection_status(tox1) != TOX_CONNECTION_NONE && tox_self_get_connection_status(tox2) != TOX_CONNECTION_NONE && tox_self_get_connection_status(tox3) != TOX_CONNECTION_NONE && - tox_self_get_connection_status(tox4) != TOX_CONNECTION_NONE) { - printf("%d %d %d %d\n", + tox_self_get_connection_status(tox4) != TOX_CONNECTION_NONE && + tox_self_get_connection_status(tox5) != TOX_CONNECTION_NONE) { + printf("%d %d %d %d %d\n", tox_self_get_connection_status(tox1), tox_self_get_connection_status(tox2), tox_self_get_connection_status(tox3), - tox_self_get_connection_status(tox4)); + tox_self_get_connection_status(tox4), + tox_self_get_connection_status(tox5)); return true; } @@ -35,13 +38,15 @@ static bool try_bootstrap(Tox *tox1, Tox *tox2, Tox *tox3, Tox *tox4) tox_iterate(tox2, nullptr); tox_iterate(tox3, nullptr); tox_iterate(tox4, nullptr); + tox_iterate(tox5, nullptr); if (i % 10 == 0) { - printf("%d %d %d %d\n", + printf("%d %d %d %d %d\n", tox_self_get_connection_status(tox1), tox_self_get_connection_status(tox2), tox_self_get_connection_status(tox3), - tox_self_get_connection_status(tox4)); + tox_self_get_connection_status(tox4), + tox_self_get_connection_status(tox5)); } c_sleep(tox_iteration_interval(tox1)); @@ -60,8 +65,8 @@ int main(int argc, char **argv) c_sleep(100); } - const uint16_t tcp_port = 7082; - uint32_t index[] = { 1, 2, 3, 4 }; + const uint16_t tcp_port = 7083; + uint32_t index[] = { 1, 2, 3, 4, 5 }; struct Tox_Options *tox_options = tox_options_new(nullptr); ck_assert(tox_options != nullptr); @@ -69,6 +74,7 @@ int main(int argc, char **argv) // tox1 is a TCP server and has UDP enabled. tox_options_set_udp_enabled(tox_options, true); tox_options_set_tcp_port(tox_options, tcp_port); + tox_options_set_local_discovery_enabled(tox_options, false); Tox *tox1 = tox_new_log(tox_options, nullptr, &index[0]); ck_assert(tox1 != nullptr); @@ -80,8 +86,10 @@ int main(int argc, char **argv) ck_assert(dht_port != 0); // tox2 is a regular DHT node bootstrapping against tox1. + tox_options_default(tox_options); tox_options_set_udp_enabled(tox_options, true); tox_options_set_tcp_port(tox_options, 0); + tox_options_set_local_discovery_enabled(tox_options, false); Tox *tox2 = tox_new_log(tox_options, nullptr, &index[1]); ck_assert(tox2 != nullptr); @@ -90,36 +98,57 @@ int main(int argc, char **argv) ck_assert(tox_bootstrap(tox2, "127.0.0.1", dht_port, dht_pk, nullptr)); // tox3 has UDP disabled and connects to tox1 via an HTTP proxy + tox_options_default(tox_options); tox_options_set_udp_enabled(tox_options, false); tox_options_set_proxy_host(tox_options, "127.0.0.1"); tox_options_set_proxy_port(tox_options, 7080); tox_options_set_proxy_type(tox_options, TOX_PROXY_TYPE_HTTP); + tox_options_set_local_discovery_enabled(tox_options, false); Tox *tox3 = tox_new_log(tox_options, nullptr, &index[2]); ck_assert(tox3 != nullptr); - // tox4 has UDP disabled and connects to tox1 via a SOCKS5 proxy + // tox4 has UDP disabled and connects to tox1 via a SOCKS5 proxy with no auth + tox_options_default(tox_options); tox_options_set_udp_enabled(tox_options, false); tox_options_set_proxy_host(tox_options, "127.0.0.1"); tox_options_set_proxy_port(tox_options, 7081); tox_options_set_proxy_type(tox_options, TOX_PROXY_TYPE_SOCKS5); + tox_options_set_local_discovery_enabled(tox_options, false); Tox *tox4 = tox_new_log(tox_options, nullptr, &index[3]); ck_assert(tox4 != nullptr); - // tox3 and tox4 bootstrap against tox1 and add it as a TCP relay + // tox5 has UDP disabled and connects to tox1 via a SOCKS5 proxy with username/password auth + tox_options_default(tox_options); + tox_options_set_udp_enabled(tox_options, false); + tox_options_set_proxy_host(tox_options, "127.0.0.1"); + tox_options_set_proxy_port(tox_options, 7082); + tox_options_set_proxy_type(tox_options, TOX_PROXY_TYPE_SOCKS5); + tox_options_set_proxy_socks5_username(tox_options, (const uint8_t *) "nurupo", strlen("nurupo")); + tox_options_set_proxy_socks5_password(tox_options, (const uint8_t *) "hunter2", strlen("hunter2")); + tox_options_set_local_discovery_enabled(tox_options, false); + + Tox *tox5 = tox_new_log(tox_options, nullptr, &index[4]); + ck_assert(tox5 != nullptr); + + // tox3, tox4 and tox5 bootstrap against tox1 and add it as a TCP relay ck_assert(tox_bootstrap(tox3, "127.0.0.1", dht_port, dht_pk, nullptr)); ck_assert(tox_add_tcp_relay(tox3, "127.0.0.1", tcp_port, dht_pk, nullptr)); ck_assert(tox_bootstrap(tox4, "127.0.0.1", dht_port, dht_pk, nullptr)); ck_assert(tox_add_tcp_relay(tox4, "127.0.0.1", tcp_port, dht_pk, nullptr)); + ck_assert(tox_bootstrap(tox5, "127.0.0.1", dht_port, dht_pk, nullptr)); + ck_assert(tox_add_tcp_relay(tox5, "127.0.0.1", tcp_port, dht_pk, nullptr)); + int ret = 1; - if (try_bootstrap(tox1, tox2, tox3, tox4)) { + if (try_bootstrap(tox1, tox2, tox3, tox4, tox5)) { ret = 0; } tox_options_free(tox_options); + tox_kill(tox5); tox_kill(tox4); tox_kill(tox3); tox_kill(tox2); diff --git a/other/proxy/proxy_server.go b/other/proxy/proxy_server.go index 9bf927c296..b4c644fcf2 100644 --- a/other/proxy/proxy_server.go +++ b/other/proxy/proxy_server.go @@ -14,9 +14,10 @@ import ( ) const ( - debug = false - httpAddr = ":7080" - socks5Addr = ":7081" + debug = false + httpAddr = "127.0.0.1:7080" + socks5NoAuthAddr = "127.0.0.1:7081" + socks5UsernamePasswordAuthAddr = "127.0.0.1:7082" ) func handleTunneling(w http.ResponseWriter, r *http.Request) { @@ -75,11 +76,19 @@ func main() { TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)), } - log.Printf("starting SOCKS5 proxy server on %s", socks5Addr) - socks5Server := socks5.NewServer( - socks5.WithLogger(socks5.NewLogger(log.New(os.Stdout, "socks5: ", log.LstdFlags))), + log.Printf("starting SOCKS5 no-auth proxy server on %s", socks5NoAuthAddr) + socks5NoAuthServer := socks5.NewServer( + socks5.WithLogger(socks5.NewLogger(log.New(os.Stdout, "socks5 no-auth: ", log.LstdFlags))), ) - go socks5Server.ListenAndServe("tcp", socks5Addr) + log.Printf("starting SOCKS5 username/password auth proxy server on %s", socks5UsernamePasswordAuthAddr) + authenticator := socks5.UserPassAuthenticator{socks5.StaticCredentials{"nurupo": "hunter2"}} + socks5UsernamePasswordAuthServer := socks5.NewServer( + socks5.WithAuthMethods([]socks5.Authenticator{authenticator}), + socks5.WithLogger(socks5.NewLogger(log.New(os.Stdout, "socks5 username/password auth: ", log.LstdFlags))), + ) + + go socks5NoAuthServer.ListenAndServe("tcp", socks5NoAuthAddr) + go socks5UsernamePasswordAuthServer.ListenAndServe("tcp", socks5UsernamePasswordAuthAddr) log.Fatal(httpServer.ListenAndServe()) } diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index b89f831e1a..398c3ebfec 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -3474,10 +3474,26 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * m->mem = mem; m->rng = rng; m->ns = ns; + // copy options by value + m->options = *options; + // deep copy of arrays/strings + // Messenger_Options is owned by the caller and the caller will free these, so we need to make our own copy + m->options.proxy_info.socks5_username = memdup(m->mem, options->proxy_info.socks5_username, options->proxy_info.socks5_username_length); + m->options.proxy_info.socks5_password = memdup(m->mem, options->proxy_info.socks5_password, options->proxy_info.socks5_password_length); + if ((options->proxy_info.socks5_username != nullptr && m->options.proxy_info.socks5_username == nullptr) + || (options->proxy_info.socks5_password != nullptr && m->options.proxy_info.socks5_password == nullptr)) { + LOGGER_WARNING(m->log, "failed to copy SOCKS5 proxy username or password"); + mem_delete(mem, m->options.proxy_info.socks5_username); + mem_delete(mem, m->options.proxy_info.socks5_password); + mem_delete(mem, m); + return nullptr; + } m->fr = friendreq_new(mem); if (m->fr == nullptr) { + mem_delete(mem, m->options.proxy_info.socks5_username); + mem_delete(mem, m->options.proxy_info.socks5_password); mem_delete(mem, m); return nullptr; } @@ -3486,26 +3502,28 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * if (m->log == nullptr) { friendreq_kill(m->fr); + mem_delete(mem, m->options.proxy_info.socks5_username); + mem_delete(mem, m->options.proxy_info.socks5_password); mem_delete(mem, m); return nullptr; } - logger_callback_log(m->log, options->log_callback, options->log_context, options->log_user_data); + logger_callback_log(m->log, m->options.log_callback, m->options.log_context, m->options.log_user_data); unsigned int net_err = 0; - if (!options->udp_disabled && options->proxy_info.proxy_type != TCP_PROXY_NONE) { + if (!m->options.udp_disabled && m->options.proxy_info.proxy_type != TCP_PROXY_NONE) { // We don't currently support UDP over proxy. LOGGER_INFO(m->log, "UDP enabled and proxy set: disabling UDP"); - options->udp_disabled = true; + m->options.udp_disabled = true; } - if (options->udp_disabled) { + if (m->options.udp_disabled) { m->net = new_networking_no_udp(m->log, m->mem, m->ns); } else { IP ip; - ip_init(&ip, options->ipv6enabled); - m->net = new_networking_ex(m->log, m->mem, m->ns, &ip, options->port_range[0], options->port_range[1], &net_err); + ip_init(&ip, m->options.ipv6enabled); + m->net = new_networking_ex(m->log, m->mem, m->ns, &ip, m->options.port_range[0], m->options.port_range[1], &net_err); } if (m->net == nullptr) { @@ -3517,21 +3535,25 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * } logger_kill(m->log); + mem_delete(mem, m->options.proxy_info.socks5_username); + mem_delete(mem, m->options.proxy_info.socks5_password); mem_delete(mem, m); return nullptr; } - m->dht = new_dht(m->log, m->mem, m->rng, m->ns, m->mono_time, m->net, options->hole_punching_enabled, options->local_discovery_enabled); + m->dht = new_dht(m->log, m->mem, m->rng, m->ns, m->mono_time, m->net, m->options.hole_punching_enabled, m->options.local_discovery_enabled); if (m->dht == nullptr) { kill_networking(m->net); friendreq_kill(m->fr); logger_kill(m->log); + mem_delete(mem, m->options.proxy_info.socks5_username); + mem_delete(mem, m->options.proxy_info.socks5_password); mem_delete(mem, m); return nullptr; } - m->net_crypto = new_net_crypto(m->log, m->mem, m->rng, m->ns, m->mono_time, m->dht, &options->proxy_info); + m->net_crypto = new_net_crypto(m->log, m->mem, m->rng, m->ns, m->mono_time, m->dht, &m->options.proxy_info); if (m->net_crypto == nullptr) { LOGGER_WARNING(m->log, "net_crypto initialisation failed"); @@ -3540,6 +3562,8 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * kill_networking(m->net); friendreq_kill(m->fr); logger_kill(m->log); + mem_delete(mem, m->options.proxy_info.socks5_username); + mem_delete(mem, m->options.proxy_info.socks5_password); mem_delete(mem, m); return nullptr; } @@ -3554,11 +3578,13 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * kill_networking(m->net); friendreq_kill(m->fr); logger_kill(m->log); + mem_delete(mem, m->options.proxy_info.socks5_username); + mem_delete(mem, m->options.proxy_info.socks5_password); mem_delete(mem, m); return nullptr; } - if (options->dht_announcements_enabled) { + if (m->options.dht_announcements_enabled) { m->forwarding = new_forwarding(m->log, m->mem, m->rng, m->mono_time, m->dht); if (m->forwarding != nullptr) { m->announce = new_announcements(m->log, m->mem, m->rng, m->mono_time, m->forwarding); @@ -3574,10 +3600,10 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * m->onion_a = new_onion_announce(m->log, m->mem, m->rng, m->mono_time, m->dht); m->onion_c = new_onion_client(m->log, m->mem, m->rng, m->mono_time, m->net_crypto); if (m->onion_c != nullptr) { - m->fr_c = new_friend_connections(m->log, m->mem, m->mono_time, m->ns, m->onion_c, options->local_discovery_enabled); + m->fr_c = new_friend_connections(m->log, m->mem, m->mono_time, m->ns, m->onion_c, m->options.local_discovery_enabled); } - if ((options->dht_announcements_enabled && (m->forwarding == nullptr || m->announce == nullptr)) || + if ((m->options.dht_announcements_enabled && (m->forwarding == nullptr || m->announce == nullptr)) || m->onion == nullptr || m->onion_a == nullptr || m->onion_c == nullptr || m->fr_c == nullptr) { LOGGER_WARNING(m->log, "onion initialisation failed"); @@ -3593,6 +3619,8 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * kill_networking(m->net); friendreq_kill(m->fr); logger_kill(m->log); + mem_delete(mem, m->options.proxy_info.socks5_username); + mem_delete(mem, m->options.proxy_info.socks5_password); mem_delete(mem, m); return nullptr; } @@ -3616,13 +3644,15 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * kill_networking(m->net); friendreq_kill(m->fr); logger_kill(m->log); + mem_delete(mem, m->options.proxy_info.socks5_username); + mem_delete(mem, m->options.proxy_info.socks5_password); mem_delete(mem, m); return nullptr; } - if (options->tcp_server_port != 0) { - m->tcp_server = new_tcp_server(m->log, m->mem, m->rng, m->ns, options->ipv6enabled, 1, - &options->tcp_server_port, dht_get_self_secret_key(m->dht), + if (m->options.tcp_server_port != 0) { + m->tcp_server = new_tcp_server(m->log, m->mem, m->rng, m->ns, m->options.ipv6enabled, 1, + &m->options.tcp_server_port, dht_get_self_secret_key(m->dht), m->onion, m->forwarding); if (m->tcp_server == nullptr) { @@ -3641,6 +3671,8 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * kill_networking(m->net); friendreq_kill(m->fr); logger_kill(m->log); + mem_delete(mem, m->options.proxy_info.socks5_username); + mem_delete(mem, m->options.proxy_info.socks5_password); mem_delete(mem, m); if (error != nullptr) { @@ -3651,7 +3683,6 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * } } - m->options = *options; friendreq_init(m->fr, m->fr_c); set_nospam(m->fr, random_u32(m->rng)); set_filter_function(m->fr, &friend_already_added, m); @@ -3704,6 +3735,8 @@ void kill_messenger(Messenger *m) mem_delete(m->mem, m->options.state_plugins); logger_kill(m->log); + mem_delete(m->mem, m->options.proxy_info.socks5_username); + mem_delete(m->mem, m->options.proxy_info.socks5_password); mem_delete(m->mem, m); } 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..4cb8dd6ba6 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 + uint8_t *socks5_username; + size_t socks5_username_length; + 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 8e1b43903d..c7b1a2ec91 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -873,6 +873,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 2235ccfd10..6309375ba8 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..206b374e5b 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) +static bool options_set_string_experimental_owned_data(bool experimental_owned_data, const char **options_str, char **options_owned_str, const char *user_str) { - return options->proxy_host; -} -bool tox_options_set_proxy_host(Tox_Options *options, const char *proxy_host) -{ - 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_experimental_owned_data(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->internal_do_not_set_directly_proxy_socks5_username_length; +} +size_t tox_options_get_proxy_socks5_password_length(const Tox_Options *options) +{ + return options->internal_do_not_set_directly_proxy_socks5_password_length; +} uint16_t tox_options_get_start_port(const Tox_Options *options) { return options->start_port; @@ -199,49 +212,109 @@ 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) +static bool options_set_array_experimental_owned_data(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) { - return options->savedata_data; -} + if (!experimental_owned_data) { + *options_data = user_data; + *options_length = user_length; + return true; + } -bool tox_options_set_savedata_data(Tox_Options *options, const uint8_t *savedata_data, size_t length) -{ - if (!options->experimental_owned_data) { - options->savedata_data = savedata_data; - options->savedata_length = length; + if (*options_owned_data != nullptr) { + free(*options_owned_data); + *options_owned_data = nullptr; + } + + if (user_data == nullptr) { + *options_data = nullptr; + *options_length = 0; return true; } - if (options->owned_savedata_data != nullptr) { - free(options->owned_savedata_data); - options->owned_savedata_data = nullptr; + uint8_t *owned_ptr = (uint8_t *)malloc(user_length); + if (owned_ptr == nullptr) { + *options_data = user_data; + *options_length = user_length; + *options_owned_data = nullptr; + return false; + } + + memcpy(owned_ptr, user_data, user_length); + *options_data = owned_ptr; + *options_length = user_length; + *options_owned_data = owned_ptr; + return true; +} + +static bool options_set_array(uint8_t **options_data, size_t *options_length, const uint8_t *user_data, size_t user_length) +{ + if (*options_data != nullptr) { + free(*options_data); } - if (savedata_data == nullptr) { - options->savedata_data = nullptr; - options->savedata_length = 0; + + if (user_data == nullptr || user_length == 0) { + *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 = nullptr; + *options_length = 0; 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; return true; } +uint8_t *tox_options_get_proxy_socks5_username(const Tox_Options *options) +{ + return options->internal_do_not_set_directly_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->internal_do_not_set_directly_proxy_socks5_username, + &options->internal_do_not_set_directly_proxy_socks5_username_length, + username, length); +} + +uint8_t *tox_options_get_proxy_socks5_password(const Tox_Options *options) +{ + return options->internal_do_not_set_directly_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->internal_do_not_set_directly_proxy_socks5_password, + &options->internal_do_not_set_directly_proxy_socks5_password_length, + 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_experimental_owned_data(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. @@ -279,6 +352,8 @@ void tox_options_free(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); free(options); } diff --git a/toxcore/tox_options.h b/toxcore/tox_options.h index 7d8b7aafaa..7862700518 100644 --- a/toxcore/tox_options.h +++ b/toxcore/tox_options.h @@ -174,6 +174,64 @@ struct Tox_Options { */ uint16_t proxy_port; + /** + * @brief SOCKS5 proxy username. + * + * 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. + * + * This member must be set using the corresponding function, it must not be + * set directly. The function always makes a copy of the provided data, + * regardless if experimental_owned_data is set or not, so it doesn't have + * to outlive the options object. + * + * @private + */ + uint8_t *internal_do_not_set_directly_proxy_socks5_username; + + /** + * @brief Length of the SOCKS5 proxy username. + * + * Must be at most TOX_MAX_PROXY_SOCKS5_USERNAME_LENGTH. + * + * This member must be set using the corresponding function, it must not be + * set directly. + * + * @private + */ + size_t internal_do_not_set_directly_proxy_socks5_username_length; + + /** + * @brief SOCKS5 proxy password. + * + * 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. + * + * This member must be set using the corresponding function, it must not be + * set directly. The function always makes a copy of the provided data, + * regardless if experimental_owned_data is set or not, so it doesn't have + * to outlive the options object. + * + * @private + */ + uint8_t *internal_do_not_set_directly_proxy_socks5_password; + + /** + * @brief Length of the SOCKS5 proxy password. + * + * Must be at most TOX_MAX_PROXY_SOCKS5_PASSWORD_LENGTH. + * + * This member must be set using the corresponding function, it must not be + * set directly. + * + * @private + */ + size_t internal_do_not_set_directly_proxy_socks5_password_length; + /** * The start port of the inclusive port range to attempt to use. * @@ -342,6 +400,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); +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); + +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);