From 61d9595756033dbb2e35548708700f66944b67c3 Mon Sep 17 00:00:00 2001 From: "zugz (tox)" Date: Fri, 16 Aug 2019 00:00:00 +0000 Subject: [PATCH 1/4] clarify and isolate use of custom IP_Port values to denote TCP connections --- toxcore/TCP_connection.c | 26 ++++++++++++++++++++++-- toxcore/TCP_connection.h | 13 ++++++++++++ toxcore/TCP_server.c | 43 +++++++++++++++++++++++++++++----------- toxcore/net_crypto.c | 11 +++++----- toxcore/network.c | 12 +++++------ toxcore/network.h | 12 +++++------ toxcore/onion_client.c | 14 ++++++------- 7 files changed, 92 insertions(+), 39 deletions(-) diff --git a/toxcore/TCP_connection.c b/toxcore/TCP_connection.c index b11b1e3b083..06e9afec6bb 100644 --- a/toxcore/TCP_connection.c +++ b/toxcore/TCP_connection.c @@ -443,7 +443,7 @@ void set_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_data_cb *tcp tcp_c->tcp_data_callback_object = object; } -/* Set the callback for TCP onion packets. +/* Set the callback for TCP oob data packets. */ void set_oob_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_oob_cb *tcp_oob_callback, void *object) { @@ -451,7 +451,7 @@ void set_oob_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_oob_cb * tcp_c->tcp_oob_callback_object = object; } -/* Set the callback for TCP oob data packets. +/* Set the callback for TCP onion packets. */ void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_onion_cb *tcp_onion_callback, void *object) { @@ -459,6 +459,28 @@ void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_onion_ tcp_c->tcp_onion_callback_object = object; } +/* Encode tcp_connections_number as a custom ip_port. + * + * return ip_port. + */ +IP_Port tcp_connections_number_to_ip_port(unsigned int tcp_connections_number) +{ + IP_Port ip_port = {{{0}}}; + ip_port.ip.family = net_family_tcp_server; + ip_port.ip.ip.v6.uint32[0] = tcp_connections_number; + return ip_port; +} + +/* Decode ip_port created by tcp_connections_number_to_ip_port to tcp_connections_number. + * + * return true on success. + * return false if ip_port is invalid. + */ +bool ip_port_to_tcp_connections_number(IP_Port ip_port, unsigned int *tcp_connections_number) +{ + *tcp_connections_number = ip_port.ip.ip.v6.uint32[0]; + return net_family_is_tcp_server(ip_port.ip.family); +} /* Find the TCP connection with public_key. * diff --git a/toxcore/TCP_connection.h b/toxcore/TCP_connection.h index 1962fb1aca5..a9a51722e10 100644 --- a/toxcore/TCP_connection.h +++ b/toxcore/TCP_connection.h @@ -145,6 +145,19 @@ typedef int tcp_oob_cb(void *object, const uint8_t *public_key, unsigned int tcp */ void set_oob_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_oob_cb *tcp_oob_callback, void *object); +/* Encode tcp_connections_number as a custom ip_port. + * + * return ip_port. + */ +IP_Port tcp_connections_number_to_ip_port(unsigned int tcp_connections_number); + +/* Decode ip_port created by tcp_connections_number_to_ip_port to tcp_connections_number. + * + * return true on success. + * return false if ip_port is invalid. + */ +bool ip_port_to_tcp_connections_number(IP_Port ip_port, unsigned int *tcp_connections_number); + /* Create a new TCP connection to public_key. * * public_key must be the counterpart to the secret key that the other peer used with new_tcp_connections(). diff --git a/toxcore/TCP_server.c b/toxcore/TCP_server.c index 23b9594015f..edb6a296ed5 100644 --- a/toxcore/TCP_server.c +++ b/toxcore/TCP_server.c @@ -823,21 +823,45 @@ static int rm_connection_index(TCP_Server *tcp_server, TCP_Secure_Connection *co return -1; } +/* Encode con_id and identifier as a custom IP_Port. + * + * return ip_port. + */ +static IP_Port con_id_to_ip_port(uint32_t con_id, uint64_t identifier) +{ + IP_Port ip_port = {{{0}}}; + ip_port.ip.family = net_family_tcp_client; + ip_port.ip.ip.v6.uint32[0] = con_id; + ip_port.ip.ip.v6.uint64[1] = identifier; + return ip_port; + +} + +/* Decode ip_port created by con_id_to_ip_port to con_id. + * + * return true on success. + * return false if ip_port is invalid. + */ +static bool ip_port_to_con_id(const TCP_Server *tcp_server, IP_Port ip_port, uint32_t *con_id) +{ + *con_id = ip_port.ip.ip.v6.uint32[0]; + + return (net_family_is_tcp_client(ip_port.ip.family) && + *con_id < tcp_server->size_accepted_connections && + tcp_server->accepted_connection_array[*con_id].identifier == ip_port.ip.ip.v6.uint64[1]); +} + static int handle_onion_recv_1(void *object, IP_Port dest, const uint8_t *data, uint16_t length) { TCP_Server *tcp_server = (TCP_Server *)object; - uint32_t index = dest.ip.ip.v6.uint32[0]; + uint32_t index; - if (index >= tcp_server->size_accepted_connections) { + if (!ip_port_to_con_id(tcp_server, dest, &index)) { return 1; } TCP_Secure_Connection *con = &tcp_server->accepted_connection_array[index]; - if (con->identifier != dest.ip.ip.v6.uint64[1]) { - return 1; - } - VLA(uint8_t, packet, 1 + length); memcpy(packet + 1, data, length); packet[0] = TCP_PACKET_ONION_RESPONSE; @@ -931,12 +955,7 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint return -1; } - IP_Port source; - source.port = 0; // dummy initialise - source.ip.family = net_family_tcp_onion; - source.ip.ip.v6.uint32[0] = con_id; - source.ip.ip.v6.uint32[1] = 0; - source.ip.ip.v6.uint64[1] = con->identifier; + IP_Port source = con_id_to_ip_port(con_id, con->identifier); onion_send_1(tcp_server->onion, data + 1 + CRYPTO_NONCE_SIZE, length - (1 + CRYPTO_NONCE_SIZE), source, data + 1); } diff --git a/toxcore/net_crypto.c b/toxcore/net_crypto.c index 3ace3beaea9..17c196c549b 100644 --- a/toxcore/net_crypto.c +++ b/toxcore/net_crypto.c @@ -1868,8 +1868,10 @@ static int crypto_connection_add_source(Net_Crypto *c, int crypt_connection_id, return 0; } - if (net_family_is_tcp_family(source.ip.family)) { - if (add_tcp_number_relay_connection(c->tcp_c, conn->connection_number_tcp, source.ip.ip.v6.uint32[0]) == 0) { + unsigned int tcp_connections_number; + + if (ip_port_to_tcp_connections_number(source, &tcp_connections_number)) { + if (add_tcp_number_relay_connection(c->tcp_c, conn->connection_number_tcp, tcp_connections_number) == 0) { return 1; } } @@ -2147,10 +2149,7 @@ static int tcp_oob_callback(void *object, const uint8_t *public_key, unsigned in } if (data[0] == NET_PACKET_CRYPTO_HS) { - IP_Port source; - source.port = 0; - source.ip.family = net_family_tcp_family; - source.ip.ip.v6.uint32[0] = tcp_connections_number; + IP_Port source = tcp_connections_number_to_ip_port(tcp_connections_number); if (handle_new_connection_handshake(c, source, data, length, userdata) != 0) { return -1; diff --git a/toxcore/network.c b/toxcore/network.c index 5e5160d8933..d7f4ea2e347 100644 --- a/toxcore/network.c +++ b/toxcore/network.c @@ -294,8 +294,8 @@ const Socket net_invalid_socket = { (int)INVALID_SOCKET }; const Family net_family_unspec = {TOX_AF_UNSPEC}; const Family net_family_ipv4 = {TOX_AF_INET}; const Family net_family_ipv6 = {TOX_AF_INET6}; -const Family net_family_tcp_family = {TCP_FAMILY}; -const Family net_family_tcp_onion = {TCP_ONION_FAMILY}; +const Family net_family_tcp_server = {TCP_SERVER}; +const Family net_family_tcp_client = {TCP_CLIENT_FAMILY}; const Family net_family_tcp_ipv4 = {TCP_INET}; const Family net_family_tcp_ipv6 = {TCP_INET6}; const Family net_family_tox_tcp_ipv4 = {TOX_TCP_INET}; @@ -316,14 +316,14 @@ bool net_family_is_ipv6(Family family) return family.value == net_family_ipv6.value; } -bool net_family_is_tcp_family(Family family) +bool net_family_is_tcp_server(Family family) { - return family.value == net_family_tcp_family.value; + return family.value == net_family_tcp_server.value; } -bool net_family_is_tcp_onion(Family family) +bool net_family_is_tcp_client(Family family) { - return family.value == net_family_tcp_onion.value; + return family.value == net_family_tcp_client.value; } bool net_family_is_tcp_ipv4(Family family) diff --git a/toxcore/network.h b/toxcore/network.h index a1d838422cc..4d2415265ff 100644 --- a/toxcore/network.h +++ b/toxcore/network.h @@ -41,8 +41,8 @@ typedef struct Family { bool net_family_is_unspec(Family family); bool net_family_is_ipv4(Family family); bool net_family_is_ipv6(Family family); -bool net_family_is_tcp_family(Family family); -bool net_family_is_tcp_onion(Family family); +bool net_family_is_tcp_server(Family family); +bool net_family_is_tcp_client(Family family); bool net_family_is_tcp_ipv4(Family family); bool net_family_is_tcp_ipv6(Family family); bool net_family_is_tox_tcp_ipv4(Family family); @@ -51,8 +51,8 @@ bool net_family_is_tox_tcp_ipv6(Family family); extern const Family net_family_unspec; extern const Family net_family_ipv4; extern const Family net_family_ipv6; -extern const Family net_family_tcp_family; -extern const Family net_family_tcp_onion; +extern const Family net_family_tcp_server; +extern const Family net_family_tcp_client; extern const Family net_family_tcp_ipv4; extern const Family net_family_tcp_ipv6; extern const Family net_family_tox_tcp_ipv4; @@ -148,10 +148,10 @@ typedef enum Net_Packet_Type { #define TOX_PROTO_UDP 2 /* TCP related */ -#define TCP_ONION_FAMILY (TOX_AF_INET6 + 1) +#define TCP_CLIENT_FAMILY (TOX_AF_INET6 + 1) #define TCP_INET (TOX_AF_INET6 + 2) #define TCP_INET6 (TOX_AF_INET6 + 3) -#define TCP_FAMILY (TOX_AF_INET6 + 4) +#define TCP_SERVER (TOX_AF_INET6 + 4) typedef union IP4 { uint32_t uint32; diff --git a/toxcore/onion_client.c b/toxcore/onion_client.c index d11a91e81df..944822095d4 100644 --- a/toxcore/onion_client.c +++ b/toxcore/onion_client.c @@ -294,8 +294,7 @@ static uint16_t random_nodes_path_onion(const Onion_Client *onion_c, Node_format } if (num_nodes >= 2) { - nodes[0].ip_port.ip.family = net_family_tcp_family; - nodes[0].ip_port.ip.ip.v4.uint32 = random_tcp; + nodes[0].ip_port = tcp_connections_number_to_ip_port(random_tcp); for (i = 1; i < max_num; ++i) { nodes[i] = onion_c->path_nodes[random_u32() % num_nodes]; @@ -307,8 +306,7 @@ static uint16_t random_nodes_path_onion(const Onion_Client *onion_c, Node_format return 0; } - nodes[0].ip_port.ip.family = net_family_tcp_family; - nodes[0].ip_port.ip.ip.v4.uint32 = random_tcp; + nodes[0].ip_port = tcp_connections_number_to_ip_port(random_tcp); for (i = 1; i < max_num; ++i) { nodes[i] = onion_c->path_nodes_bs[random_u32() % num_nodes_bs]; @@ -491,7 +489,9 @@ static int send_onion_packet_tcp_udp(const Onion_Client *onion_c, const Onion_Pa return 0; } - if (net_family_is_tcp_family(path->ip_port1.ip.family)) { + unsigned int tcp_connections_number; + + if (ip_port_to_tcp_connections_number(path->ip_port1, &tcp_connections_number)) { uint8_t packet[ONION_MAX_PACKET_SIZE]; int len = create_onion_packet_tcp(packet, sizeof(packet), path, dest, data, length); @@ -499,7 +499,7 @@ static int send_onion_packet_tcp_udp(const Onion_Client *onion_c, const Onion_Pa return -1; } - return send_tcp_onion_request(onion_c->c, path->ip_port1.ip.ip.v4.uint32, packet, len); + return send_tcp_onion_request(onion_c->c, tcp_connections_number, packet, len); } return -1; @@ -1026,7 +1026,7 @@ static int handle_tcp_onion(void *object, const uint8_t *data, uint16_t length, } IP_Port ip_port = {{{0}}}; - ip_port.ip.family = net_family_tcp_family; + ip_port.ip.family = net_family_tcp_server; if (data[0] == NET_PACKET_ANNOUNCE_RESPONSE) { return handle_announce_response(object, ip_port, data, length, userdata); From c63b6a6452d9bfe976575bb29dbe7edef06d4c25 Mon Sep 17 00:00:00 2001 From: "zugz (tox)" Date: Fri, 16 Aug 2019 00:00:00 +0000 Subject: [PATCH 2/4] add capability for dht nodes and tcp relays to forward packets to arbitrary hosts --- CMakeLists.txt | 4 +- toxcore/DHT.c | 23 +++++-- toxcore/DHT.h | 10 ++- toxcore/Makefile.inc | 2 + toxcore/TCP_server.c | 42 +++++++++++ toxcore/TCP_server.h | 2 + toxcore/forwarding.c | 161 +++++++++++++++++++++++++++++++++++++++++++ toxcore/forwarding.h | 58 ++++++++++++++++ toxcore/network.h | 3 + toxcore/onion.c | 9 +++ toxcore/onion.h | 2 + 11 files changed, 307 insertions(+), 9 deletions(-) create mode 100644 toxcore/forwarding.c create mode 100644 toxcore/forwarding.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c93f17e6741..bf2c7103352 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,7 +206,9 @@ set(toxcore_SOURCES ${toxcore_SOURCES} toxcore/onion_announce.c toxcore/onion_announce.h toxcore/onion_client.c - toxcore/onion_client.h) + toxcore/onion_client.h + toxcore/forwarding.c + toxcore/forwarding.h) # LAYER 5: Friend requests and connections # ---------------------------------------- diff --git a/toxcore/DHT.c b/toxcore/DHT.c index 79925552acc..bdc11b9329f 100644 --- a/toxcore/DHT.c +++ b/toxcore/DHT.c @@ -413,7 +413,7 @@ int packed_node_size(Family ip_family) * Returns size of packed IP_Port data on success * Return -1 on failure. */ -int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port) +int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port, bool any_family) { if (data == nullptr) { return -1; @@ -436,7 +436,12 @@ int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port) is_ipv4 = false; net_family = TOX_TCP_INET6; } else { - return -1; + if (any_family) { + is_ipv4 = false; + net_family = ip_port->ip.family.value; + } else { + return -1; + } } if (is_ipv4) { @@ -491,7 +496,7 @@ static int dht_create_packet(const uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE], * Return size of unpacked ip_port on success. * Return -1 on failure. */ -int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool tcp_enabled) +int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool tcp_enabled, bool any_family) { if (data == nullptr) { return -1; @@ -521,7 +526,12 @@ int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool is_ipv4 = false; host_family = net_family_tcp_ipv6; } else { - return -1; + if (any_family) { + is_ipv4 = false; + host_family.value = data[0]; + } else { + return -1; + } } if (is_ipv4) { @@ -559,7 +569,7 @@ int pack_nodes(uint8_t *data, uint16_t length, const Node_format *nodes, uint16_ uint32_t packed_length = 0; for (uint32_t i = 0; i < number && packed_length < length; ++i) { - const int ipp_size = pack_ip_port(data + packed_length, length - packed_length, &nodes[i].ip_port); + const int ipp_size = pack_ip_port(data + packed_length, length - packed_length, &nodes[i].ip_port, false); if (ipp_size == -1) { return -1; @@ -596,7 +606,8 @@ int unpack_nodes(Node_format *nodes, uint16_t max_num_nodes, uint16_t *processed uint32_t num = 0, len_processed = 0; while (num < max_num_nodes && len_processed < length) { - const int ipp_size = unpack_ip_port(&nodes[num].ip_port, data + len_processed, length - len_processed, tcp_enabled); + const int ipp_size = unpack_ip_port(&nodes[num].ip_port, data + len_processed, length - len_processed, tcp_enabled, + false); if (ipp_size == -1) { return -1; diff --git a/toxcore/DHT.h b/toxcore/DHT.h index 5feb74d275f..c6d5bd41cc2 100644 --- a/toxcore/DHT.h +++ b/toxcore/DHT.h @@ -167,14 +167,14 @@ int packed_node_size(Family ip_family); * Returns size of packed IP_Port data on success * Return -1 on failure. */ -int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port); +int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port, bool any_family); /* Unpack IP_Port structure from data of max size length into ip_port. * * Return size of unpacked ip_port on success. * Return -1 on failure. */ -int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool tcp_enabled); +int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool tcp_enabled, bool any_family); /* Pack number of nodes into data of maxlength length. * @@ -327,6 +327,12 @@ bool node_addable_to_close_list(DHT *dht, const uint8_t *public_key, IP_Port ip_ int get_close_nodes(const DHT *dht, const uint8_t *public_key, Node_format *nodes_list, Family sa_family, bool is_LAN, uint8_t want_good); +/* Put ip_port for a random node in ip_port. + * May fail (with low probability) even if there are nodes available. + * + * return true on success, false otherwise. + */ +bool random_dht_node_ip_port(const DHT *dht, IP_Port *ip_port); /* Put up to max_num nodes in nodes from the random friends. * diff --git a/toxcore/Makefile.inc b/toxcore/Makefile.inc index a68b2a7d043..168f27553e6 100644 --- a/toxcore/Makefile.inc +++ b/toxcore/Makefile.inc @@ -46,6 +46,8 @@ libtoxcore_la_SOURCES = ../toxcore/ccompat.h \ ../toxcore/onion_announce.c \ ../toxcore/onion_client.h \ ../toxcore/onion_client.c \ + ../toxcore/forwarders.h \ + ../toxcore/forwarders.c \ ../toxcore/TCP_client.h \ ../toxcore/TCP_client.c \ ../toxcore/TCP_server.h \ diff --git a/toxcore/TCP_server.c b/toxcore/TCP_server.c index edb6a296ed5..77b9f5c772c 100644 --- a/toxcore/TCP_server.c +++ b/toxcore/TCP_server.c @@ -39,6 +39,7 @@ #include #endif +#include "forwarding.h" #include "mono_time.h" #include "util.h" @@ -82,6 +83,7 @@ typedef struct TCP_Secure_Connection { struct TCP_Server { Onion *onion; + Forwarding *forwarding; #ifdef TCP_SERVER_USE_EPOLL int efd; @@ -873,6 +875,24 @@ static int handle_onion_recv_1(void *object, IP_Port dest, const uint8_t *data, return 0; } +static bool handle_forward_request_tcp(void *object, IP_Port dest, const uint8_t *data, uint16_t length) +{ + TCP_Server *tcp_server = (TCP_Server *)object; + uint32_t index; + + if (!ip_port_to_con_id(tcp_server, dest, &index)) { + return 1; + } + + TCP_Secure_Connection *con = &tcp_server->accepted_connection_array[index]; + + VLA(uint8_t, packet, 1 + length); + memcpy(packet + 1, data, length); + packet[0] = TCP_PACKET_FORWARDING; + + return (write_packet_TCP_secure_connection(con, packet, SIZEOF_VLA(packet), 0) == 1); +} + /* return 0 on success * return -1 on failure */ @@ -967,6 +987,20 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint return -1; } + case TCP_PACKET_FORWARD_REQUEST: { + if (tcp_server->forwarding) { + IP_Port source = con_id_to_ip_port(con_id, con->identifier); + handle_forward_request(tcp_server->forwarding, source, data + 1, length - 1); + } + + return 0; + } + + case TCP_PACKET_FORWARDING: { + return -1; + } + + default: { if (data[0] < NUM_RESERVED_PORTS) { return -1; @@ -1159,8 +1193,12 @@ TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, const uin if (onion) { temp->onion = onion; set_callback_handle_recv_1(onion, &handle_onion_recv_1, temp); + + temp->forwarding = onion->forwarding; + set_callback_forward_request_not_inet(temp->forwarding, &handle_forward_request_tcp, temp); } + memcpy(temp->secret_key, secret_key, CRYPTO_SECRET_KEY_SIZE); crypto_derive_public_key(temp->public_key, temp->secret_key); @@ -1493,6 +1531,10 @@ void kill_TCP_server(TCP_Server *tcp_server) set_callback_handle_recv_1(tcp_server->onion, nullptr, nullptr); } + if (tcp_server->forwarding) { + set_callback_forward_request_not_inet(tcp_server->forwarding, nullptr, nullptr); + } + bs_list_free(&tcp_server->accepted_key_list); #ifdef TCP_SERVER_USE_EPOLL diff --git a/toxcore/TCP_server.h b/toxcore/TCP_server.h index 252df9faf2c..bb7780d6d77 100644 --- a/toxcore/TCP_server.h +++ b/toxcore/TCP_server.h @@ -52,6 +52,8 @@ #define TCP_PACKET_OOB_RECV 7 #define TCP_PACKET_ONION_REQUEST 8 #define TCP_PACKET_ONION_RESPONSE 9 +#define TCP_PACKET_FORWARD_REQUEST 10 +#define TCP_PACKET_FORWARDING 11 #define ARRAY_ENTRY_SIZE 6 diff --git a/toxcore/forwarding.c b/toxcore/forwarding.c new file mode 100644 index 00000000000..b6c6036ed57 --- /dev/null +++ b/toxcore/forwarding.c @@ -0,0 +1,161 @@ +/* + * Copyright © 2019 The TokTok team. + * + * This file is part of Tox, the free peer to peer instant messenger. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "forwarding.h" + +#include +#include + +#include "ccompat.h" +#include "DHT.h" + +struct Forwarding { + Networking_Core *net; + + forward_request_cb *forward_request_cb; + void *forward_request_callback_object; +}; + +int prepend_ip_port(uint8_t *out, uint16_t out_length, IP_Port ip_port, const uint8_t *data, uint16_t length) +{ + if (out_length < MAX_PACKED_IPPORT_SIZE) { + return -1; + } + + int ipport_length = pack_ip_port(out, MAX_PACKED_IPPORT_SIZE, &ip_port, true); + + if (ipport_length == -1) { + return -1; + } + + if (out_length < ipport_length + length) { + return -1; + } + + memcpy(out + ipport_length, data, length); + + return ipport_length + length; +} + +/* Encrypt and send unencrypted forwarding packet. + * Encryption is with a fresh random symmetric key and the zero nonce. + * + * return true on success, false otherwise. + */ +static bool send_forwarding_packet(Networking_Core *net, IP_Port dest, const uint8_t *plain, + uint16_t length) +{ + const uint16_t len = 1 + CRYPTO_SYMMETRIC_KEY_SIZE + length + CRYPTO_MAC_SIZE; + + if (len > FORWARDING_MAX_PACKET_SIZE) { + return false; + } + + VLA(uint8_t, packet, len); + const uint8_t zero_nonce[CRYPTO_NONCE_SIZE] = {0}; + + packet[0] = NET_PACKET_FORWARDING; + new_symmetric_key(packet + 1); + + if (encrypt_data_symmetric(packet + 1, zero_nonce, plain, length, + packet + 1 + CRYPTO_SYMMETRIC_KEY_SIZE) != + length + CRYPTO_MAC_SIZE) { + return false; + } + + return (sendpacket(net, dest, packet, len) == len); +} + +bool handle_forward_request(const Forwarding *forwarding, IP_Port source, const uint8_t *packet, uint16_t length) +{ + if (length > FORWARD_REQUEST_MAX_PACKET_SIZE) { + return false; + } + + IP_Port dest; + int ipport_length = unpack_ip_port(&dest, packet, length, true, true); + + if (ipport_length == -1) { + return false; + } + + VLA(uint8_t, plain, length - ipport_length + MAX_PACKED_IPPORT_SIZE); + + int plain_length = prepend_ip_port(plain, SIZEOF_VLA(plain), source, packet + ipport_length, length - ipport_length); + + if (plain_length == -1) { + return false; + } + + if (!net_family_is_ipv4(dest.ip.family) && !net_family_is_ipv6(dest.ip.family)) { + return (forwarding->forward_request_cb && + forwarding->forward_request_cb(forwarding->forward_request_callback_object, dest, plain, plain_length)); + } + + return send_forwarding_packet(forwarding->net, dest, plain, plain_length); +} + +static int handle_forward_request_direct(void *object, IP_Port source, const uint8_t *packet, uint16_t length, + void *userdata) +{ + if (!handle_forward_request((Forwarding *)object, source, packet + 1, length - 1)) { + return 1; + } + + return 0; +} + +void set_callback_forward_request_not_inet(Forwarding *forwarding, forward_request_cb *function, void *object) +{ + forwarding->forward_request_cb = function; + forwarding->forward_request_callback_object = object; +} + +Forwarding *new_forwarding(Networking_Core *net) +{ + if (net == nullptr) { + return nullptr; + } + + Forwarding *forwarding = (Forwarding *)calloc(1, sizeof(Forwarding)); + + if (forwarding == nullptr) { + return nullptr; + } + + forwarding->net = net; + + networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REQUEST, &handle_forward_request_direct, forwarding); + + return forwarding; +} + +void kill_forwarding(Forwarding *forwarding) +{ + if (forwarding == nullptr) { + return; + } + + networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REQUEST, nullptr, nullptr); + + free(forwarding); +} diff --git a/toxcore/forwarding.h b/toxcore/forwarding.h new file mode 100644 index 00000000000..51715f5f5dd --- /dev/null +++ b/toxcore/forwarding.h @@ -0,0 +1,58 @@ +/* + * Copyright © 2019 The TokTok team. + * + * This file is part of Tox, the free peer to peer instant messenger. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + */ +#ifndef C_TOXCORE_TOXCORE_FORWARDING_H +#define C_TOXCORE_TOXCORE_FORWARDING_H + +#include "crypto_core.h" +#include "network.h" + +#define MAX_PACKED_IPPORT_SIZE (1 + SIZE_IP6 + sizeof(uint16_t)) + +#define MAX_FORWARD_DATA_SIZE 4096 + +#define FORWARD_REQUEST_MAX_PACKET_SIZE (1 + MAX_PACKED_IPPORT_SIZE + MAX_FORWARD_DATA_SIZE) +#define FORWARDING_MAX_PACKET_SIZE (1 + CRYPTO_SHARED_KEY_SIZE + MAX_PACKED_IPPORT_SIZE + MAX_FORWARD_DATA_SIZE + CRYPTO_MAC_SIZE) + +typedef struct Forwarding Forwarding; + +/* Write packed ip_port followed by data to out. + * Fail if the total length is more than out_length. + * + * return output length, or -1 on failure. + */ +int prepend_ip_port(uint8_t *out, uint16_t out_length, IP_Port ip_port, const uint8_t *data, uint16_t length); + +/* Handle forward request packet. + * Intended for use by TCP server when handling forward requests. + * + * return true on success, false otherwise. + */ +bool handle_forward_request(const Forwarding *forwarding, IP_Port source, const uint8_t *packet, uint16_t length); + +/* Set callback to handle a forward request when the dest ip_port doesn't have + * TOX_AF_INET6 or TOX_AF_INET as the family. + */ +typedef bool forward_request_cb(void *object, IP_Port dest, const uint8_t *data, uint16_t length); +void set_callback_forward_request_not_inet(Forwarding *forwarding, forward_request_cb *function, void *object); + +Forwarding *new_forwarding(Networking_Core *net); + +void kill_forwarding(); + +#endif diff --git a/toxcore/network.h b/toxcore/network.h index 4d2415265ff..1f35a0dd025 100644 --- a/toxcore/network.h +++ b/toxcore/network.h @@ -124,6 +124,9 @@ typedef enum Net_Packet_Type { NET_PACKET_ONION_RECV_2 = 0x8d, NET_PACKET_ONION_RECV_1 = 0x8e, + NET_PACKET_FORWARD_REQUEST = 0x90, + NET_PACKET_FORWARDING = 0x91, + BOOTSTRAP_INFO_PACKET_ID = 0xf0, /* Only used for bootstrap nodes */ NET_PACKET_MAX = 0xff, /* This type must remain within a single uint8. */ diff --git a/toxcore/onion.c b/toxcore/onion.c index f1873992d5b..792cbb95fce 100644 --- a/toxcore/onion.c +++ b/toxcore/onion.c @@ -683,6 +683,13 @@ Onion *new_onion(Mono_Time *mono_time, DHT *dht) new_symmetric_key(onion->secret_symmetric_key); onion->timestamp = mono_time_get(onion->mono_time); + onion->forwarding = new_forwarding(onion->net); + + if (onion->forwarding == nullptr) { + free(onion); + return nullptr; + } + networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_INITIAL, &handle_send_initial, onion); networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_1, &handle_send_1, onion); networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_2, &handle_send_2, onion); @@ -708,5 +715,7 @@ void kill_onion(Onion *onion) networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_2, nullptr, nullptr); networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_1, nullptr, nullptr); + kill_forwarding(onion->forwarding); + free(onion); } diff --git a/toxcore/onion.h b/toxcore/onion.h index 20cd1e5c2f1..62c867ffc2a 100644 --- a/toxcore/onion.h +++ b/toxcore/onion.h @@ -25,6 +25,7 @@ #define C_TOXCORE_TOXCORE_ONION_H #include "DHT.h" +#include "forwarding.h" #include "mono_time.h" typedef int onion_recv_1_cb(void *object, IP_Port dest, const uint8_t *data, uint16_t length); @@ -33,6 +34,7 @@ typedef struct Onion { Mono_Time *mono_time; DHT *dht; Networking_Core *net; + Forwarding *forwarding; uint8_t secret_symmetric_key[CRYPTO_SYMMETRIC_KEY_SIZE]; uint64_t timestamp; From 4476e75c70f9b45713d0b8a7fe735184ba226493 Mon Sep 17 00:00:00 2001 From: "zugz (tox)" Date: Fri, 16 Aug 2019 00:00:00 +0000 Subject: [PATCH 3/4] add capability for peers to use forwarders to send packets to peers --- CMakeLists.txt | 4 +- toxcore/DHT.c | 37 ++++++ toxcore/Makefile.inc | 2 + toxcore/TCP_client.c | 26 ++++ toxcore/TCP_client.h | 6 + toxcore/TCP_connection.c | 78 ++++++++++++ toxcore/TCP_connection.h | 16 +++ toxcore/TCP_server.c | 2 - toxcore/forwarders.c | 265 +++++++++++++++++++++++++++++++++++++++ toxcore/forwarders.h | 60 +++++++++ toxcore/forwarding.c | 6 +- toxcore/onion.c | 4 + toxcore/onion_client.c | 10 ++ 13 files changed, 508 insertions(+), 8 deletions(-) create mode 100644 toxcore/forwarders.c create mode 100644 toxcore/forwarders.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bf2c7103352..fd257ce68c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,7 +208,9 @@ set(toxcore_SOURCES ${toxcore_SOURCES} toxcore/onion_client.c toxcore/onion_client.h toxcore/forwarding.c - toxcore/forwarding.h) + toxcore/forwarding.h + toxcore/forwarders.c + toxcore/forwarders.h) # LAYER 5: Friend requests and connections # ---------------------------------------- diff --git a/toxcore/DHT.c b/toxcore/DHT.c index bdc11b9329f..899a7c8b7d0 100644 --- a/toxcore/DHT.c +++ b/toxcore/DHT.c @@ -2571,6 +2571,43 @@ static uint16_t list_nodes(Client_data *list, size_t length, const Mono_Time *mo return count; } +/* Put ip_port for a random node in ip_port. + * May fail (with low probability) even if there are nodes available. + * + * return true on success, false otherwise. + */ +bool random_dht_node_ip_port(const DHT *dht, IP_Port *ip_port) +{ + const uint32_t max_num_nodes = LCLIENT_LIST + dht->num_friends * MAX_FRIEND_CLIENTS; + bool found = false; + + for (uint32_t n = 0; !found && n < max_num_nodes; ++n) { + uint32_t i = random_u32() % max_num_nodes; + const Client_data *client_list = dht->close_clientlist; + + if (i >= LCLIENT_LIST) { + client_list = dht->friends_list[(i - LCLIENT_LIST) / MAX_FRIEND_CLIENTS].client_list; + i = (i - LCLIENT_LIST) % MAX_FRIEND_CLIENTS; + } + + const Client_data *client = &client_list[i]; + + if (!mono_time_is_timeout(dht->mono_time, client->assoc4.timestamp, BAD_NODE_TIMEOUT)) { + *ip_port = client->assoc4.ip_port; + found = true; + } + + if (!mono_time_is_timeout(dht->mono_time, client->assoc6.timestamp, BAD_NODE_TIMEOUT)) { + if (!found || (random_u08() % 2)) { + *ip_port = client->assoc6.ip_port; + found = true; + } + } + } + + return found; +} + /* Put up to max_num nodes in nodes from the random friends. * * return the number of nodes. diff --git a/toxcore/Makefile.inc b/toxcore/Makefile.inc index 168f27553e6..8ef6dfcc412 100644 --- a/toxcore/Makefile.inc +++ b/toxcore/Makefile.inc @@ -48,6 +48,8 @@ libtoxcore_la_SOURCES = ../toxcore/ccompat.h \ ../toxcore/onion_client.c \ ../toxcore/forwarders.h \ ../toxcore/forwarders.c \ + ../toxcore/forwarding.h \ + ../toxcore/forwarding.c \ ../toxcore/TCP_client.h \ ../toxcore/TCP_client.c \ ../toxcore/TCP_server.h \ diff --git a/toxcore/TCP_client.c b/toxcore/TCP_client.c index 6eae84086a7..68a6820cb25 100644 --- a/toxcore/TCP_client.c +++ b/toxcore/TCP_client.c @@ -83,6 +83,9 @@ struct TCP_Client_Connection { tcp_onion_response_cb *onion_callback; void *onion_callback_object; + tcp_client_forwarding_cb *forwarding_callback; + void *forwarding_callback_object; + /* Can be used by user. */ void *custom_object; uint32_t custom_uint; @@ -675,6 +678,24 @@ void onion_response_handler(TCP_Client_Connection *con, tcp_onion_response_cb *o con->onion_callback_object = object; } +/* return 1 on success. + * return 0 if could not send packet. + * return -1 on failure (connection must be killed). + */ +int send_forward_request_tcp(TCP_Client_Connection *con, const uint8_t *data, uint16_t length) +{ + VLA(uint8_t, packet, 1 + length); + packet[0] = TCP_PACKET_FORWARD_REQUEST; + memcpy(packet + 1, data, length); + return write_packet_TCP_client_secure_connection(con, packet, SIZEOF_VLA(packet), 0); +} + +void forwarding_handler(TCP_Client_Connection *con, tcp_client_forwarding_cb *forwarding_callback, void *object) +{ + con->forwarding_callback = forwarding_callback; + con->forwarding_callback_object = object; +} + /* Create new TCP connection to ip_port/public_key */ TCP_Client_Connection *new_TCP_connection(const Mono_Time *mono_time, IP_Port ip_port, const uint8_t *public_key, @@ -898,6 +919,11 @@ static int handle_TCP_client_packet(TCP_Client_Connection *conn, const uint8_t * return 0; } + case TCP_PACKET_FORWARDING: { + conn->forwarding_callback(conn->forwarding_callback_object, data + 1, length - 1, conn->ip_port, userdata); + return 0; + } + default: { if (data[0] < NUM_RESERVED_PORTS) { return -1; diff --git a/toxcore/TCP_client.h b/toxcore/TCP_client.h index 9d43d642b37..c44b6194e92 100644 --- a/toxcore/TCP_client.h +++ b/toxcore/TCP_client.h @@ -77,6 +77,9 @@ void kill_TCP_connection(TCP_Client_Connection *tcp_connection); typedef int tcp_onion_response_cb(void *object, const uint8_t *data, uint16_t length, void *userdata); +typedef int tcp_client_forwarding_cb(void *object, const uint8_t *data, uint16_t length, IP_Port forwarder, + void *userdata); + /* return 1 on success. * return 0 if could not send packet. * return -1 on failure (connection must be killed). @@ -84,6 +87,9 @@ typedef int tcp_onion_response_cb(void *object, const uint8_t *data, uint16_t le int send_onion_request(TCP_Client_Connection *con, const uint8_t *data, uint16_t length); void onion_response_handler(TCP_Client_Connection *con, tcp_onion_response_cb *onion_callback, void *object); +int send_forward_request_tcp(TCP_Client_Connection *con, const uint8_t *data, uint16_t length); +void forwarding_handler(TCP_Client_Connection *con, tcp_client_forwarding_cb *forwarding_callback, void *object); + typedef int tcp_routing_response_cb(void *object, uint8_t connection_id, const uint8_t *public_key); typedef int tcp_routing_status_cb(void *object, uint32_t number, uint8_t connection_id, uint8_t status); diff --git a/toxcore/TCP_connection.c b/toxcore/TCP_connection.c index 06e9afec6bb..4a350a04389 100644 --- a/toxcore/TCP_connection.c +++ b/toxcore/TCP_connection.c @@ -57,6 +57,9 @@ struct TCP_Connections { tcp_onion_cb *tcp_onion_callback; void *tcp_onion_callback_object; + tcp_forwarding_cb *tcp_forwarding_callback; + void *tcp_forwarding_callback_object; + TCP_Proxy_Info proxy_info; bool onion_status; @@ -385,6 +388,28 @@ int get_random_tcp_onion_conn_number(TCP_Connections *tcp_c) return -1; } +/* Return TCP connection number of active TCP connection with ip_port. + * + * TODO(zugz): as for get_random_tcp_onion_conn_number, the return value isn't + * a stable reference to the TCP connection. + * + * return TCP connection number on success. + * return -1 on failure. + */ +int get_conn_number_by_ip_port(TCP_Connections *tcp_c, IP_Port ip_port) +{ + for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) { + const IP_Port conn_ip_port = tcp_con_ip_port(tcp_c->tcp_connections[i].connection); + + if (ipport_equal(&ip_port, &conn_ip_port) && + tcp_c->tcp_connections[i].status == TCP_CONN_CONNECTED) { + return i; + } + } + + return -1; +} + /* Send an onion packet via the TCP relay corresponding to tcp_connections_number. * * return 0 on success. @@ -408,6 +433,29 @@ int tcp_send_onion_request(TCP_Connections *tcp_c, unsigned int tcp_connections_ return -1; } +/* Send a forward request packet to the TCP relay corresponding to tcp_connections_number. + * + * return 0 on success. + * return -1 on failure. + */ +int tcp_send_forward_request(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *data, + uint16_t length) +{ + if (tcp_connections_number >= tcp_c->tcp_connections_length) { + return -1; + } + + if (tcp_c->tcp_connections[tcp_connections_number].status == TCP_CONN_CONNECTED) { + int ret = send_forward_request_tcp(tcp_c->tcp_connections[tcp_connections_number].connection, data, length); + + if (ret == 1) { + return 0; + } + } + + return -1; +} + /* Send an oob packet via the TCP relay corresponding to tcp_connections_number. * * return 0 on success. @@ -459,6 +507,15 @@ void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_onion_ tcp_c->tcp_onion_callback_object = object; } +/* Set the callback for TCP forwarding packets. + */ +void set_forwarding_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_forwarding_cb *tcp_forwarding_callback, + void *object) +{ + tcp_c->tcp_forwarding_callback = tcp_forwarding_callback; + tcp_c->tcp_forwarding_callback_object = object; +} + /* Encode tcp_connections_number as a custom ip_port. * * return ip_port. @@ -1092,6 +1149,26 @@ static int tcp_onion_callback(void *object, const uint8_t *data, uint16_t length return 0; } +static int tcp_forwarding_callback(void *object, const uint8_t *data, uint16_t length, IP_Port forwarder, + void *userdata) +{ + TCP_Connections *tcp_c = (TCP_Connections *)object; + + int conn_number = get_conn_number_by_ip_port(tcp_c, forwarder); + + if (conn_number == -1) { + return -1; + } + + const IP_Port forwarder_con = tcp_connections_number_to_ip_port(conn_number); + + if (tcp_c->tcp_forwarding_callback) { + tcp_c->tcp_forwarding_callback(tcp_c->tcp_forwarding_callback_object, data, length, forwarder_con, userdata); + } + + return 0; +} + /* Set callbacks for the TCP relay connection. * * return 0 on success. @@ -1110,6 +1187,7 @@ static int tcp_relay_set_callbacks(TCP_Connections *tcp_c, int tcp_connections_n tcp_con_set_custom_object(con, tcp_c); tcp_con_set_custom_uint(con, tcp_connections_number); onion_response_handler(con, &tcp_onion_callback, tcp_c); + forwarding_handler(con, &tcp_forwarding_callback, tcp_c); routing_response_handler(con, &tcp_response_callback, con); routing_status_handler(con, &tcp_status_callback, con); routing_data_handler(con, &tcp_conn_data_callback, con); diff --git a/toxcore/TCP_connection.h b/toxcore/TCP_connection.h index a9a51722e10..f05fbc58c91 100644 --- a/toxcore/TCP_connection.h +++ b/toxcore/TCP_connection.h @@ -118,6 +118,14 @@ int tcp_send_onion_request(TCP_Connections *tcp_c, unsigned int tcp_connections_ */ int set_tcp_onion_status(TCP_Connections *tcp_c, bool status); +/* Send a forward request packet to the TCP relay corresponding to tcp_connections_number. + * + * return 0 on success. + * return -1 on failure. + */ +int tcp_send_forward_request(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *data, + uint16_t length); + /* Send an oob packet via the TCP relay corresponding to tcp_connections_number. * * return 0 on success. @@ -138,6 +146,14 @@ typedef int tcp_onion_cb(void *object, const uint8_t *data, uint16_t length, voi */ void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_onion_cb *tcp_onion_callback, void *object); +typedef int tcp_forwarding_cb(void *object, const uint8_t *data, uint16_t length, IP_Port forwarder, void *userdata); + +/* Set the callback for TCP forwarding packets. + */ +void set_forwarding_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_forwarding_cb *tcp_forwarding_callback, + void *object); + + typedef int tcp_oob_cb(void *object, const uint8_t *public_key, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length, void *userdata); diff --git a/toxcore/TCP_server.c b/toxcore/TCP_server.c index 77b9f5c772c..6da6dbcd5f3 100644 --- a/toxcore/TCP_server.c +++ b/toxcore/TCP_server.c @@ -1000,7 +1000,6 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint return -1; } - default: { if (data[0] < NUM_RESERVED_PORTS) { return -1; @@ -1198,7 +1197,6 @@ TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, const uin set_callback_forward_request_not_inet(temp->forwarding, &handle_forward_request_tcp, temp); } - memcpy(temp->secret_key, secret_key, CRYPTO_SECRET_KEY_SIZE); crypto_derive_public_key(temp->public_key, temp->secret_key); diff --git a/toxcore/forwarders.c b/toxcore/forwarders.c new file mode 100644 index 00000000000..b4f37dabc44 --- /dev/null +++ b/toxcore/forwarders.c @@ -0,0 +1,265 @@ +/* + * Copyright © 2019 The TokTok team. + * + * This file is part of Tox, the free peer to peer instant messenger. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "forwarders.h" + +#include +#include + +#include "ccompat.h" +#include "forwarding.h" +#include "mono_time.h" + +#define FORWARDER_RESPONSE_TIMEOUT 10 +#define MAX_FORWARD_USE_NO_RESPONSE 2 + +#define NUM_FORWARDERS 8 + + +typedef struct Forwarder_Info { + IP_Port ip_port; + bool set; + uint16_t no_response_times; + uint64_t no_response_last_used; +} Forwarder_Info; + +struct Forwarders { + Mono_Time *mono_time; + DHT *dht; + Networking_Core *net; + Net_Crypto *c; + + Forwarder_Info forwarder_infos[NUM_FORWARDERS]; + + forwarded_cb *forwarded_cb; + void *forwarded_callback_object; +}; + +/* Put random forwarder in forwarder. + * Uses either a known good forwarder, or a random DHT node or TCP connection. + * + * return true on success, false otherwise. + */ +static bool get_forwarder(Forwarders *forwarders, IP_Port *forwarder) +{ + Forwarder_Info *forwarder_info = &forwarders->forwarder_infos[random_u32() % NUM_FORWARDERS]; + + if (forwarder_info->set && + (forwarder_info->no_response_times < MAX_FORWARD_USE_NO_RESPONSE || + !mono_time_is_timeout(forwarders->mono_time, + forwarder_info->no_response_last_used, FORWARDER_RESPONSE_TIMEOUT))) { + *forwarder = forwarder_info->ip_port; + + if (forwarder_info->no_response_times < MAX_FORWARD_USE_NO_RESPONSE) { + forwarder_info->no_response_last_used = mono_time_get(forwarders->mono_time); + } + + ++forwarder_info->no_response_times; + return true; + } + + if (dht_isconnected(forwarders->dht)) { + if (random_dht_node_ip_port(forwarders->dht, forwarder)) { + return true; + } + } + + /* TODO(zugz): need to tell TCP_connection that we want connections + * available if we can't connect to the DHT. Currently this is done by the + * onion, using set_tcp_onion_status, but when we disable the onion we + * will need to replicate this. + */ + int random_tcp = get_random_tcp_con_number(forwarders->c); + + if (random_tcp == -1) { + return false; + } + + *forwarder = tcp_connections_number_to_ip_port(random_tcp); + return true; +} + +/* Add new forwarder, or note that existing forwarder has responded. + */ +static void add_forwarder(Forwarders *forwarders, IP_Port forwarder) +{ + Forwarder_Info *update = &forwarders->forwarder_infos[random_u08() % NUM_FORWARDERS]; + + for (unsigned i = 0; i < NUM_FORWARDERS; ++i) { + Forwarder_Info *forwarder_info = &forwarders->forwarder_infos[i]; + + if (forwarder_info->set && ipport_equal(&forwarder_info->ip_port, &forwarder)) { + forwarder_info->no_response_times = 0; + return; + } + + if (!forwarder_info->set || + (forwarder_info->no_response_times > 0 && + (update->no_response_times == 0 || + forwarder_info->no_response_last_used < update->no_response_last_used))) { + update = forwarder_info; + } + } + + update->set = true; + update->ip_port = forwarder; + update->no_response_times = 0; +} + + +static int handle_plain_forwarding(Forwarders *forwarders, const uint8_t *data, uint16_t length, IP_Port forwarder, + void *userdata) +{ + IP_Port sender; + int ipport_length = unpack_ip_port(&sender, data, length, true, true); + + if (ipport_length == -1) { + return 1; + } + + if (!forwarders->forwarded_cb) { + return 1; + } + + if (forwarders->forwarded_cb(forwarders->forwarded_callback_object, sender, forwarder, data + ipport_length, + length - ipport_length, userdata)) { + add_forwarder(forwarders, forwarder); + } + + return 0; +} + +static int handle_plain_forwarding_cb(void *object, const uint8_t *data, uint16_t length, IP_Port forwarder, + void *userdata) +{ + return handle_plain_forwarding((Forwarders *)object, data, length, forwarder, userdata); +} + +static int handle_forwarding(void *object, IP_Port source, const uint8_t *packet, uint16_t length, void *userdata) +{ + Forwarders *forwarders = (Forwarders *)object; + + if (length > FORWARDING_MAX_PACKET_SIZE || length < 1 + CRYPTO_SYMMETRIC_KEY_SIZE + CRYPTO_MAC_SIZE) { + return 1; + } + + const uint8_t zero_nonce[CRYPTO_NONCE_SIZE] = {0}; + + const uint16_t plain_length = length - 1 - CRYPTO_SYMMETRIC_KEY_SIZE - CRYPTO_MAC_SIZE; + VLA(uint8_t, plain, plain_length); + + int len = decrypt_data_symmetric(packet + 1, zero_nonce, packet + 1 + CRYPTO_SYMMETRIC_KEY_SIZE, + length - 1 - CRYPTO_SYMMETRIC_KEY_SIZE, plain); + + if (len != plain_length) { + return 1; + } + + return handle_plain_forwarding(forwarders, plain, len, source, userdata); +} + +static bool send_forward_request_packet(Networking_Core *net, IP_Port forwarder, const uint8_t *data, uint16_t length) +{ + const uint16_t len = 1 + length; + + if (len > FORWARD_REQUEST_MAX_PACKET_SIZE) { + return false; + } + + VLA(uint8_t, packet, len); + packet[0] = NET_PACKET_FORWARD_REQUEST; + memcpy(packet + 1, data, length); + return (sendpacket(net, forwarder, packet, len) == len); +} + +bool send_forward_request_to(const Forwarders *forwarders, IP_Port forwarder, IP_Port dest, const uint8_t *data, + uint16_t length) +{ + VLA(uint8_t, request, length + MAX_PACKED_IPPORT_SIZE); + + int request_length = prepend_ip_port(request, SIZEOF_VLA(request), dest, data, length); + + unsigned int tcp_connection_number; + + if (ip_port_to_tcp_connections_number(forwarder, &tcp_connection_number)) { + return (tcp_send_forward_request(nc_get_tcp_c(forwarders->c), tcp_connection_number, request, + request_length) == 0); + } + + return send_forward_request_packet(forwarders->net, forwarder, request, request_length); +} + +bool send_forward_request(Forwarders *forwarders, IP_Port dest, const uint8_t *data, uint16_t length) +{ + IP_Port forwarder; + + if (!get_forwarder(forwarders, &forwarder)) { + return false; + } + + return send_forward_request_to(forwarders, forwarder, dest, data, length); +} + +void set_callback_forwarded(Forwarders *forwarders, forwarded_cb *function, void *object) +{ + forwarders->forwarded_cb = function; + forwarders->forwarded_callback_object = object; +} + +Forwarders *new_forwarders(Mono_Time *mono_time, DHT *dht, Net_Crypto *c) +{ + if (mono_time == nullptr || dht == nullptr || c == nullptr) { + return nullptr; + } + + Forwarders *forwarders = (Forwarders *)calloc(1, sizeof(Forwarders)); + + if (forwarders == nullptr) { + return nullptr; + } + + forwarders->mono_time = mono_time; + forwarders->dht = dht; + forwarders->c = c; + forwarders->net = dht_get_net(dht); + + networking_registerhandler(forwarders->net, NET_PACKET_FORWARDING, &handle_forwarding, forwarders); + + set_forwarding_packet_tcp_connection_callback(nc_get_tcp_c(forwarders->c), &handle_plain_forwarding_cb, forwarders); + + return forwarders; +} + +void kill_forwarders(Forwarders *forwarders) +{ + if (forwarders == nullptr) { + return; + } + + networking_registerhandler(forwarders->net, NET_PACKET_FORWARDING, nullptr, nullptr); + + set_forwarding_packet_tcp_connection_callback(nc_get_tcp_c(forwarders->c), nullptr, nullptr); + + free(forwarders); +} + +// TODO(zugz): saving? diff --git a/toxcore/forwarders.h b/toxcore/forwarders.h new file mode 100644 index 00000000000..7c080c79bca --- /dev/null +++ b/toxcore/forwarders.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2019 The TokTok team. + * + * This file is part of Tox, the free peer to peer instant messenger. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + */ +#ifndef C_TOXCORE_TOXCORE_FORWARDERS_H +#define C_TOXCORE_TOXCORE_FORWARDERS_H + +#include "DHT.h" +#include "net_crypto.h" +#include "mono_time.h" + +typedef struct Forwarders Forwarders; + +/* Send data of length to a random forwarder for forwarding to dest. + * Maximum length of data is MAX_FORWARD_DATA_SIZE. + * + * return true on success, false otherwise. + */ +bool send_forward_request(Forwarders *forwarders, IP_Port dest, const uint8_t *data, uint16_t length); + +/* Send data of length to forwarder for forwarding to dest. + * Maximum length of data is MAX_FORWARD_DATA_SIZE. + * + * return true on success, false otherwise. + */ +bool send_forward_request_to(const Forwarders *forwarders, IP_Port forwarder, IP_Port dest, const uint8_t *data, + uint16_t length); + +/* Set callback to handle a forwarded packet. + * + * Callback should return true if the forwarded packet is a reply to a + * previously sent packet (indicating that the forwarder is functional), false + * otherwise. + * + * To reply to the packet, callback should use send_forward_request_to() to send a reply + * forwarded via forwarder. + */ +typedef bool forwarded_cb(void *object, IP_Port sender, IP_Port forwarder, const uint8_t *data, uint16_t length, + void *userdata); +void set_callback_forwarded(Forwarders *forwarders, forwarded_cb *function, void *object); + +Forwarders *new_forwarders(Mono_Time *mono_time, DHT *dht, Net_Crypto *c); + +void kill_forwarders(Forwarders *forwarders); + +#endif diff --git a/toxcore/forwarding.c b/toxcore/forwarding.c index b6c6036ed57..3009e4aab23 100644 --- a/toxcore/forwarding.c +++ b/toxcore/forwarding.c @@ -117,11 +117,7 @@ bool handle_forward_request(const Forwarding *forwarding, IP_Port source, const static int handle_forward_request_direct(void *object, IP_Port source, const uint8_t *packet, uint16_t length, void *userdata) { - if (!handle_forward_request((Forwarding *)object, source, packet + 1, length - 1)) { - return 1; - } - - return 0; + return handle_forward_request((Forwarding *)object, source, packet + 1, length - 1) ? 0 : 1; } void set_callback_forward_request_not_inet(Forwarding *forwarding, forward_request_cb *function, void *object) diff --git a/toxcore/onion.c b/toxcore/onion.c index 792cbb95fce..bdf2b9090ab 100644 --- a/toxcore/onion.c +++ b/toxcore/onion.c @@ -683,6 +683,10 @@ Onion *new_onion(Mono_Time *mono_time, DHT *dht) new_symmetric_key(onion->secret_symmetric_key); onion->timestamp = mono_time_get(onion->mono_time); + // TODO(zugz): For now, Forwarding is treated as a component of Onion, + // while in reality it is a parallel system. Similarly for Forwarders and + // Onion_Client. This fits the architecture of the rest of tox, but is not + // intended to be permanent. onion->forwarding = new_forwarding(onion->net); if (onion->forwarding == nullptr) { diff --git a/toxcore/onion_client.c b/toxcore/onion_client.c index 944822095d4..fe80490f09f 100644 --- a/toxcore/onion_client.c +++ b/toxcore/onion_client.c @@ -31,6 +31,7 @@ #include #include +#include "forwarders.h" #include "LAN_discovery.h" #include "mono_time.h" #include "util.h" @@ -116,6 +117,7 @@ struct Onion_Client { DHT *dht; Net_Crypto *c; Networking_Core *net; + Forwarders *forwarders; Onion_Friend *friends_list; uint16_t num_friends; @@ -1889,6 +1891,13 @@ Onion_Client *new_onion_client(Mono_Time *mono_time, Net_Crypto *c) onion_c->dht = nc_get_dht(c); onion_c->net = dht_get_net(onion_c->dht); onion_c->c = c; + onion_c->forwarders = new_forwarders(onion_c->mono_time, onion_c->dht, onion_c->c); + + if (onion_c->forwarders == nullptr) { + free(onion_c); + return nullptr; + } + new_symmetric_key(onion_c->secret_symmetric_key); crypto_new_keypair(onion_c->temp_public_key, onion_c->temp_secret_key); networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_announce_response, onion_c); @@ -1908,6 +1917,7 @@ void kill_onion_client(Onion_Client *onion_c) ping_array_kill(onion_c->announce_ping_array); realloc_onion_friends(onion_c, 0); + kill_forwarders(onion_c->forwarders); networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, nullptr, nullptr); networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, nullptr, nullptr); oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, nullptr, nullptr); From 16aa18caef11461b0ce5f334c683c2d1b1898196 Mon Sep 17 00:00:00 2001 From: "zugz (tox)" Date: Fri, 16 Aug 2019 00:00:00 +0000 Subject: [PATCH 4/4] add forwarding auto_test --- CMakeLists.txt | 1 + auto_tests/Makefile.inc | 5 + auto_tests/forwarding_test.c | 196 +++++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 auto_tests/forwarding_test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index fd257ce68c6..fa10394a515 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -409,6 +409,7 @@ auto_test(dht MSVC_DONT_BUILD) auto_test(encryptsave) auto_test(file_transfer) auto_test(file_saving) +auto_test(forwarding) auto_test(friend_connection) auto_test(friend_request) auto_test(invalid_tcp_proxy) diff --git a/auto_tests/Makefile.inc b/auto_tests/Makefile.inc index 98f9db13151..f8725e71f8f 100644 --- a/auto_tests/Makefile.inc +++ b/auto_tests/Makefile.inc @@ -12,6 +12,7 @@ TESTS = \ encryptsave_test \ file_saving_test \ file_transfer_test \ + forwarding_test \ friend_connection_test \ friend_request_test \ invalid_tcp_proxy_test \ @@ -107,6 +108,10 @@ file_transfer_test_SOURCES = ../auto_tests/file_transfer_test.c file_transfer_test_CFLAGS = $(AUTOTEST_CFLAGS) file_transfer_test_LDADD = $(AUTOTEST_LDADD) +forwarding_test_SOURCES = ../auto_tests/forwarding_test.c +forwarding_test_CFLAGS = $(AUTOTEST_CFLAGS) +forwarding_test_LDADD = $(AUTOTEST_LDADD) + friend_connection_test_SOURCES = ../auto_tests/friend_connection_test.c friend_connection_test_CFLAGS = $(AUTOTEST_CFLAGS) friend_connection_test_LDADD = $(AUTOTEST_LDADD) diff --git a/auto_tests/forwarding_test.c b/auto_tests/forwarding_test.c new file mode 100644 index 00000000000..cc250761d59 --- /dev/null +++ b/auto_tests/forwarding_test.c @@ -0,0 +1,196 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "../toxcore/tox.h" +#include "../testing/misc_tools.h" +#include "../toxcore/mono_time.h" +#include "../toxcore/forwarding.h" +#include "../toxcore/forwarders.h" +#include "../toxcore/util.h" +#include "check_compat.h" + +#ifndef USE_IPV6 +#define USE_IPV6 1 +#endif + +static inline IP get_loopback(void) +{ + IP ip; +#if USE_IPV6 + ip.family = net_family_ipv6; + ip.ip.v6 = get_ip6_loopback(); +#else + ip.family = net_family_ipv4; + ip.ip.v4 = get_ip4_loopback(); +#endif + return ip; +} + +#define NUM_FORWARDER 5 +#define FORWARDER_TCP_RELAY_PORT 36570 +#define FORWARDING_BASE_PORT 36571 + +typedef struct Test_Data { + Forwarders *forwarders; + uint32_t send_back; + bool sent; + bool returned; +} Test_Data; + +static bool test_forwarded_cb(void *object, IP_Port sender, IP_Port forwarder, + const uint8_t *data, uint16_t length, void *userdata) +{ + Test_Data *test_data = (Test_Data *)object; + uint8_t *index = (uint8_t *)userdata; + + if (length == 11 && memcmp("hello: ", data, 7) == 0) { + uint8_t reply[11]; + memcpy(reply, "reply: ", 7); + memcpy(reply + 7, data + 7, 4); + send_forward_request_to(test_data->forwarders, forwarder, sender, reply, 11); + return false; + } + + if (length == 11 && memcmp("reply: ", data, 7) == 0) { + ck_assert_msg(memcmp(&test_data->send_back, data + 7, 4) == 0, + "[%u] got unexpected reply: %u", *index, (uint32_t)(data + 7)); + printf("[%u] got reply\n", *index); + test_data->returned = true; + return true; + } + + printf("[%u] got unexpected data\n", *index); + return false; +} + +static bool all_returned(Test_Data *test_data) +{ + for (uint32_t i = 0; i < NUM_FORWARDER; ++i) { + if (!test_data[i].returned) { + return false; + } + } + + return true; +} + +static void test_forwarding(void) +{ + assert(sizeof(char) == 1); + + uint32_t index[NUM_FORWARDER]; + Logger *logs[NUM_FORWARDER]; + Mono_Time *mono_times[NUM_FORWARDER]; + Networking_Core *nets[NUM_FORWARDER]; + DHT *dhts[NUM_FORWARDER]; + Net_Crypto *cs[NUM_FORWARDER]; + Forwarding *forwardings[NUM_FORWARDER]; + Forwarders *forwarderses[NUM_FORWARDER]; + + Test_Data test_data[NUM_FORWARDER] = {{0}}; + + IP ip = get_loopback(); + TCP_Proxy_Info inf = {{{{0}}}}; + + for (uint32_t i = 0; i < NUM_FORWARDER; ++i) { + index[i] = i + 1; + logs[i] = logger_new(); + logger_callback_log(logs[i], (logger_cb *)print_debug_log, nullptr, &index[i]); + mono_times[i] = mono_time_new(); + + if (false && i < NUM_FORWARDER / 2) { + nets[i] = new_networking_no_udp(logs[i]); + } else { + nets[i] = new_networking(logs[i], ip, FORWARDING_BASE_PORT + i); + } + + dhts[i] = new_dht(logs[i], mono_times[i], nets[i], true); + cs[i] = new_net_crypto(logs[i], mono_times[i], dhts[i], &inf); + forwardings[i] = new_forwarding(nets[i]); + forwarderses[i] = new_forwarders(mono_times[i], dhts[i], cs[i]); + ck_assert_msg((forwarderses[i] != nullptr), "Forwarders failed initializing."); + + test_data[i].forwarders = forwarderses[i]; + set_callback_forwarded(forwarderses[i], test_forwarded_cb, &test_data[i]); + } + + printf("testing forwarding via tcp relays and dht\n"); + struct Tox_Options *opts = tox_options_new(nullptr); + tox_options_set_tcp_port(opts, FORWARDER_TCP_RELAY_PORT); + IP_Port relay_ipport_tcp = {ip, net_htons(FORWARDER_TCP_RELAY_PORT)}; + Tox *relay = tox_new_log(opts, nullptr, nullptr); + ck_assert_msg(relay != nullptr, "Failed to create TCP relay"); + uint8_t dpk[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_dht_id(relay, dpk); + + printf("1-%d are TCP only, %d-%d DHT only\n", + NUM_FORWARDER / 2, NUM_FORWARDER / 2 + 1, NUM_FORWARDER); + + for (uint32_t i = 0; i < NUM_FORWARDER / 2; ++i) { + set_tcp_onion_status(nc_get_tcp_c(cs[i]), 1); + ck_assert_msg(add_tcp_relay(cs[i], relay_ipport_tcp, dpk) == 0, + "Failed to add TCP relay"); + }; + + IP_Port relay_ipport_udp = {ip, net_htons(tox_self_get_udp_port(relay, nullptr))}; + + for (uint32_t i = NUM_FORWARDER / 2; i < NUM_FORWARDER; ++i) { + dht_bootstrap(dhts[i % NUM_FORWARDER], relay_ipport_udp, dpk); + } + + do { + for (uint32_t i = 0; i < NUM_FORWARDER; ++i) { + if (!test_data[i].sent) { + uint32_t dest_i = random_u32() % NUM_FORWARDER; + IP_Port dest = {ip, net_htons(FORWARDING_BASE_PORT + dest_i)}; + uint8_t data[11]; + + memcpy(data, "hello: ", 7); + test_data[i].send_back = random_u32(); + *(uint32_t *)(data + 7) = test_data[i].send_back; + + if (send_forward_request(forwarderses[i], dest, data, 11)) { + printf("%u --> ? --> %u\n", i + 1, dest_i + 1); + test_data[i].sent = true; + } + } + + mono_time_update(mono_times[i]); + networking_poll(nets[i], &index[i]); + do_net_crypto(cs[i], &index[i]); + do_dht(dhts[i]); + } + + tox_iterate(relay, nullptr); + + c_sleep(50); + } while (!all_returned(test_data)); + + + for (uint32_t i = 0; i < NUM_FORWARDER; ++i) { + kill_forwarders(forwarderses[i]); + kill_forwarding(forwardings[i]); + kill_net_crypto(cs[i]); + kill_dht(dhts[i]); + kill_networking(nets[i]); + mono_time_free(mono_times[i]); + logger_kill(logs[i]); + } + + tox_kill(relay); +} + + +int main(void) +{ + setvbuf(stdout, nullptr, _IONBF, 0); + + test_forwarding(); + + return 0; +}