diff --git a/auto_tests/TCP_test.c b/auto_tests/TCP_test.c index 25827df3..566013d7 100644 --- a/auto_tests/TCP_test.c +++ b/auto_tests/TCP_test.c @@ -381,7 +381,7 @@ START_TEST(test_client) ip_port_tcp_s.port = htons(ports[rand() % NUM_PORTS]); ip_port_tcp_s.ip.family = AF_INET6; ip_port_tcp_s.ip.ip6.in6_addr = in6addr_loopback; - TCP_Client_Connection *conn = new_TCP_connection(ip_port_tcp_s, self_public_key, f_public_key, f_secret_key); + TCP_Client_Connection *conn = new_TCP_connection(ip_port_tcp_s, self_public_key, f_public_key, f_secret_key, 0); c_sleep(50); do_TCP_connection(conn); ck_assert_msg(conn->status == TCP_CLIENT_UNCONFIRMED, "Wrong status. Expected: %u, is: %u", TCP_CLIENT_UNCONFIRMED, @@ -408,7 +408,7 @@ START_TEST(test_client) uint8_t f2_public_key[crypto_box_PUBLICKEYBYTES]; uint8_t f2_secret_key[crypto_box_SECRETKEYBYTES]; crypto_box_keypair(f2_public_key, f2_secret_key); - TCP_Client_Connection *conn2 = new_TCP_connection(ip_port_tcp_s, self_public_key, f2_public_key, f2_secret_key); + TCP_Client_Connection *conn2 = new_TCP_connection(ip_port_tcp_s, self_public_key, f2_public_key, f2_secret_key, 0); routing_response_handler(conn, response_callback, ((void *)conn) + 2); routing_status_handler(conn, status_callback, (void *)2); routing_data_handler(conn, data_callback, (void *)3); @@ -475,7 +475,7 @@ START_TEST(test_client_invalid) ip_port_tcp_s.port = htons(ports[rand() % NUM_PORTS]); ip_port_tcp_s.ip.family = AF_INET6; ip_port_tcp_s.ip.ip6.in6_addr = in6addr_loopback; - TCP_Client_Connection *conn = new_TCP_connection(ip_port_tcp_s, self_public_key, f_public_key, f_secret_key); + TCP_Client_Connection *conn = new_TCP_connection(ip_port_tcp_s, self_public_key, f_public_key, f_secret_key, 0); c_sleep(50); do_TCP_connection(conn); ck_assert_msg(conn->status == TCP_CLIENT_CONNECTING, "Wrong status. Expected: %u, is: %u", TCP_CLIENT_CONNECTING, diff --git a/auto_tests/onion_test.c b/auto_tests/onion_test.c index 3ba96e0a..29f91308 100644 --- a/auto_tests/onion_test.c +++ b/auto_tests/onion_test.c @@ -242,7 +242,7 @@ Onions *new_onions(uint16_t port) DHT *dht = new_DHT(new_networking(ip, port)); on->onion = new_onion(dht); on->onion_a = new_onion_announce(dht); - on->onion_c = new_onion_client(new_net_crypto(dht)); + on->onion_c = new_onion_client(new_net_crypto(dht, 0)); if (on->onion && on->onion_a && on->onion_c) return on; diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index 5212b9c5..8211d78b 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -1850,8 +1850,7 @@ Messenger *new_messenger(uint8_t ipv6enabled) free(m); return NULL; } - - m->net_crypto = new_net_crypto(m->dht); + m->net_crypto = new_net_crypto(m->dht, 0); if (m->net_crypto == NULL) { kill_networking(m->net); diff --git a/toxcore/TCP_client.c b/toxcore/TCP_client.c index ff92d215..06a5c394 100644 --- a/toxcore/TCP_client.c +++ b/toxcore/TCP_client.c @@ -35,8 +35,11 @@ /* return 1 on success * return 0 on failure */ -static int connect_sock_to(sock_t sock, IP_Port ip_port) +static int connect_sock_to(sock_t sock, IP_Port ip_port, TCP_Proxy_Info *proxy_info) { + if (proxy_info) + ip_port = proxy_info->ip_port; + struct sockaddr_storage addr = {0}; size_t addrsize; @@ -62,18 +65,102 @@ static int connect_sock_to(sock_t sock, IP_Port ip_port) connect(sock, (struct sockaddr *)&addr, addrsize); return 1; } + + +static void 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 */ + + TCP_conn->last_packet_length = 3; + TCP_conn->last_packet_sent = 0; +} + +/* return 1 on success. + * return 0 if no data received. + * return -1 on failure (connection refused). + */ +static int socks5_read_handshake_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] == 5 && data[1] == 0) + return 1; + + return -1; +} + +static void socks5_generate_connection_request(TCP_Client_Connection *TCP_conn) +{ + TCP_conn->last_packet[0] = 5; /* SOCKSv5 */ + TCP_conn->last_packet[1] = 1; /* command code: establish a TCP/IP stream connection */ + TCP_conn->last_packet[2] = 0; /* reserved, must be 0 */ + uint16_t length = 3; + + if (TCP_conn->ip_port.ip.family == AF_INET) { + TCP_conn->last_packet[3] = 1; /* IPv4 address */ + ++length; + memcpy(TCP_conn->last_packet + length, TCP_conn->ip_port.ip.ip4.uint8, sizeof(IP4)); + length += sizeof(IP4); + } else { + TCP_conn->last_packet[3] = 4; /* IPv6 address */ + ++length; + memcpy(TCP_conn->last_packet + length, TCP_conn->ip_port.ip.ip6.uint8, sizeof(IP6)); + length += sizeof(IP6); + } + + memcpy(TCP_conn->last_packet + length, &TCP_conn->ip_port.port, sizeof(uint16_t)); + length += sizeof(uint16_t); + + 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). + */ +static int socks5_read_connection_response(TCP_Client_Connection *TCP_conn) +{ + if (TCP_conn->ip_port.ip.family == AF_INET) { + uint8_t data[4 + sizeof(IP4) + sizeof(uint16_t)]; + int ret = read_TCP_packet(TCP_conn->sock, data, sizeof(data)); + + if (ret == -1) + return 0; + + if (data[0] == 5 && data[1] == 0) + return 1; + + } else { + uint8_t data[4 + sizeof(IP6) + sizeof(uint16_t)]; + int ret = read_TCP_packet(TCP_conn->sock, data, sizeof(data)); + + if (ret == -1) + return 0; + + if (data[0] == 5 && data[1] == 0) + return 1; + } + + return -1; +} + /* return 0 on success. * return -1 on failure. */ -static int generate_handshake(TCP_Client_Connection *TCP_conn, const uint8_t *self_public_key, - const uint8_t *self_secret_key) +static int generate_handshake(TCP_Client_Connection *TCP_conn) { uint8_t plain[crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES]; crypto_box_keypair(plain, TCP_conn->temp_secret_key); - encrypt_precompute(TCP_conn->public_key, self_secret_key, TCP_conn->shared_key); random_nonce(TCP_conn->sent_nonce); memcpy(plain + crypto_box_PUBLICKEYBYTES, TCP_conn->sent_nonce, crypto_box_NONCEBYTES); - memcpy(TCP_conn->last_packet, self_public_key, crypto_box_PUBLICKEYBYTES); + memcpy(TCP_conn->last_packet, TCP_conn->self_public_key, crypto_box_PUBLICKEYBYTES); new_nonce(TCP_conn->last_packet + crypto_box_PUBLICKEYBYTES); int len = encrypt_data_symmetric(TCP_conn->shared_key, TCP_conn->last_packet + crypto_box_PUBLICKEYBYTES, plain, sizeof(plain), TCP_conn->last_packet + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES); @@ -448,7 +535,7 @@ void onion_response_handler(TCP_Client_Connection *con, int (*onion_callback)(vo /* Create new TCP connection to ip_port/public_key */ TCP_Client_Connection *new_TCP_connection(IP_Port ip_port, const uint8_t *public_key, const uint8_t *self_public_key, - const uint8_t *self_secret_key) + const uint8_t *self_secret_key, TCP_Proxy_Info *proxy_info) { if (networking_at_startup() != 0) { return NULL; @@ -468,7 +555,7 @@ TCP_Client_Connection *new_TCP_connection(IP_Port ip_port, const uint8_t *public return 0; } - if (!(set_socket_nonblock(sock) && connect_sock_to(sock, ip_port))) { + if (!(set_socket_nonblock(sock) && connect_sock_to(sock, ip_port, proxy_info))) { kill_sock(sock); return NULL; } @@ -480,15 +567,24 @@ TCP_Client_Connection *new_TCP_connection(IP_Port ip_port, const uint8_t *public return NULL; } - temp->status = TCP_CLIENT_CONNECTING; temp->sock = sock; memcpy(temp->public_key, public_key, crypto_box_PUBLICKEYBYTES); + memcpy(temp->self_public_key, self_public_key, crypto_box_PUBLICKEYBYTES); + encrypt_precompute(temp->public_key, self_secret_key, temp->shared_key); temp->ip_port = ip_port; - if (generate_handshake(temp, self_public_key, self_secret_key) == -1) { - kill_sock(sock); - free(temp); - return NULL; + if (proxy_info) { + temp->status = TCP_CLIENT_PROXY_CONNECTING; + temp->proxy_info = *proxy_info; + socks5_generate_handshake(temp); + } else { + temp->status = TCP_CLIENT_CONNECTING; + + if (generate_handshake(temp) == -1) { + kill_sock(sock); + free(temp); + return NULL; + } } temp->kill_at = unix_time() + TCP_CONNECTION_TIMEOUT; @@ -682,6 +778,38 @@ void do_TCP_connection(TCP_Client_Connection *TCP_connection) return; } + if (TCP_connection->status == TCP_CLIENT_PROXY_CONNECTING) { + if (send_pending_data(TCP_connection) == 0) { + int ret = socks5_read_handshake_response(TCP_connection); + + if (ret == -1) { + TCP_connection->kill_at = 0; + TCP_connection->status = TCP_CLIENT_DISCONNECTED; + } + + if (ret == 1) { + socks5_generate_connection_request(TCP_connection); + TCP_connection->status = TCP_CLIENT_PROXY_UNCONFIRMED; + } + } + } + + if (TCP_connection->status == TCP_CLIENT_PROXY_UNCONFIRMED) { + if (send_pending_data(TCP_connection) == 0) { + int ret = socks5_read_connection_response(TCP_connection); + + if (ret == -1) { + TCP_connection->kill_at = 0; + TCP_connection->status = TCP_CLIENT_DISCONNECTED; + } + + if (ret == 1) { + generate_handshake(TCP_connection); + TCP_connection->status = TCP_CLIENT_CONNECTING; + } + } + } + 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 9998d20c..83cb48ba 100644 --- a/toxcore/TCP_client.h +++ b/toxcore/TCP_client.h @@ -29,8 +29,14 @@ #define TCP_CONNECTION_TIMEOUT 10 +typedef struct { + IP_Port ip_port; +} TCP_Proxy_Info; + enum { TCP_CLIENT_NO_STATUS, + TCP_CLIENT_PROXY_CONNECTING, + TCP_CLIENT_PROXY_UNCONFIRMED, TCP_CLIENT_CONNECTING, TCP_CLIENT_UNCONFIRMED, TCP_CLIENT_CONFIRMED, @@ -39,8 +45,10 @@ enum { typedef struct { uint8_t status; sock_t sock; + uint8_t self_public_key[crypto_box_PUBLICKEYBYTES]; /* our public key */ uint8_t public_key[crypto_box_PUBLICKEYBYTES]; /* public key of the server */ IP_Port ip_port; /* The ip and port of the server */ + TCP_Proxy_Info proxy_info; uint8_t recv_nonce[crypto_box_NONCEBYTES]; /* Nonce of received packets. */ uint8_t sent_nonce[crypto_box_NONCEBYTES]; /* Nonce of sent packets. */ uint8_t shared_key[crypto_box_BEFORENMBYTES]; @@ -85,7 +93,7 @@ typedef struct { /* Create new TCP connection to ip_port/public_key */ TCP_Client_Connection *new_TCP_connection(IP_Port ip_port, const uint8_t *public_key, const uint8_t *self_public_key, - const uint8_t *self_secret_key); + const uint8_t *self_secret_key, TCP_Proxy_Info *proxy_info); /* Run the TCP connection */ diff --git a/toxcore/net_crypto.c b/toxcore/net_crypto.c index 98b84e14..f26e40f6 100644 --- a/toxcore/net_crypto.c +++ b/toxcore/net_crypto.c @@ -1916,7 +1916,14 @@ int add_tcp_relay(Net_Crypto *c, IP_Port ip_port, const uint8_t *public_key) for (i = 0; i < MAX_TCP_CONNECTIONS; ++i) { if (c->tcp_connections_new[i] == NULL) { - c->tcp_connections_new[i] = new_TCP_connection(ip_port, public_key, c->dht->self_public_key, c->dht->self_secret_key); + if (c->proxy_set) { + c->tcp_connections_new[i] = new_TCP_connection(ip_port, public_key, c->dht->self_public_key, c->dht->self_secret_key, + &c->proxy_info); + } else { + c->tcp_connections_new[i] = new_TCP_connection(ip_port, public_key, c->dht->self_public_key, c->dht->self_secret_key, + 0); + } + return 0; } } @@ -2607,7 +2614,7 @@ void load_keys(Net_Crypto *c, const uint8_t *keys) /* Run this to (re)initialize net_crypto. * Sets all the global connection variables to their default values. */ -Net_Crypto *new_net_crypto(DHT *dht) +Net_Crypto *new_net_crypto(DHT *dht, TCP_Proxy_Info *proxy_info) { unix_time_update(); @@ -2634,6 +2641,12 @@ Net_Crypto *new_net_crypto(DHT *dht) bs_list_init(&temp->ip_port_list, sizeof(IP_Port), 8); pthread_mutex_init(&temp->connections_mutex, NULL); + + if (proxy_info) { + temp->proxy_info = *proxy_info; + temp->proxy_set = 1; + } + return temp; } diff --git a/toxcore/net_crypto.h b/toxcore/net_crypto.h index b8236a0e..2918dd44 100644 --- a/toxcore/net_crypto.h +++ b/toxcore/net_crypto.h @@ -201,6 +201,9 @@ typedef struct { int (*tcp_onion_callback)(void *object, const uint8_t *data, uint16_t length); void *tcp_onion_callback_object; + + uint8_t proxy_set; + TCP_Proxy_Info proxy_info; } Net_Crypto; @@ -377,7 +380,7 @@ void load_keys(Net_Crypto *c, const uint8_t *keys); /* Create new instance of Net_Crypto. * Sets all the global connection variables to their default values. */ -Net_Crypto *new_net_crypto(DHT *dht); +Net_Crypto *new_net_crypto(DHT *dht, TCP_Proxy_Info *proxy_info); /* return the optimal interval in ms for running do_net_crypto. */