From 3acf43b76accbe0285e9cec0ff3536efeb3f8d8c Mon Sep 17 00:00:00 2001 From: irungentoo Date: Fri, 14 Mar 2014 14:12:35 -0400 Subject: [PATCH] A bit of work done on the TCP relay server. --- auto_tests/TCP_test.c | 117 ++++++++++++++++++++++++++++++++++++++++++ docs/TCP_Network.txt | 3 ++ docs/TODO | 2 +- toxcore/TCP_server.c | 94 +++++++++++++++++++++++---------- toxcore/TCP_server.h | 8 +-- 5 files changed, 192 insertions(+), 32 deletions(-) create mode 100644 auto_tests/TCP_test.c diff --git a/auto_tests/TCP_test.c b/auto_tests/TCP_test.c new file mode 100644 index 00000000..983b07d2 --- /dev/null +++ b/auto_tests/TCP_test.c @@ -0,0 +1,117 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "../toxcore/TCP_server.h" +#include "../toxcore/util.h" + +#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) +#define c_sleep(x) Sleep(1*x) +#else +#include +#define c_sleep(x) usleep(1000*x) +#endif + +#define NUM_PORTS 3 + +START_TEST(test_basic) +{ + uint16_t ports[NUM_PORTS] = {12345, 33445, 25643}; + uint8_t self_public_key[crypto_box_PUBLICKEYBYTES]; + uint8_t self_secret_key[crypto_box_SECRETKEYBYTES]; + crypto_box_keypair(self_public_key, self_secret_key); + TCP_Server *tcp_s = new_TCP_server(1, NUM_PORTS, ports, self_public_key, self_secret_key); + ck_assert_msg(tcp_s != NULL, "Failed to create TCP relay server"); + + sock_t sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + struct sockaddr_in6 addr6_loopback = {0}; + addr6_loopback.sin6_family = AF_INET6; + addr6_loopback.sin6_port = htons(ports[rand() % NUM_PORTS]); + addr6_loopback.sin6_addr = in6addr_loopback; + + int ret = connect(sock, (struct sockaddr *)&addr6_loopback, sizeof(addr6_loopback)); + ck_assert_msg(ret == 0, "Failed to connect to TCP relay server"); + + uint8_t f_public_key[crypto_box_PUBLICKEYBYTES]; + uint8_t f_secret_key[crypto_box_SECRETKEYBYTES]; + uint8_t f_nonce[crypto_box_NONCEBYTES]; + crypto_box_keypair(f_public_key, f_secret_key); + random_nonce(f_nonce); + + uint8_t t_secret_key[crypto_box_SECRETKEYBYTES]; + uint8_t handshake_plain[TCP_HANDSHAKE_PLAIN_SIZE]; + crypto_box_keypair(handshake_plain, t_secret_key); + memcpy(handshake_plain + crypto_box_PUBLICKEYBYTES, f_nonce, crypto_box_NONCEBYTES); + uint8_t handshake[TCP_CLIENT_HANDSHAKE_SIZE]; + memcpy(handshake, f_public_key, crypto_box_PUBLICKEYBYTES); + new_nonce(handshake + crypto_box_PUBLICKEYBYTES); + + ret = encrypt_data(self_public_key, f_secret_key, handshake + crypto_box_PUBLICKEYBYTES, handshake_plain, + TCP_HANDSHAKE_PLAIN_SIZE, handshake + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES); + ck_assert_msg(ret == TCP_CLIENT_HANDSHAKE_SIZE - (crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES), + "Encrypt failed."); + ck_assert_msg(send(sock, handshake, TCP_CLIENT_HANDSHAKE_SIZE - 1, 0) == TCP_CLIENT_HANDSHAKE_SIZE - 1, "send Failed."); + c_sleep(50); + do_TCP_server(tcp_s); + c_sleep(50); + do_TCP_server(tcp_s); + c_sleep(50); + ck_assert_msg(send(sock, handshake + (TCP_CLIENT_HANDSHAKE_SIZE - 1), 1, 0) == 1, "send Failed."); + c_sleep(50); + do_TCP_server(tcp_s); + c_sleep(50); + do_TCP_server(tcp_s); + c_sleep(50); + uint8_t response[TCP_SERVER_HANDSHAKE_SIZE]; + uint8_t response_plain[TCP_HANDSHAKE_PLAIN_SIZE]; + ck_assert_msg(recv(sock, response, TCP_SERVER_HANDSHAKE_SIZE, 0) == TCP_SERVER_HANDSHAKE_SIZE, "recv Failed."); + ret = decrypt_data(self_public_key, f_secret_key, response, response + crypto_box_NONCEBYTES, + TCP_SERVER_HANDSHAKE_SIZE - crypto_box_NONCEBYTES, response_plain); + ck_assert_msg(ret == TCP_HANDSHAKE_PLAIN_SIZE, "Decrypt Failed."); + uint8_t f_nonce_r[crypto_box_NONCEBYTES]; + uint8_t f_shared_key[crypto_box_BEFORENMBYTES]; + encrypt_precompute(response_plain, t_secret_key, f_shared_key); + memcpy(f_nonce_r, response_plain + crypto_box_BEFORENMBYTES, crypto_box_NONCEBYTES); + + +} +END_TEST + +#define DEFTESTCASE(NAME) \ + TCase *tc_##NAME = tcase_create(#NAME); \ + tcase_add_test(tc_##NAME, test_##NAME); \ + suite_add_tcase(s, tc_##NAME); + +#define DEFTESTCASE_SLOW(NAME, TIMEOUT) \ + DEFTESTCASE(NAME) \ + tcase_set_timeout(tc_##NAME, TIMEOUT); +Suite *TCP_suite(void) +{ + Suite *s = suite_create("TCP"); + + DEFTESTCASE_SLOW(basic, 5); + return s; +} + +int main(int argc, char *argv[]) +{ + srand((unsigned int) time(NULL)); + + Suite *TCP = TCP_suite(); + SRunner *test_runner = srunner_create(TCP); + + int number_failed = 0; + srunner_run_all(test_runner, CK_NORMAL); + number_failed = srunner_ntests_failed(test_runner); + + srunner_free(test_runner); + + return number_failed; +} diff --git a/docs/TCP_Network.txt b/docs/TCP_Network.txt index ce52b8e9..80cdfbdb 100644 --- a/docs/TCP_Network.txt +++ b/docs/TCP_Network.txt @@ -58,6 +58,9 @@ So if you would inspect the TCP stream you would see: [[uint16_t (length of data)][data]][[uint16_t (length of data)][data]][[uint16_t (length of data)][data]] +Note that both handshake packets don't have this format (the length for them is +always the same so we don't need to specify it.) + When the client connects to the server, he sends this packet: [public key of client (32 bytes)][nonce for the encrypted data [24 bytes]][encrypted with the private key of the client and public key of the diff --git a/docs/TODO b/docs/TODO index 6c0197db..f7ef8ade 100644 --- a/docs/TODO +++ b/docs/TODO @@ -41,7 +41,7 @@ Friend_requests.c: [NOT STARTED] Make the core save/datafile portable across client versions/different processor architectures. -[NOT STARTED] A way for people to connect to people on Tox if they are behind a bad NAT that +[IN PROGRESS] A way for people to connect to people on Tox if they are behind a bad NAT that blocks UDP (or is just unpunchable) (docs/TCP_Network.txt) [NEEDS TESTING] Make the save made with tox_save_encrypted(...) harder to brute force. diff --git a/toxcore/TCP_server.c b/toxcore/TCP_server.c index 602d0000..df56f490 100644 --- a/toxcore/TCP_server.c +++ b/toxcore/TCP_server.c @@ -118,7 +118,7 @@ static uint16_t read_length(sock_t sock) int count; ioctl(sock, FIONREAD, &count); - if (count >= sizeof(uint16_t)) { + if ((unsigned int)count >= sizeof(uint16_t)) { uint16_t length; int len = recv(sock, &length, sizeof(uint16_t), 0); @@ -169,7 +169,7 @@ static void kill_TCP_connection(TCP_Secure_Connection *con) memset(con, 0, sizeof(TCP_Secure_Connection)); } -/* return 0 if everything went well. +/* return 1 if everything went well. * return -1 if the connection must be killed. */ static int handle_TCP_handshake(TCP_Secure_Connection *con, uint8_t *data, uint16_t length, uint8_t *self_secret_key) @@ -209,35 +209,23 @@ static int handle_TCP_handshake(TCP_Secure_Connection *con, uint8_t *data, uint1 encrypt_precompute(plain, temp_secret_key, con->shared_key); con->status = TCP_STATUS_UNCONFIRMED; - return 0; + return 1; } -/* return 0 if everything went well. +/* return 1 if connection handshake was handled correctly. + * return 0 if we didn't get it yet. * return -1 if the connection must be killed. */ static int read_connection_handshake(TCP_Secure_Connection *con, uint8_t *self_secret_key) { - while (1) { - if (con->next_packet_length == 0) { - uint16_t len = read_length(con->sock); + uint8_t data[TCP_CLIENT_HANDSHAKE_SIZE]; + int len = 0; - if (len == 0) - break; - - if (len != TCP_CLIENT_HANDSHAKE_SIZE) - return -1; - - con->next_packet_length = len; - } else { - uint8_t data[con->next_packet_length]; - - if (read_TCP_packet(con->sock, data, con->next_packet_length) != -1) { - return handle_TCP_handshake(con, data, con->next_packet_length, self_secret_key); - } else { - break; - } - } + if ((len = read_TCP_packet(con->sock, data, TCP_CLIENT_HANDSHAKE_SIZE)) != -1) { + return handle_TCP_handshake(con, data, len, self_secret_key); } + + return 0; } /* return 1 on success @@ -253,8 +241,17 @@ static int accept_connection(TCP_Server *TCP_server, sock_t sock) return 0; } - printf("accepted %u\n", sock); + TCP_Secure_Connection *conn = + &TCP_server->incomming_connection_queue[TCP_server->incomming_connection_queue_index % MAX_INCOMMING_CONNECTIONS]; + if (conn->status != TCP_STATUS_NO_STATUS) + kill_TCP_connection(conn); + + conn->status = TCP_STATUS_CONNECTED; + conn->sock = sock; + conn->next_packet_length = 0; + + ++TCP_server->incomming_connection_queue_index; return 1; } @@ -288,7 +285,7 @@ TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, uint16_t if (num_sockets == 0 || ports == NULL) return NULL; - TCP_Server *temp = calloc(1, sizeof(Networking_Core)); + TCP_Server *temp = calloc(1, sizeof(TCP_Server)); if (temp == NULL) return NULL; @@ -324,22 +321,63 @@ TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, uint16_t return temp; } -void do_TCP_server(TCP_Server *TCP_server) +static void do_TCP_accept_new(TCP_Server *TCP_server) { uint32_t i; for (i = 0; i < TCP_server->num_listening_socks; ++i) { struct sockaddr_storage addr; - int addrlen = sizeof(addr); + unsigned int addrlen = sizeof(addr); sock_t sock; do { sock = accept(TCP_server->socks_listening[i], (struct sockaddr *)&addr, &addrlen); - //TODO } while (accept_connection(TCP_server, sock)); } } +static void do_TCP_incomming(TCP_Server *TCP_server) +{ + uint32_t i; + + for (i = 0; i < MAX_INCOMMING_CONNECTIONS; ++i) { + if (TCP_server->incomming_connection_queue[i].status != TCP_STATUS_CONNECTED) + continue; + + int ret = read_connection_handshake(&TCP_server->incomming_connection_queue[i], TCP_server->secret_key); + + if (ret == -1) { + kill_TCP_connection(&TCP_server->incomming_connection_queue[i]); + } else if (ret == 1) { + TCP_Secure_Connection *conn_old = &TCP_server->incomming_connection_queue[i]; + TCP_Secure_Connection *conn_new = + &TCP_server->unconfirmed_connection_queue[TCP_server->unconfirmed_connection_queue_index % MAX_INCOMMING_CONNECTIONS]; + + if (conn_new->status != TCP_STATUS_NO_STATUS) + kill_TCP_connection(conn_new); + + memcpy(conn_new, conn_old, sizeof(TCP_Secure_Connection)); + memset(conn_old, 0, sizeof(TCP_Secure_Connection)); + ++TCP_server->unconfirmed_connection_queue_index; + } + } +} + +static void do_TCP_unconfirmed(TCP_Server *TCP_server) +{ + uint32_t i; + + for (i = 0; i < MAX_INCOMMING_CONNECTIONS; ++i) { + if (TCP_server->incomming_connection_queue[i].status != TCP_STATUS_CONNECTED) + continue; + } +} +void do_TCP_server(TCP_Server *TCP_server) +{ + do_TCP_accept_new(TCP_server); + do_TCP_incomming(TCP_server); +} + void kill_TCP_server(TCP_Server *TCP_server) { uint32_t i; diff --git a/toxcore/TCP_server.h b/toxcore/TCP_server.h index 573108af..89ccb50f 100644 --- a/toxcore/TCP_server.h +++ b/toxcore/TCP_server.h @@ -22,12 +22,12 @@ #include "net_crypto.h" -#define TCP_MAX_BACKLOG 128 +#define MAX_INCOMMING_CONNECTIONS 32 + +#define TCP_MAX_BACKLOG MAX_INCOMMING_CONNECTIONS #define MAX_PACKET_SIZE 8192 -#define MAX_INCOMMING_CONNECTIONS 32 - #define TCP_HANDSHAKE_PLAIN_SIZE (crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES) #define TCP_SERVER_HANDSHAKE_SIZE (crypto_box_NONCEBYTES + TCP_HANDSHAKE_PLAIN_SIZE + crypto_box_MACBYTES) #define TCP_CLIENT_HANDSHAKE_SIZE (crypto_box_PUBLICKEYBYTES + TCP_SERVER_HANDSHAKE_SIZE) @@ -57,6 +57,8 @@ typedef struct { uint8_t secret_key[crypto_box_SECRETKEYBYTES]; TCP_Secure_Connection incomming_connection_queue[MAX_INCOMMING_CONNECTIONS]; uint16_t incomming_connection_queue_index; + TCP_Secure_Connection unconfirmed_connection_queue[MAX_INCOMMING_CONNECTIONS]; + uint16_t unconfirmed_connection_queue_index; } TCP_Server; /* Create new TCP server instance.