diff --git a/auto_tests/TCP_test.c b/auto_tests/TCP_test.c index 20bf6592b..de37bb970 100644 --- a/auto_tests/TCP_test.c +++ b/auto_tests/TCP_test.c @@ -10,6 +10,8 @@ #include #include "../toxcore/TCP_server.h" +#include "../toxcore/TCP_client.h" + #include "../toxcore/util.h" #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) @@ -21,7 +23,7 @@ #define NUM_PORTS 3 -uint16_t ports[NUM_PORTS] = {12345, 33445, 25643}; +uint16_t ports[NUM_PORTS] = {1234, 33445, 25643}; START_TEST(test_basic) { @@ -30,6 +32,7 @@ START_TEST(test_basic) 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, NULL); ck_assert_msg(tcp_s != NULL, "Failed to create TCP relay server"); + ck_assert_msg(tcp_s->num_listening_socks == NUM_PORTS, "Failed to bind to all ports"); sock_t sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); struct sockaddr_in6 addr6_loopback = {0}; @@ -204,6 +207,7 @@ START_TEST(test_some) 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, NULL); ck_assert_msg(tcp_s != NULL, "Failed to create TCP relay server"); + ck_assert_msg(tcp_s->num_listening_socks == NUM_PORTS, "Failed to bind to all ports"); struct sec_TCP_con *con1 = new_TCP_con(tcp_s); struct sec_TCP_con *con2 = new_TCP_con(tcp_s); @@ -287,6 +291,69 @@ START_TEST(test_some) } END_TEST +START_TEST(test_client) +{ + unix_time_update(); + 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, NULL); + ck_assert_msg(tcp_s != NULL, "Failed to create TCP relay server"); + ck_assert_msg(tcp_s->num_listening_socks == NUM_PORTS, "Failed to bind to all ports"); + + uint8_t f_public_key[crypto_box_PUBLICKEYBYTES]; + uint8_t f_secret_key[crypto_box_SECRETKEYBYTES]; + crypto_box_keypair(f_public_key, f_secret_key); + IP_Port ip_port_tcp_s; + + 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); + c_sleep(50); + do_TCP_connection(conn); + ck_assert_msg(conn->status == TCP_CLIENT_UNCONFIRMED, "Wrong status. Expected: %u, is: %u", TCP_CLIENT_UNCONFIRMED, + conn->status); + c_sleep(50); + do_TCP_server(tcp_s); + c_sleep(50); + do_TCP_connection(conn); + ck_assert_msg(conn->status == TCP_CLIENT_CONFIRMED, "Wrong status. Expected: %u, is: %u", TCP_CLIENT_CONFIRMED, + conn->status); +} +END_TEST + +START_TEST(test_client_invalid) +{ + unix_time_update(); + 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); + + uint8_t f_public_key[crypto_box_PUBLICKEYBYTES]; + uint8_t f_secret_key[crypto_box_SECRETKEYBYTES]; + crypto_box_keypair(f_public_key, f_secret_key); + IP_Port ip_port_tcp_s; + + 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); + c_sleep(50); + do_TCP_connection(conn); + ck_assert_msg(conn->status == TCP_CLIENT_CONNECTING, "Wrong status. Expected: %u, is: %u", TCP_CLIENT_CONNECTING, + conn->status); + c_sleep(5000); + do_TCP_connection(conn); + ck_assert_msg(conn->status == TCP_CLIENT_CONNECTING, "Wrong status. Expected: %u, is: %u", TCP_CLIENT_CONNECTING, + conn->status); + c_sleep(6000); + do_TCP_connection(conn); + ck_assert_msg(conn->status == TCP_CLIENT_DISCONNECTED, "Wrong status. Expected: %u, is: %u", TCP_CLIENT_DISCONNECTED, + conn->status); +} +END_TEST + #define DEFTESTCASE(NAME) \ TCase *tc_##NAME = tcase_create(#NAME); \ tcase_add_test(tc_##NAME, test_##NAME); \ @@ -301,6 +368,8 @@ Suite *TCP_suite(void) DEFTESTCASE_SLOW(basic, 5); DEFTESTCASE_SLOW(some, 10); + DEFTESTCASE_SLOW(client, 10); + DEFTESTCASE_SLOW(client_invalid, 15); return s; } diff --git a/toxcore/TCP_client.c b/toxcore/TCP_client.c new file mode 100644 index 000000000..29b4e7e77 --- /dev/null +++ b/toxcore/TCP_client.c @@ -0,0 +1,237 @@ +/* +* TCP_client.c -- Implementation of the TCP relay client part of Tox. +* +* Copyright (C) 2014 Tox project All Rights Reserved. +* +* This file is part of Tox. +* +* 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 . +* +*/ + + +#include "TCP_client.h" + +#if !defined(_WIN32) && !defined(__WIN32__) && !defined (WIN32) +#include +#endif + +#include "util.h" + +/* return 1 on success + * return 0 on failure + */ +static int connect_sock_to(sock_t sock, IP_Port ip_port) +{ + struct sockaddr_storage addr = {0}; + size_t addrsize; + + if (ip_port.ip.family == AF_INET) { + struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; + + addrsize = sizeof(struct sockaddr_in); + addr4->sin_family = AF_INET; + addr4->sin_addr = ip_port.ip.ip4.in_addr; + addr4->sin_port = ip_port.port; + } else if (ip_port.ip.family == AF_INET6) { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; + + addrsize = sizeof(struct sockaddr_in6); + addr6->sin6_family = AF_INET6; + addr6->sin6_addr = ip_port.ip.ip6.in6_addr; + addr6->sin6_port = ip_port.port; + } else { + return 0; + } + + /* nonblocking socket, connect will never return success */ + connect(sock, (struct sockaddr *)&addr, addrsize); + return 1; +} +/* return 0 on success. + * return -1 on failure. + */ +static int generate_handshake(TCP_Client_Connection *TCP_conn, uint8_t *self_public_key, uint8_t *self_secret_key) +{ + 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); + new_nonce(TCP_conn->last_packet + crypto_box_PUBLICKEYBYTES); + int len = encrypt_data_fast(TCP_conn->shared_key, TCP_conn->last_packet + crypto_box_PUBLICKEYBYTES, plain, + sizeof(plain), TCP_conn->last_packet + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES); + + if (len != sizeof(plain) + crypto_box_MACBYTES) + return -1; + + TCP_conn->last_packet_length = crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES + sizeof(plain) + crypto_box_MACBYTES; + TCP_conn->last_packet_sent = 0; + return 0; +} + +/* data must be of length TCP_SERVER_HANDSHAKE_SIZE + * + * return 0 on success. + * return -1 on failure. + */ +static int handle_handshake(TCP_Client_Connection *TCP_conn, uint8_t *data) +{ + uint8_t plain[crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES]; + int len = decrypt_data_fast(TCP_conn->shared_key, data, data + crypto_box_NONCEBYTES, + TCP_SERVER_HANDSHAKE_SIZE - crypto_box_NONCEBYTES, plain); + + if (len != sizeof(plain)) + return -1; + + memcpy(TCP_conn->recv_nonce, plain + crypto_box_PUBLICKEYBYTES, crypto_box_NONCEBYTES); + encrypt_precompute(plain, TCP_conn->temp_secret_key, TCP_conn->shared_key); + memset(TCP_conn->temp_secret_key, 0, crypto_box_SECRETKEYBYTES); + return 0; +} + +/* return 0 if pending data was sent completely + * return -1 if it wasn't + */ +static int send_pending_data(TCP_Client_Connection *con) +{ + if (con->last_packet_length == 0) { + return 0; + } + + uint16_t left = con->last_packet_length - con->last_packet_sent; + int len = send(con->sock, con->last_packet + con->last_packet_sent, left, MSG_NOSIGNAL); + + if (len <= 0) + return -1; + + if (len == left) { + con->last_packet_length = 0; + con->last_packet_sent = 0; + return 0; + } + + if (len > left) + return -1; + + con->last_packet_sent += len; + return -1; +} + +static int send_disconnect_packet(TCP_Client_Connection *TCP_connection) +{ + + return 0; +} + +/* Create new TCP connection to ip_port/public_key + */ +TCP_Client_Connection *new_TCP_connection(IP_Port ip_port, uint8_t *public_key, uint8_t *self_public_key, + uint8_t *self_secret_key) +{ + if (networking_at_startup() != 0) { + return NULL; + } + + if (ip_port.ip.family != AF_INET && ip_port.ip.family != AF_INET6) + return NULL; + + sock_t sock = socket(ip_port.ip.family, SOCK_STREAM, IPPROTO_TCP); + + if (!sock_valid(sock)) { + printf("fail1 %u\n", sock); + return NULL; + } + + if (!(set_socket_nonblock(sock) && connect_sock_to(sock, ip_port))) { + kill_sock(sock); + return NULL; + } + + TCP_Client_Connection *temp = calloc(sizeof(TCP_Client_Connection), 1); + + if (temp == NULL) { + kill_sock(sock); + return NULL; + } + + temp->status = TCP_CLIENT_CONNECTING; + temp->sock = sock; + memcpy(temp->public_key, public_key, crypto_box_PUBLICKEYBYTES); + + if (generate_handshake(temp, self_public_key, self_secret_key) == -1) { + kill_sock(sock); + free(temp); + return NULL; + } + + temp->kill_at = unix_time() + TCP_CONNECTION_TIMEOUT; + + return temp; +} + +static int do_confirmed_TCP(TCP_Client_Connection *TCP_connection) +{ + + return 0; +} + +/* Run the TCP connection + */ +void do_TCP_connection(TCP_Client_Connection *TCP_connection) +{ + unix_time_update(); + + if (TCP_connection->status == TCP_CLIENT_DISCONNECTED) { + return; + } + + if (TCP_connection->status == TCP_CLIENT_CONNECTING) { + if (send_pending_data(TCP_connection) == 0) { + TCP_connection->status = TCP_CLIENT_UNCONFIRMED; + } + } + + if (TCP_connection->status == TCP_CLIENT_UNCONFIRMED) { + uint8_t data[TCP_SERVER_HANDSHAKE_SIZE]; + int len = read_TCP_packet(TCP_connection->sock, data, sizeof(data)); + + if (sizeof(data) == len) { + if (handle_handshake(TCP_connection, data) == 0) { + TCP_connection->status = TCP_CLIENT_CONFIRMED; + } else { + TCP_connection->kill_at = 0; + TCP_connection->status = TCP_CLIENT_DISCONNECTED; + } + } + } + + if (TCP_connection->status == TCP_CLIENT_CONFIRMED) { + do_confirmed_TCP(TCP_connection); + } + + if (TCP_connection->kill_at <= unix_time()) { + TCP_connection->status = TCP_CLIENT_DISCONNECTED; + } +} + +/* Kill the TCP connection + */ +void kill_TCP_connection(TCP_Client_Connection *TCP_connection) +{ + kill_sock(TCP_connection->sock); + memset(TCP_connection, 0, sizeof(TCP_Client_Connection)); + free(TCP_connection); +} \ No newline at end of file diff --git a/toxcore/TCP_client.h b/toxcore/TCP_client.h new file mode 100644 index 000000000..eb2aa39c5 --- /dev/null +++ b/toxcore/TCP_client.h @@ -0,0 +1,78 @@ +/* +* TCP_client.h -- Implementation of the TCP relay client part of Tox. +* +* Copyright (C) 2014 Tox project All Rights Reserved. +* +* This file is part of Tox. +* +* 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 TCP_CLIENT_H +#define TCP_CLIENT_H + +#include "net_crypto.h" +#include "TCP_server.h" + +#define TCP_CONNECTION_TIMEOUT 10 + +enum { + TCP_CLIENT_NO_STATUS, + TCP_CLIENT_CONNECTING, + TCP_CLIENT_UNCONFIRMED, + TCP_CLIENT_CONFIRMED, + TCP_CLIENT_DISCONNECTED, +}; +typedef struct { + uint8_t status; + sock_t sock; + uint8_t public_key[crypto_box_PUBLICKEYBYTES]; /* public key of the server */ + 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]; + uint16_t next_packet_length; + + uint8_t temp_secret_key[crypto_box_SECRETKEYBYTES]; + + uint8_t last_packet[2 + MAX_PACKET_SIZE]; + uint16_t last_packet_length; + uint16_t last_packet_sent; + + uint64_t kill_at; + + uint64_t last_pinged; + uint64_t ping_id; +} TCP_Client_Connection; + +/* Create new TCP connection to ip_port/public_key + */ +TCP_Client_Connection *new_TCP_connection(IP_Port ip_port, uint8_t *public_key, uint8_t *self_public_key, + uint8_t *self_secret_key); + +/* Run the TCP connection + */ +void do_TCP_connection(TCP_Client_Connection *TCP_connection); + +/* Kill the TCP connection + */ +void kill_TCP_connection(TCP_Client_Connection *TCP_connection); + +int get_TCP_connection_status(TCP_Client_Connection *TCP_connection); + +int read_TCP_connection(TCP_Client_Connection *TCP_connection, uint8_t *data); + + +#endif diff --git a/toxcore/TCP_server.c b/toxcore/TCP_server.c index a69ee4e29..4eed12099 100644 --- a/toxcore/TCP_server.c +++ b/toxcore/TCP_server.c @@ -157,11 +157,14 @@ static int get_TCP_connection_index(TCP_Server *TCP_server, uint8_t *public_key) return -1; } -/* return length on success +/* Read the next two bytes in TCP stream then convert them to + * length (host byte order). + * + * return length on success * return 0 if nothing has been read from socket. * return ~0 on failure. */ -static uint16_t read_length(sock_t sock) +uint16_t read_TCP_length(sock_t sock) { #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) unsigned long count = 0; @@ -173,7 +176,7 @@ static uint16_t read_length(sock_t sock) if ((unsigned int)count >= sizeof(uint16_t)) { uint16_t length; - int len = recv(sock, (uint8_t *)&length, sizeof(uint16_t), 0); + int len = recv(sock, (uint8_t *)&length, sizeof(uint16_t), MSG_NOSIGNAL); if (len != sizeof(uint16_t)) { fprintf(stderr, "FAIL recv packet\n"); @@ -192,10 +195,12 @@ static uint16_t read_length(sock_t sock) return 0; } -/* return length on success - * return -1 on failure +/* Read length bytes from socket. + * + * return length on success + * return -1 on failure/no data in buffer. */ -static int read_TCP_packet(sock_t sock, uint8_t *data, uint16_t length) +int read_TCP_packet(sock_t sock, uint8_t *data, uint16_t length) { #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) unsigned long count = 0; @@ -206,7 +211,7 @@ static int read_TCP_packet(sock_t sock, uint8_t *data, uint16_t length) #endif if (count >= length) { - int len = recv(sock, data, length, 0); + int len = recv(sock, data, length, MSG_NOSIGNAL); if (len != length) { fprintf(stderr, "FAIL recv packet\n"); @@ -226,7 +231,7 @@ static int read_TCP_packet(sock_t sock, uint8_t *data, uint16_t length) static int read_packet_TCP_secure_connection(TCP_Secure_Connection *con, uint8_t *data, uint16_t max_len) { if (con->next_packet_length == 0) { - uint16_t len = read_length(con->sock); + uint16_t len = read_TCP_length(con->sock); if (len == (uint16_t)~0) return -1; @@ -268,7 +273,7 @@ static int send_pending_data(TCP_Secure_Connection *con) } uint16_t left = con->last_packet_length - con->last_packet_sent; - int len = send(con->sock, con->last_packet + con->last_packet_sent, left, 0); + int len = send(con->sock, con->last_packet + con->last_packet_sent, left, MSG_NOSIGNAL); if (len <= 0) return -1; @@ -310,7 +315,7 @@ static int write_packet_TCP_secure_connection(TCP_Secure_Connection *con, uint8_ increment_nonce(con->sent_nonce); - len = send(con->sock, packet, sizeof(packet), 0); + len = send(con->sock, packet, sizeof(packet), MSG_NOSIGNAL); if ((unsigned int)len == sizeof(packet)) return 1; @@ -368,7 +373,7 @@ static int handle_TCP_handshake(TCP_Secure_Connection *con, uint8_t *data, uint1 if (len != TCP_HANDSHAKE_PLAIN_SIZE + crypto_box_MACBYTES) return -1; - if (TCP_SERVER_HANDSHAKE_SIZE != send(con->sock, response, TCP_SERVER_HANDSHAKE_SIZE, 0)) + if (TCP_SERVER_HANDSHAKE_SIZE != send(con->sock, response, TCP_SERVER_HANDSHAKE_SIZE, MSG_NOSIGNAL)) return -1; encrypt_precompute(plain, temp_secret_key, con->shared_key); @@ -740,16 +745,15 @@ TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, uint16_t if (num_sockets == 0 || ports == NULL) return NULL; + if (networking_at_startup() != 0) { + return NULL; + } + TCP_Server *temp = calloc(1, sizeof(TCP_Server)); if (temp == NULL) return NULL; - if (onion) { - temp->onion = onion; - set_callback_handle_recv_1(onion, &handle_onion_recv_1, temp); - } - temp->socks_listening = calloc(num_sockets, sizeof(sock_t)); if (temp->socks_listening == NULL) { @@ -776,6 +780,16 @@ TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, uint16_t } } + if (temp->num_listening_socks == 0) { + free(temp); + return NULL; + } + + if (onion) { + temp->onion = onion; + set_callback_handle_recv_1(onion, &handle_onion_recv_1, temp); + } + memcpy(temp->public_key, public_key, crypto_box_PUBLICKEYBYTES); memcpy(temp->secret_key, secret_key, crypto_box_SECRETKEYBYTES); return temp; diff --git a/toxcore/TCP_server.h b/toxcore/TCP_server.h index e8101cc29..3ae0eba43 100644 --- a/toxcore/TCP_server.h +++ b/toxcore/TCP_server.h @@ -20,9 +20,16 @@ * */ +#ifndef TCP_SERVER_H +#define TCP_SERVER_H + #include "net_crypto.h" #include "onion.h" +#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) +#define MSG_NOSIGNAL 0 +#endif + #define MAX_INCOMMING_CONNECTIONS 32 #define TCP_MAX_BACKLOG MAX_INCOMMING_CONNECTIONS @@ -116,3 +123,21 @@ void do_TCP_server(TCP_Server *TCP_server); /* Kill the TCP server */ void kill_TCP_server(TCP_Server *TCP_server); + +/* Read the next two bytes in TCP stream then convert them to + * length (host byte order). + * + * return length on success + * return 0 if nothing has been read from socket. + * return ~0 on failure. + */ +uint16_t read_TCP_length(sock_t sock); + +/* Read length bytes from socket. + * + * return length on success + * return -1 on failure/no data in buffer. + */ +int read_TCP_packet(sock_t sock, uint8_t *data, uint16_t length); + +#endif diff --git a/toxcore/network.c b/toxcore/network.c index ac5c0a722..47afab8e4 100644 --- a/toxcore/network.c +++ b/toxcore/network.c @@ -500,7 +500,7 @@ int networking_wait_cleanup(Networking_Core *net, uint8_t *data) } uint8_t at_startup_ran = 0; -static int at_startup(void) +int networking_at_startup(void) { if (at_startup_ran != 0) return 0; @@ -556,7 +556,7 @@ Networking_Core *new_networking(IP ip, uint16_t port) return NULL; } - if (at_startup() != 0) + if (networking_at_startup() != 0) return NULL; Networking_Core *temp = calloc(1, sizeof(Networking_Core)); @@ -946,7 +946,7 @@ int addr_resolve(const char *address, IP *to, IP *extra) hints.ai_family = family; hints.ai_socktype = SOCK_DGRAM; // type of socket Tox uses. - if (at_startup() != 0) + if (networking_at_startup() != 0) return 0; rc = getaddrinfo(address, NULL, &hints, &server); diff --git a/toxcore/network.h b/toxcore/network.h index fa39ae59b..42ade8009 100644 --- a/toxcore/network.h +++ b/toxcore/network.h @@ -307,6 +307,13 @@ typedef struct { uint64_t send_fail_eagain; } Networking_Core; +/* Run this before creating sockets. + * + * return 0 on success + * return -1 on failure + */ +int networking_at_startup(void); + /* Check if socket is valid. * * return 1 if valid