Added TCP_client.

Some work done on the TCP part.
This commit is contained in:
irungentoo 2014-04-06 20:51:03 -04:00
parent ef744ebbc2
commit bd0d24fc9c
No known key found for this signature in database
GPG Key ID: 10349DC9BED89E98
7 changed files with 450 additions and 20 deletions

View File

@ -10,6 +10,8 @@
#include <time.h>
#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;
}

237
toxcore/TCP_client.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#include "TCP_client.h"
#if !defined(_WIN32) && !defined(__WIN32__) && !defined (WIN32)
#include <sys/ioctl.h>
#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);
}

78
toxcore/TCP_client.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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