summaryrefslogtreecommitdiff
path: root/libs/libtox/src/toxcore/TCP_connection.c
diff options
context:
space:
mode:
authoraunsane <aunsane@gmail.com>2017-12-15 01:05:56 +0300
committeraunsane <aunsane@gmail.com>2017-12-15 01:05:56 +0300
commite124aa3611f38573898aa79c6eabe77bc874e58f (patch)
tree819464260f758bbc002b23c0c8a77f93751dcb42 /libs/libtox/src/toxcore/TCP_connection.c
parentbbd9647d47f20d10b39570def918a0ac68c305c9 (diff)
preparing to build tox from sources
Diffstat (limited to 'libs/libtox/src/toxcore/TCP_connection.c')
-rw-r--r--libs/libtox/src/toxcore/TCP_connection.c1491
1 files changed, 1491 insertions, 0 deletions
diff --git a/libs/libtox/src/toxcore/TCP_connection.c b/libs/libtox/src/toxcore/TCP_connection.c
new file mode 100644
index 0000000000..251594912a
--- /dev/null
+++ b/libs/libtox/src/toxcore/TCP_connection.c
@@ -0,0 +1,1491 @@
+/*
+ * Handles TCP relay connections between two Tox clients.
+ */
+
+/*
+ * Copyright © 2016-2017 The TokTok team.
+ * Copyright © 2015 Tox project.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "TCP_connection.h"
+
+#include "util.h"
+
+#include <assert.h>
+
+
+struct TCP_Connections {
+ DHT *dht;
+
+ uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE];
+ uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE];
+
+ TCP_Connection_to *connections;
+ uint32_t connections_length; /* Length of connections array. */
+
+ TCP_con *tcp_connections;
+ uint32_t tcp_connections_length; /* Length of tcp_connections array. */
+
+ int (*tcp_data_callback)(void *object, int id, const uint8_t *data, uint16_t length, void *userdata);
+ void *tcp_data_callback_object;
+
+ int (*tcp_oob_callback)(void *object, const uint8_t *public_key, unsigned int tcp_connections_number,
+ const uint8_t *data, uint16_t length, void *userdata);
+ void *tcp_oob_callback_object;
+
+ int (*tcp_onion_callback)(void *object, const uint8_t *data, uint16_t length, void *userdata);
+ void *tcp_onion_callback_object;
+
+ TCP_Proxy_Info proxy_info;
+
+ bool onion_status;
+ uint16_t onion_num_conns;
+};
+
+
+const uint8_t *tcp_connections_public_key(const TCP_Connections *tcp_c)
+{
+ return tcp_c->self_public_key;
+}
+
+
+/* Set the size of the array to num.
+ *
+ * return -1 if realloc fails.
+ * return 0 if it succeeds.
+ */
+#define realloc_tox_array(array, element_type, num, temp_pointer) \
+ (num \
+ ? (temp_pointer = (element_type *)realloc( \
+ array, \
+ (num) * sizeof(element_type)), \
+ temp_pointer ? (array = temp_pointer, 0) : -1) \
+ : (free(array), array = NULL, 0))
+
+
+/* return 1 if the connections_number is not valid.
+ * return 0 if the connections_number is valid.
+ */
+static bool connections_number_not_valid(const TCP_Connections *tcp_c, int connections_number)
+{
+ if ((unsigned int)connections_number >= tcp_c->connections_length) {
+ return 1;
+ }
+
+ if (tcp_c->connections == NULL) {
+ return 1;
+ }
+
+ if (tcp_c->connections[connections_number].status == TCP_CONN_NONE) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* return 1 if the tcp_connections_number is not valid.
+ * return 0 if the tcp_connections_number is valid.
+ */
+static bool tcp_connections_number_not_valid(const TCP_Connections *tcp_c, int tcp_connections_number)
+{
+ if ((unsigned int)tcp_connections_number >= tcp_c->tcp_connections_length) {
+ return 1;
+ }
+
+ if (tcp_c->tcp_connections == NULL) {
+ return 1;
+ }
+
+ if (tcp_c->tcp_connections[tcp_connections_number].status == TCP_CONN_NONE) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Create a new empty connection.
+ *
+ * return -1 on failure.
+ * return connections_number on success.
+ */
+static int create_connection(TCP_Connections *tcp_c)
+{
+ uint32_t i;
+
+ for (i = 0; i < tcp_c->connections_length; ++i) {
+ if (tcp_c->connections[i].status == TCP_CONN_NONE) {
+ return i;
+ }
+ }
+
+ int id = -1;
+
+ TCP_Connection_to *temp_pointer;
+
+ if (realloc_tox_array(tcp_c->connections, TCP_Connection_to, tcp_c->connections_length + 1,
+ temp_pointer) == 0) {
+ id = tcp_c->connections_length;
+ ++tcp_c->connections_length;
+ memset(&(tcp_c->connections[id]), 0, sizeof(TCP_Connection_to));
+ }
+
+ return id;
+}
+
+/* Create a new empty tcp connection.
+ *
+ * return -1 on failure.
+ * return tcp_connections_number on success.
+ */
+static int create_tcp_connection(TCP_Connections *tcp_c)
+{
+ uint32_t i;
+
+ for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
+ if (tcp_c->tcp_connections[i].status == TCP_CONN_NONE) {
+ return i;
+ }
+ }
+
+ int id = -1;
+
+ TCP_con *temp_pointer;
+
+ if (realloc_tox_array(tcp_c->tcp_connections, TCP_con, tcp_c->tcp_connections_length + 1, temp_pointer) == 0) {
+ id = tcp_c->tcp_connections_length;
+ ++tcp_c->tcp_connections_length;
+ memset(&(tcp_c->tcp_connections[id]), 0, sizeof(TCP_con));
+ }
+
+ return id;
+}
+
+/* Wipe a connection.
+ *
+ * return -1 on failure.
+ * return 0 on success.
+ */
+static int wipe_connection(TCP_Connections *tcp_c, int connections_number)
+{
+ if (connections_number_not_valid(tcp_c, connections_number)) {
+ return -1;
+ }
+
+ uint32_t i;
+ memset(&(tcp_c->connections[connections_number]), 0 , sizeof(TCP_Connection_to));
+
+ for (i = tcp_c->connections_length; i != 0; --i) {
+ if (tcp_c->connections[i - 1].status != TCP_CONN_NONE) {
+ break;
+ }
+ }
+
+ if (tcp_c->connections_length != i) {
+ tcp_c->connections_length = i;
+ TCP_Connection_to *temp_pointer;
+ realloc_tox_array(tcp_c->connections, TCP_Connection_to, tcp_c->connections_length, temp_pointer);
+ }
+
+ return 0;
+}
+
+/* Wipe a connection.
+ *
+ * return -1 on failure.
+ * return 0 on success.
+ */
+static int wipe_tcp_connection(TCP_Connections *tcp_c, int tcp_connections_number)
+{
+ if (tcp_connections_number_not_valid(tcp_c, tcp_connections_number)) {
+ return -1;
+ }
+
+ uint32_t i;
+ memset(&(tcp_c->tcp_connections[tcp_connections_number]), 0 , sizeof(TCP_con));
+
+ for (i = tcp_c->tcp_connections_length; i != 0; --i) {
+ if (tcp_c->tcp_connections[i - 1].status != TCP_CONN_NONE) {
+ break;
+ }
+ }
+
+ if (tcp_c->tcp_connections_length != i) {
+ tcp_c->tcp_connections_length = i;
+ TCP_con *temp_pointer;
+ realloc_tox_array(tcp_c->tcp_connections, TCP_con, tcp_c->tcp_connections_length, temp_pointer);
+ }
+
+ return 0;
+}
+
+static TCP_Connection_to *get_connection(const TCP_Connections *tcp_c, int connections_number)
+{
+ if (connections_number_not_valid(tcp_c, connections_number)) {
+ return 0;
+ }
+
+ return &tcp_c->connections[connections_number];
+}
+
+static TCP_con *get_tcp_connection(const TCP_Connections *tcp_c, int tcp_connections_number)
+{
+ if (tcp_connections_number_not_valid(tcp_c, tcp_connections_number)) {
+ return 0;
+ }
+
+ return &tcp_c->tcp_connections[tcp_connections_number];
+}
+
+/* Send a packet to the TCP connection.
+ *
+ * return -1 on failure.
+ * return 0 on success.
+ */
+int send_packet_tcp_connection(TCP_Connections *tcp_c, int connections_number, const uint8_t *packet, uint16_t length)
+{
+ TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
+
+ if (!con_to) {
+ return -1;
+ }
+
+ // TODO(irungentoo): detect and kill bad relays.
+ // TODO(irungentoo): thread safety?
+ unsigned int i;
+ int ret = -1;
+
+ bool limit_reached = 0;
+
+ for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ uint32_t tcp_con_num = con_to->connections[i].tcp_connection;
+ uint8_t status = con_to->connections[i].status;
+ uint8_t connection_id = con_to->connections[i].connection_id;
+
+ if (tcp_con_num && status == TCP_CONNECTIONS_STATUS_ONLINE) {
+ tcp_con_num -= 1;
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_con_num);
+
+ if (!tcp_con) {
+ continue;
+ }
+
+ ret = send_data(tcp_con->connection, connection_id, packet, length);
+
+ if (ret == 0) {
+ limit_reached = 1;
+ }
+
+ if (ret == 1) {
+ break;
+ }
+ }
+ }
+
+ if (ret == 1) {
+ return 0;
+ }
+
+ if (!limit_reached) {
+ ret = 0;
+
+ /* Send oob packets to all relays tied to the connection. */
+ for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ uint32_t tcp_con_num = con_to->connections[i].tcp_connection;
+ uint8_t status = con_to->connections[i].status;
+
+ if (tcp_con_num && status == TCP_CONNECTIONS_STATUS_REGISTERED) {
+ tcp_con_num -= 1;
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_con_num);
+
+ if (!tcp_con) {
+ continue;
+ }
+
+ if (send_oob_packet(tcp_con->connection, con_to->public_key, packet, length) == 1) {
+ ret += 1;
+ }
+ }
+ }
+
+ if (ret >= 1) {
+ return 0;
+ }
+
+ return -1;
+ }
+
+ return -1;
+}
+
+/* Return a random TCP connection number for use in send_tcp_onion_request.
+ *
+ * TODO(irungentoo): This number is just the index of an array that the elements
+ * can change without warning.
+ *
+ * return TCP connection number on success.
+ * return -1 on failure.
+ */
+int get_random_tcp_onion_conn_number(TCP_Connections *tcp_c)
+{
+ unsigned int i, r = rand();
+
+ for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
+ unsigned int index = ((i + r) % tcp_c->tcp_connections_length);
+
+ if (tcp_c->tcp_connections[index].onion && tcp_c->tcp_connections[index].status == TCP_CONN_CONNECTED) {
+ return index;
+ }
+ }
+
+ return -1;
+}
+
+/* Send an onion packet via the TCP relay corresponding to tcp_connections_number.
+ *
+ * return 0 on success.
+ * return -1 on failure.
+ */
+int tcp_send_onion_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_onion_request(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.
+ * return -1 on failure.
+ */
+int tcp_send_oob_packet(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *public_key,
+ const uint8_t *packet, uint16_t length)
+{
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ if (tcp_con->status != TCP_CONN_CONNECTED) {
+ return -1;
+ }
+
+ int ret = send_oob_packet(tcp_con->connection, public_key, packet, length);
+
+ if (ret == 1) {
+ return 0;
+ }
+
+ return -1;
+}
+
+/* Set the callback for TCP data packets.
+ */
+void set_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_data_callback)(void *object, int id,
+ const uint8_t *data, uint16_t length, void *userdata), void *object)
+{
+ tcp_c->tcp_data_callback = tcp_data_callback;
+ tcp_c->tcp_data_callback_object = object;
+}
+
+/* Set the callback for TCP onion packets.
+ */
+void set_oob_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_oob_callback)(void *object,
+ const uint8_t *public_key, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length, void *userdata),
+ void *object)
+{
+ tcp_c->tcp_oob_callback = tcp_oob_callback;
+ tcp_c->tcp_oob_callback_object = object;
+}
+
+/* Set the callback for TCP oob data packets.
+ */
+void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_onion_callback)(void *object,
+ const uint8_t *data, uint16_t length, void *userdata), void *object)
+{
+ tcp_c->tcp_onion_callback = tcp_onion_callback;
+ tcp_c->tcp_onion_callback_object = object;
+}
+
+
+/* Find the TCP connection with public_key.
+ *
+ * return connections_number on success.
+ * return -1 on failure.
+ */
+static int find_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key)
+{
+ unsigned int i;
+
+ for (i = 0; i < tcp_c->connections_length; ++i) {
+ TCP_Connection_to *con_to = get_connection(tcp_c, i);
+
+ if (con_to) {
+ if (public_key_cmp(con_to->public_key, public_key) == 0) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+/* Find the TCP connection to a relay with relay_pk.
+ *
+ * return connections_number on success.
+ * return -1 on failure.
+ */
+static int find_tcp_connection_relay(TCP_Connections *tcp_c, const uint8_t *relay_pk)
+{
+ unsigned int i;
+
+ for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
+
+ if (tcp_con) {
+ if (tcp_con->status == TCP_CONN_SLEEPING) {
+ if (public_key_cmp(tcp_con->relay_pk, relay_pk) == 0) {
+ return i;
+ }
+ } else {
+ if (public_key_cmp(tcp_con->connection->public_key, relay_pk) == 0) {
+ return i;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+/* 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().
+ *
+ * id is the id in the callbacks for that connection.
+ *
+ * return connections_number on success.
+ * return -1 on failure.
+ */
+int new_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key, int id)
+{
+ if (find_tcp_connection_to(tcp_c, public_key) != -1) {
+ return -1;
+ }
+
+ int connections_number = create_connection(tcp_c);
+
+ if (connections_number == -1) {
+ return -1;
+ }
+
+ TCP_Connection_to *con_to = &tcp_c->connections[connections_number];
+
+ con_to->status = TCP_CONN_VALID;
+ memcpy(con_to->public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
+ con_to->id = id;
+
+ return connections_number;
+}
+
+/* return 0 on success.
+ * return -1 on failure.
+ */
+int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number)
+{
+ TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
+
+ if (!con_to) {
+ return -1;
+ }
+
+ unsigned int i;
+
+ for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ if (con_to->connections[i].tcp_connection) {
+ unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1;
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ continue;
+ }
+
+ if (tcp_con->status == TCP_CONN_CONNECTED) {
+ send_disconnect_request(tcp_con->connection, con_to->connections[i].connection_id);
+ }
+
+ if (con_to->connections[i].status == TCP_CONNECTIONS_STATUS_ONLINE) {
+ --tcp_con->lock_count;
+
+ if (con_to->status == TCP_CONN_SLEEPING) {
+ --tcp_con->sleep_count;
+ }
+ }
+ }
+ }
+
+ return wipe_connection(tcp_c, connections_number);
+}
+
+/* Set connection status.
+ *
+ * status of 1 means we are using the connection.
+ * status of 0 means we are not using it.
+ *
+ * Unused tcp connections will be disconnected from but kept in case they are needed.
+ *
+ * return 0 on success.
+ * return -1 on failure.
+ */
+int set_tcp_connection_to_status(TCP_Connections *tcp_c, int connections_number, bool status)
+{
+ TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
+
+ if (!con_to) {
+ return -1;
+ }
+
+ if (status) {
+ /* Connection is unsleeping. */
+ if (con_to->status != TCP_CONN_SLEEPING) {
+ return -1;
+ }
+
+ unsigned int i;
+
+ for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ if (con_to->connections[i].tcp_connection) {
+ unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1;
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ continue;
+ }
+
+ if (tcp_con->status == TCP_CONN_SLEEPING) {
+ tcp_con->unsleep = 1;
+ }
+ }
+ }
+
+ con_to->status = TCP_CONN_VALID;
+ return 0;
+ }
+
+ /* Connection is going to sleep. */
+ if (con_to->status != TCP_CONN_VALID) {
+ return -1;
+ }
+
+ unsigned int i;
+
+ for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ if (con_to->connections[i].tcp_connection) {
+ unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1;
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ continue;
+ }
+
+ if (con_to->connections[i].status == TCP_CONNECTIONS_STATUS_ONLINE) {
+ ++tcp_con->sleep_count;
+ }
+ }
+ }
+
+ con_to->status = TCP_CONN_SLEEPING;
+ return 0;
+}
+
+static bool tcp_connection_in_conn(TCP_Connection_to *con_to, unsigned int tcp_connections_number)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* return index on success.
+ * return -1 on failure.
+ */
+static int add_tcp_connection_to_conn(TCP_Connection_to *con_to, unsigned int tcp_connections_number)
+{
+ unsigned int i;
+
+ if (tcp_connection_in_conn(con_to, tcp_connections_number)) {
+ return -1;
+ }
+
+ for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ if (con_to->connections[i].tcp_connection == 0) {
+ con_to->connections[i].tcp_connection = tcp_connections_number + 1;
+ con_to->connections[i].status = TCP_CONNECTIONS_STATUS_NONE;
+ con_to->connections[i].connection_id = 0;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* return index on success.
+ * return -1 on failure.
+ */
+static int rm_tcp_connection_from_conn(TCP_Connection_to *con_to, unsigned int tcp_connections_number)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) {
+ con_to->connections[i].tcp_connection = 0;
+ con_to->connections[i].status = TCP_CONNECTIONS_STATUS_NONE;
+ con_to->connections[i].connection_id = 0;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* return number of online connections on success.
+ * return -1 on failure.
+ */
+static unsigned int online_tcp_connection_from_conn(TCP_Connection_to *con_to)
+{
+ unsigned int i, count = 0;
+
+ for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ if (con_to->connections[i].tcp_connection) {
+ if (con_to->connections[i].status == TCP_CONNECTIONS_STATUS_ONLINE) {
+ ++count;
+ }
+ }
+ }
+
+ return count;
+}
+
+/* return index on success.
+ * return -1 on failure.
+ */
+static int set_tcp_connection_status(TCP_Connection_to *con_to, unsigned int tcp_connections_number,
+ unsigned int status, uint8_t connection_id)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) {
+
+ if (con_to->connections[i].status == status) {
+ return -1;
+ }
+
+ con_to->connections[i].status = status;
+ con_to->connections[i].connection_id = connection_id;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* Kill a TCP relay connection.
+ *
+ * return 0 on success.
+ * return -1 on failure.
+ */
+static int kill_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number)
+{
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ unsigned int i;
+
+ for (i = 0; i < tcp_c->connections_length; ++i) {
+ TCP_Connection_to *con_to = get_connection(tcp_c, i);
+
+ if (con_to) {
+ rm_tcp_connection_from_conn(con_to, tcp_connections_number);
+ }
+ }
+
+ if (tcp_con->onion) {
+ --tcp_c->onion_num_conns;
+ }
+
+ kill_TCP_connection(tcp_con->connection);
+
+ return wipe_tcp_connection(tcp_c, tcp_connections_number);
+}
+
+static int reconnect_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number)
+{
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ if (tcp_con->status == TCP_CONN_SLEEPING) {
+ return -1;
+ }
+
+ IP_Port ip_port = tcp_con->connection->ip_port;
+ uint8_t relay_pk[CRYPTO_PUBLIC_KEY_SIZE];
+ memcpy(relay_pk, tcp_con->connection->public_key, CRYPTO_PUBLIC_KEY_SIZE);
+ kill_TCP_connection(tcp_con->connection);
+ tcp_con->connection = new_TCP_connection(ip_port, relay_pk, tcp_c->self_public_key, tcp_c->self_secret_key,
+ &tcp_c->proxy_info);
+
+ if (!tcp_con->connection) {
+ kill_tcp_relay_connection(tcp_c, tcp_connections_number);
+ return -1;
+ }
+
+ unsigned int i;
+
+ for (i = 0; i < tcp_c->connections_length; ++i) {
+ TCP_Connection_to *con_to = get_connection(tcp_c, i);
+
+ if (con_to) {
+ set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_NONE, 0);
+ }
+ }
+
+ if (tcp_con->onion) {
+ --tcp_c->onion_num_conns;
+ tcp_con->onion = 0;
+ }
+
+ tcp_con->lock_count = 0;
+ tcp_con->sleep_count = 0;
+ tcp_con->connected_time = 0;
+ tcp_con->status = TCP_CONN_VALID;
+ tcp_con->unsleep = 0;
+
+ return 0;
+}
+
+static int sleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number)
+{
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ if (tcp_con->status != TCP_CONN_CONNECTED) {
+ return -1;
+ }
+
+ if (tcp_con->lock_count != tcp_con->sleep_count) {
+ return -1;
+ }
+
+ tcp_con->ip_port = tcp_con->connection->ip_port;
+ memcpy(tcp_con->relay_pk, tcp_con->connection->public_key, CRYPTO_PUBLIC_KEY_SIZE);
+
+ kill_TCP_connection(tcp_con->connection);
+ tcp_con->connection = NULL;
+
+ unsigned int i;
+
+ for (i = 0; i < tcp_c->connections_length; ++i) {
+ TCP_Connection_to *con_to = get_connection(tcp_c, i);
+
+ if (con_to) {
+ set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_NONE, 0);
+ }
+ }
+
+ if (tcp_con->onion) {
+ --tcp_c->onion_num_conns;
+ tcp_con->onion = 0;
+ }
+
+ tcp_con->lock_count = 0;
+ tcp_con->sleep_count = 0;
+ tcp_con->connected_time = 0;
+ tcp_con->status = TCP_CONN_SLEEPING;
+ tcp_con->unsleep = 0;
+
+ return 0;
+}
+
+static int unsleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number)
+{
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ if (tcp_con->status != TCP_CONN_SLEEPING) {
+ return -1;
+ }
+
+ tcp_con->connection = new_TCP_connection(tcp_con->ip_port, tcp_con->relay_pk, tcp_c->self_public_key,
+ tcp_c->self_secret_key, &tcp_c->proxy_info);
+
+ if (!tcp_con->connection) {
+ kill_tcp_relay_connection(tcp_c, tcp_connections_number);
+ return -1;
+ }
+
+ tcp_con->lock_count = 0;
+ tcp_con->sleep_count = 0;
+ tcp_con->connected_time = 0;
+ tcp_con->status = TCP_CONN_VALID;
+ tcp_con->unsleep = 0;
+ return 0;
+}
+
+/* Send a TCP routing request.
+ *
+ * return 0 on success.
+ * return -1 on failure.
+ */
+static int send_tcp_relay_routing_request(TCP_Connections *tcp_c, int tcp_connections_number, uint8_t *public_key)
+{
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ if (tcp_con->status == TCP_CONN_SLEEPING) {
+ return -1;
+ }
+
+ if (send_routing_request(tcp_con->connection, public_key) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int tcp_response_callback(void *object, uint8_t connection_id, const uint8_t *public_key)
+{
+ TCP_Client_Connection *TCP_client_con = (TCP_Client_Connection *)object;
+ TCP_Connections *tcp_c = (TCP_Connections *)TCP_client_con->custom_object;
+
+ unsigned int tcp_connections_number = TCP_client_con->custom_uint;
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ int connections_number = find_tcp_connection_to(tcp_c, public_key);
+
+ if (connections_number == -1) {
+ return -1;
+ }
+
+ TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
+
+ if (con_to == NULL) {
+ return -1;
+ }
+
+ if (set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_REGISTERED, connection_id) == -1) {
+ return -1;
+ }
+
+ set_tcp_connection_number(tcp_con->connection, connection_id, connections_number);
+
+ return 0;
+}
+
+static int tcp_status_callback(void *object, uint32_t number, uint8_t connection_id, uint8_t status)
+{
+ TCP_Client_Connection *TCP_client_con = (TCP_Client_Connection *)object;
+ TCP_Connections *tcp_c = (TCP_Connections *)TCP_client_con->custom_object;
+
+ unsigned int tcp_connections_number = TCP_client_con->custom_uint;
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+ TCP_Connection_to *con_to = get_connection(tcp_c, number);
+
+ if (!con_to || !tcp_con) {
+ return -1;
+ }
+
+ if (status == 1) {
+ if (set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_REGISTERED, connection_id) == -1) {
+ return -1;
+ }
+
+ --tcp_con->lock_count;
+
+ if (con_to->status == TCP_CONN_SLEEPING) {
+ --tcp_con->sleep_count;
+ }
+ } else if (status == 2) {
+ if (set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_ONLINE, connection_id) == -1) {
+ return -1;
+ }
+
+ ++tcp_con->lock_count;
+
+ if (con_to->status == TCP_CONN_SLEEPING) {
+ ++tcp_con->sleep_count;
+ }
+ }
+
+ return 0;
+}
+
+static int tcp_conn_data_callback(void *object, uint32_t number, uint8_t connection_id, const uint8_t *data,
+ uint16_t length, void *userdata)
+{
+ if (length == 0) {
+ return -1;
+ }
+
+ TCP_Client_Connection *TCP_client_con = (TCP_Client_Connection *)object;
+ TCP_Connections *tcp_c = (TCP_Connections *)TCP_client_con->custom_object;
+
+ unsigned int tcp_connections_number = TCP_client_con->custom_uint;
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ TCP_Connection_to *con_to = get_connection(tcp_c, number);
+
+ if (!con_to) {
+ return -1;
+ }
+
+ if (tcp_c->tcp_data_callback) {
+ tcp_c->tcp_data_callback(tcp_c->tcp_data_callback_object, con_to->id, data, length, userdata);
+ }
+
+ return 0;
+}
+
+static int tcp_conn_oob_callback(void *object, const uint8_t *public_key, const uint8_t *data, uint16_t length,
+ void *userdata)
+{
+ if (length == 0) {
+ return -1;
+ }
+
+ TCP_Client_Connection *TCP_client_con = (TCP_Client_Connection *)object;
+ TCP_Connections *tcp_c = (TCP_Connections *)TCP_client_con->custom_object;
+
+ unsigned int tcp_connections_number = TCP_client_con->custom_uint;
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ /* TODO(irungentoo): optimize */
+ int connections_number = find_tcp_connection_to(tcp_c, public_key);
+
+ TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
+
+ if (con_to && tcp_connection_in_conn(con_to, tcp_connections_number)) {
+ return tcp_conn_data_callback(object, connections_number, 0, data, length, userdata);
+ }
+
+ if (tcp_c->tcp_oob_callback) {
+ tcp_c->tcp_oob_callback(tcp_c->tcp_oob_callback_object, public_key, tcp_connections_number, data, length, userdata);
+ }
+
+ return 0;
+}
+
+static int tcp_onion_callback(void *object, const uint8_t *data, uint16_t length, void *userdata)
+{
+ TCP_Connections *tcp_c = (TCP_Connections *)object;
+
+ if (tcp_c->tcp_onion_callback) {
+ tcp_c->tcp_onion_callback(tcp_c->tcp_onion_callback_object, data, length, userdata);
+ }
+
+ return 0;
+}
+
+/* Set callbacks for the TCP relay connection.
+ *
+ * return 0 on success.
+ * return -1 on failure.
+ */
+static int tcp_relay_set_callbacks(TCP_Connections *tcp_c, int tcp_connections_number)
+{
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ TCP_Client_Connection *con = tcp_con->connection;
+
+ con->custom_object = tcp_c;
+ con->custom_uint = tcp_connections_number;
+ onion_response_handler(con, &tcp_onion_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);
+ oob_data_handler(con, &tcp_conn_oob_callback, con);
+
+ return 0;
+}
+
+static int tcp_relay_on_online(TCP_Connections *tcp_c, int tcp_connections_number)
+{
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ unsigned int i, sent = 0;
+
+ for (i = 0; i < tcp_c->connections_length; ++i) {
+ TCP_Connection_to *con_to = get_connection(tcp_c, i);
+
+ if (con_to) {
+ if (tcp_connection_in_conn(con_to, tcp_connections_number)) {
+ if (send_tcp_relay_routing_request(tcp_c, tcp_connections_number, con_to->public_key) == 0) {
+ ++sent;
+ }
+ }
+ }
+ }
+
+ tcp_relay_set_callbacks(tcp_c, tcp_connections_number);
+ tcp_con->status = TCP_CONN_CONNECTED;
+
+ /* If this connection isn't used by any connection, we don't need to wait for them to come online. */
+ if (sent) {
+ tcp_con->connected_time = unix_time();
+ } else {
+ tcp_con->connected_time = 0;
+ }
+
+ if (tcp_c->onion_status && tcp_c->onion_num_conns < NUM_ONION_TCP_CONNECTIONS) {
+ tcp_con->onion = 1;
+ ++tcp_c->onion_num_conns;
+ }
+
+ return 0;
+}
+
+static int add_tcp_relay_instance(TCP_Connections *tcp_c, IP_Port ip_port, const uint8_t *relay_pk)
+{
+ if (ip_port.ip.family == TCP_INET) {
+ ip_port.ip.family = TOX_AF_INET;
+ } else if (ip_port.ip.family == TCP_INET6) {
+ ip_port.ip.family = TOX_AF_INET6;
+ }
+
+ if (ip_port.ip.family != TOX_AF_INET && ip_port.ip.family != TOX_AF_INET6) {
+ return -1;
+ }
+
+ int tcp_connections_number = create_tcp_connection(tcp_c);
+
+ if (tcp_connections_number == -1) {
+ return -1;
+ }
+
+ TCP_con *tcp_con = &tcp_c->tcp_connections[tcp_connections_number];
+
+ tcp_con->connection = new_TCP_connection(ip_port, relay_pk, tcp_c->self_public_key, tcp_c->self_secret_key,
+ &tcp_c->proxy_info);
+
+ if (!tcp_con->connection) {
+ return -1;
+ }
+
+ tcp_con->status = TCP_CONN_VALID;
+
+ return tcp_connections_number;
+}
+
+/* Add a TCP relay to the TCP_Connections instance.
+ *
+ * return 0 on success.
+ * return -1 on failure.
+ */
+int add_tcp_relay_global(TCP_Connections *tcp_c, IP_Port ip_port, const uint8_t *relay_pk)
+{
+ int tcp_connections_number = find_tcp_connection_relay(tcp_c, relay_pk);
+
+ if (tcp_connections_number != -1) {
+ return -1;
+ }
+
+ if (add_tcp_relay_instance(tcp_c, ip_port, relay_pk) == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Add a TCP relay tied to a connection.
+ *
+ * return 0 on success.
+ * return -1 on failure.
+ */
+int add_tcp_number_relay_connection(TCP_Connections *tcp_c, int connections_number, unsigned int tcp_connections_number)
+{
+ TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
+
+ if (!con_to) {
+ return -1;
+ }
+
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ if (con_to->status != TCP_CONN_SLEEPING && tcp_con->status == TCP_CONN_SLEEPING) {
+ tcp_con->unsleep = 1;
+ }
+
+ if (add_tcp_connection_to_conn(con_to, tcp_connections_number) == -1) {
+ return -1;
+ }
+
+ if (tcp_con->status == TCP_CONN_CONNECTED) {
+ if (send_tcp_relay_routing_request(tcp_c, tcp_connections_number, con_to->public_key) == 0) {
+ tcp_con->connected_time = unix_time();
+ }
+ }
+
+ return 0;
+}
+
+/* Add a TCP relay tied to a connection.
+ *
+ * This should be called with the same relay by two peers who want to create a TCP connection with each other.
+ *
+ * return 0 on success.
+ * return -1 on failure.
+ */
+int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, IP_Port ip_port, const uint8_t *relay_pk)
+{
+ TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
+
+ if (!con_to) {
+ return -1;
+ }
+
+ int tcp_connections_number = find_tcp_connection_relay(tcp_c, relay_pk);
+
+ if (tcp_connections_number != -1) {
+ return add_tcp_number_relay_connection(tcp_c, connections_number, tcp_connections_number);
+ }
+
+ if (online_tcp_connection_from_conn(con_to) >= RECOMMENDED_FRIEND_TCP_CONNECTIONS) {
+ return -1;
+ }
+
+ tcp_connections_number = add_tcp_relay_instance(tcp_c, ip_port, relay_pk);
+
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
+
+ if (!tcp_con) {
+ return -1;
+ }
+
+ if (add_tcp_connection_to_conn(con_to, tcp_connections_number) == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* return number of online tcp relays tied to the connection on success.
+ * return 0 on failure.
+ */
+unsigned int tcp_connection_to_online_tcp_relays(TCP_Connections *tcp_c, int connections_number)
+{
+ TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
+
+ if (!con_to) {
+ return 0;
+ }
+
+ return online_tcp_connection_from_conn(con_to);
+}
+
+/* Copy a maximum of max_num TCP relays we are connected to to tcp_relays.
+ * NOTE that the family of the copied ip ports will be set to TCP_INET or TCP_INET6.
+ *
+ * return number of relays copied to tcp_relays on success.
+ * return 0 on failure.
+ */
+unsigned int tcp_copy_connected_relays(TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num)
+{
+ unsigned int i, copied = 0, r = rand();
+
+ for (i = 0; (i < tcp_c->tcp_connections_length) && (copied < max_num); ++i) {
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, (i + r) % tcp_c->tcp_connections_length);
+
+ if (!tcp_con) {
+ continue;
+ }
+
+ if (tcp_con->status == TCP_CONN_CONNECTED) {
+ memcpy(tcp_relays[copied].public_key, tcp_con->connection->public_key, CRYPTO_PUBLIC_KEY_SIZE);
+ tcp_relays[copied].ip_port = tcp_con->connection->ip_port;
+
+ if (tcp_relays[copied].ip_port.ip.family == TOX_AF_INET) {
+ tcp_relays[copied].ip_port.ip.family = TCP_INET;
+ } else if (tcp_relays[copied].ip_port.ip.family == TOX_AF_INET6) {
+ tcp_relays[copied].ip_port.ip.family = TCP_INET6;
+ }
+
+ ++copied;
+ }
+ }
+
+ return copied;
+}
+
+/* Set if we want TCP_connection to allocate some connection for onion use.
+ *
+ * If status is 1, allocate some connections. if status is 0, don't.
+ *
+ * return 0 on success.
+ * return -1 on failure.
+ */
+int set_tcp_onion_status(TCP_Connections *tcp_c, bool status)
+{
+ if (tcp_c->onion_status == status) {
+ return -1;
+ }
+
+ if (status) {
+ unsigned int i;
+
+ for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
+
+ if (tcp_con) {
+ if (tcp_con->status == TCP_CONN_CONNECTED && !tcp_con->onion) {
+ ++tcp_c->onion_num_conns;
+ tcp_con->onion = 1;
+ }
+ }
+
+ if (tcp_c->onion_num_conns >= NUM_ONION_TCP_CONNECTIONS) {
+ break;
+ }
+ }
+
+ if (tcp_c->onion_num_conns < NUM_ONION_TCP_CONNECTIONS) {
+ unsigned int wakeup = NUM_ONION_TCP_CONNECTIONS - tcp_c->onion_num_conns;
+
+ for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
+
+ if (tcp_con) {
+ if (tcp_con->status == TCP_CONN_SLEEPING) {
+ tcp_con->unsleep = 1;
+ }
+ }
+
+ if (!wakeup) {
+ break;
+ }
+ }
+ }
+
+ tcp_c->onion_status = 1;
+ } else {
+ unsigned int i;
+
+ for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
+
+ if (tcp_con) {
+ if (tcp_con->onion) {
+ --tcp_c->onion_num_conns;
+ tcp_con->onion = 0;
+ }
+ }
+ }
+
+ tcp_c->onion_status = 0;
+ }
+
+ return 0;
+}
+
+/* Returns a new TCP_Connections object associated with the secret_key.
+ *
+ * In order for others to connect to this instance new_tcp_connection_to() must be called with the
+ * public_key associated with secret_key.
+ *
+ * Returns NULL on failure.
+ */
+TCP_Connections *new_tcp_connections(const uint8_t *secret_key, TCP_Proxy_Info *proxy_info)
+{
+ if (secret_key == NULL) {
+ return NULL;
+ }
+
+ TCP_Connections *temp = (TCP_Connections *)calloc(1, sizeof(TCP_Connections));
+
+ if (temp == NULL) {
+ return NULL;
+ }
+
+ memcpy(temp->self_secret_key, secret_key, CRYPTO_SECRET_KEY_SIZE);
+ crypto_derive_public_key(temp->self_public_key, temp->self_secret_key);
+ temp->proxy_info = *proxy_info;
+
+ return temp;
+}
+
+static void do_tcp_conns(TCP_Connections *tcp_c, void *userdata)
+{
+ unsigned int i;
+
+ for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
+
+ if (tcp_con) {
+ if (tcp_con->status != TCP_CONN_SLEEPING) {
+ do_TCP_connection(tcp_con->connection, userdata);
+
+ /* callbacks can change TCP connection address. */
+ tcp_con = get_tcp_connection(tcp_c, i);
+
+ // Make sure the TCP connection wasn't dropped in any of the callbacks.
+ assert(tcp_con != NULL);
+
+ if (tcp_con->connection->status == TCP_CLIENT_DISCONNECTED) {
+ if (tcp_con->status == TCP_CONN_CONNECTED) {
+ reconnect_tcp_relay_connection(tcp_c, i);
+ } else {
+ kill_tcp_relay_connection(tcp_c, i);
+ }
+
+ continue;
+ }
+
+ if (tcp_con->status == TCP_CONN_VALID && tcp_con->connection->status == TCP_CLIENT_CONFIRMED) {
+ tcp_relay_on_online(tcp_c, i);
+ }
+
+ if (tcp_con->status == TCP_CONN_CONNECTED && !tcp_con->onion && tcp_con->lock_count
+ && tcp_con->lock_count == tcp_con->sleep_count
+ && is_timeout(tcp_con->connected_time, TCP_CONNECTION_ANNOUNCE_TIMEOUT)) {
+ sleep_tcp_relay_connection(tcp_c, i);
+ }
+ }
+
+ if (tcp_con->status == TCP_CONN_SLEEPING && tcp_con->unsleep) {
+ unsleep_tcp_relay_connection(tcp_c, i);
+ }
+ }
+ }
+}
+
+static void kill_nonused_tcp(TCP_Connections *tcp_c)
+{
+ if (tcp_c->tcp_connections_length == 0) {
+ return;
+ }
+
+ unsigned int i;
+ unsigned int num_online = 0;
+ unsigned int num_kill = 0;
+ VLA(unsigned int, to_kill, tcp_c->tcp_connections_length);
+
+ for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
+
+ if (tcp_con) {
+ if (tcp_con->status == TCP_CONN_CONNECTED) {
+ if (!tcp_con->onion && !tcp_con->lock_count && is_timeout(tcp_con->connected_time, TCP_CONNECTION_ANNOUNCE_TIMEOUT)) {
+ to_kill[num_kill] = i;
+ ++num_kill;
+ }
+
+ ++num_online;
+ }
+ }
+ }
+
+ if (num_online <= RECOMMENDED_FRIEND_TCP_CONNECTIONS) {
+ return;
+ }
+
+ unsigned int n = num_online - RECOMMENDED_FRIEND_TCP_CONNECTIONS;
+
+ if (n < num_kill) {
+ num_kill = n;
+ }
+
+ for (i = 0; i < num_kill; ++i) {
+ kill_tcp_relay_connection(tcp_c, to_kill[i]);
+ }
+}
+
+void do_tcp_connections(TCP_Connections *tcp_c, void *userdata)
+{
+ do_tcp_conns(tcp_c, userdata);
+ kill_nonused_tcp(tcp_c);
+}
+
+void kill_tcp_connections(TCP_Connections *tcp_c)
+{
+ unsigned int i;
+
+ for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
+ kill_TCP_connection(tcp_c->tcp_connections[i].connection);
+ }
+
+ free(tcp_c->tcp_connections);
+ free(tcp_c->connections);
+ free(tcp_c);
+}