From 6c71bb7e64c557d13e7eea4102f1e0bb41ec172f Mon Sep 17 00:00:00 2001 From: irungentoo Date: Sat, 27 Sep 2014 18:25:03 -0400 Subject: [PATCH] Moved all the connection stuff from messenger to friend_connection. Messenger was doing way do many things. friend_connection takes care of finding and establishing a connection to friends. --- toxcore/DHT.h | 2 +- toxcore/Makefile.inc | 2 + toxcore/Messenger.c | 245 ++-------------- toxcore/Messenger.h | 27 +- toxcore/friend_connection.c | 565 ++++++++++++++++++++++++++++++++++++ toxcore/friend_connection.h | 140 +++++++++ 6 files changed, 745 insertions(+), 236 deletions(-) create mode 100644 toxcore/friend_connection.c create mode 100644 toxcore/friend_connection.h diff --git a/toxcore/DHT.h b/toxcore/DHT.h index 5339d3e6..e1e14cd9 100644 --- a/toxcore/DHT.h +++ b/toxcore/DHT.h @@ -122,7 +122,7 @@ typedef struct { uint64_t NATping_timestamp; } NAT; -#define DHT_FRIEND_MAX_LOCKS 2 +#define DHT_FRIEND_MAX_LOCKS 32 typedef struct { uint8_t client_id[CLIENT_ID_SIZE]; diff --git a/toxcore/Makefile.inc b/toxcore/Makefile.inc index 8af793c8..9fd1f94a 100644 --- a/toxcore/Makefile.inc +++ b/toxcore/Makefile.inc @@ -19,6 +19,8 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \ ../toxcore/friend_requests.c \ ../toxcore/LAN_discovery.h \ ../toxcore/LAN_discovery.c \ + ../toxcore/friend_connection.h \ + ../toxcore/friend_connection.c \ ../toxcore/Messenger.h \ ../toxcore/Messenger.c \ ../toxcore/ping.h \ diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index 8faa7f03..04830260 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -153,73 +153,9 @@ void getaddress(const Messenger *m, uint8_t *address) memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(nospam), &checksum, sizeof(checksum)); } -/* callback for recv TCP relay nodes. */ -static int tcp_relay_node_callback(void *object, uint32_t number, IP_Port ip_port, const uint8_t *public_key) -{ - Messenger *m = object; - - if (friend_not_valid(m, number)) - return -1; - - if (m->friendlist[number].crypt_connection_id != -1) { - return add_tcp_relay_peer(m->net_crypto, m->friendlist[number].crypt_connection_id, ip_port, public_key); - } else { - return add_tcp_relay(m->net_crypto, ip_port, public_key); - } -} - -static int friend_new_connection(Messenger *m, int32_t friendnumber, const uint8_t *real_public_key); -/* Callback for DHT ip_port changes. */ -static void dht_ip_callback(void *data, int32_t number, IP_Port ip_port) -{ - Messenger *m = data; - - if (friend_not_valid(m, number)) - return; - - if (m->friendlist[number].crypt_connection_id == -1) { - friend_new_connection(m, number, m->friendlist[number].client_id); - } - - set_direct_ip_port(m->net_crypto, m->friendlist[number].crypt_connection_id, ip_port); - m->friendlist[number].dht_ip_port = ip_port; - m->friendlist[number].dht_ip_port_lastrecv = unix_time(); -} - -/* Callback for dht public key changes. */ -static void dht_pk_callback(void *data, int32_t number, const uint8_t *dht_public_key) -{ - Messenger *m = data; - - if (friend_not_valid(m, number)) - return; - - m->friendlist[number].dht_ping_lastrecv = unix_time(); - - if (memcmp(m->friendlist[number].dht_temp_pk, dht_public_key, crypto_box_PUBLICKEYBYTES) == 0) - return; - - if (m->friendlist[number].dht_lock) { - if (DHT_delfriend(m->dht, m->friendlist[number].dht_temp_pk, m->friendlist[number].dht_lock) != 0) { - printf("a. Could not delete dht peer. Please report this.\n"); - return; - } - - m->friendlist[number].dht_lock = 0; - } - - DHT_addfriend(m->dht, dht_public_key, dht_ip_callback, data, number, &m->friendlist[number].dht_lock); - - if (m->friendlist[number].crypt_connection_id == -1) { - friend_new_connection(m, number, m->friendlist[number].client_id); - } - - set_connection_dht_public_key(m->net_crypto, m->friendlist[number].crypt_connection_id, dht_public_key); - onion_set_friend_DHT_pubkey(m->onion_c, m->friendlist[number].onion_friendnum, dht_public_key); - - memcpy(m->friendlist[number].dht_temp_pk, dht_public_key, crypto_box_PUBLICKEYBYTES); -} - +static int handle_status(void *object, int i, uint8_t status); +static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len); +static int handle_custom_lossy_packet(void *object, int friend_num, const uint8_t *packet, uint16_t length); /* * Add a friend. @@ -283,18 +219,17 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u memset(&(m->friendlist[m->numfriends]), 0, sizeof(Friend)); - int32_t onion_friendnum = onion_addfriend(m->onion_c, client_id); + int friendcon_id = new_friend_connection(m->fr_c, client_id); - if (onion_friendnum == -1) - return FAERR_UNKNOWN; + if (friendcon_id == -1) + return -1; uint32_t i; for (i = 0; i <= m->numfriends; ++i) { if (m->friendlist[i].status == NOFRIEND) { - m->friendlist[i].onion_friendnum = onion_friendnum; m->friendlist[i].status = FRIEND_ADDED; - m->friendlist[i].crypt_connection_id = -1; + m->friendlist[i].friendcon_id = friendcon_id; m->friendlist[i].friendrequest_lastsent = 0; m->friendlist[i].friendrequest_timeout = FRIENDREQUEST_TIMEOUT; id_copy(m->friendlist[i].client_id, client_id); @@ -311,8 +246,9 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u m->friendlist[i].message_id = 0; m->friendlist[i].receives_read_receipts = 1; /* Default: YES. */ memcpy(&(m->friendlist[i].friendrequest_nospam), address + crypto_box_PUBLICKEYBYTES, sizeof(uint32_t)); - recv_tcp_relay_handler(m->onion_c, onion_friendnum, &tcp_relay_node_callback, m, i); - onion_dht_pk_callback(m->onion_c, onion_friendnum, &dht_pk_callback, m, i); + friend_connection_callbacks(m->fr_c, friendcon_id, MESSENGER_CALLBACK_INDEX, &handle_status, &handle_packet, + &handle_custom_lossy_packet, m, i); + if (m->numfriends == i) ++m->numfriends; @@ -341,18 +277,17 @@ int32_t m_addfriend_norequest(Messenger *m, const uint8_t *client_id) memset(&(m->friendlist[m->numfriends]), 0, sizeof(Friend)); - int32_t onion_friendnum = onion_addfriend(m->onion_c, client_id); + int friendcon_id = new_friend_connection(m->fr_c, client_id); - if (onion_friendnum == -1) + if (friendcon_id == -1) return -1; uint32_t i; for (i = 0; i <= m->numfriends; ++i) { if (m->friendlist[i].status == NOFRIEND) { - m->friendlist[i].onion_friendnum = onion_friendnum; m->friendlist[i].status = FRIEND_CONFIRMED; - m->friendlist[i].crypt_connection_id = -1; + m->friendlist[i].friendcon_id = friendcon_id; m->friendlist[i].friendrequest_lastsent = 0; id_copy(m->friendlist[i].client_id, client_id); m->friendlist[i].statusmessage = calloc(1, 1); @@ -365,8 +300,8 @@ int32_t m_addfriend_norequest(Messenger *m, const uint8_t *client_id) m->friendlist[i].is_typing = 0; m->friendlist[i].message_id = 0; m->friendlist[i].receives_read_receipts = 1; /* Default: YES. */ - recv_tcp_relay_handler(m->onion_c, onion_friendnum, &tcp_relay_node_callback, m, i); - onion_dht_pk_callback(m->onion_c, onion_friendnum, &dht_pk_callback, m, i); + friend_connection_callbacks(m->fr_c, friendcon_id, MESSENGER_CALLBACK_INDEX, &handle_status, &handle_packet, + &handle_custom_lossy_packet, m, i); if (m->numfriends == i) ++m->numfriends; @@ -391,16 +326,11 @@ int m_delfriend(Messenger *m, int32_t friendnumber) if (m->friendlist[friendnumber].status == FRIEND_ONLINE) remove_online_friend(m, friendnumber); - onion_delfriend(m->onion_c, m->friendlist[friendnumber].onion_friendnum); - - if (m->friendlist[friendnumber].dht_lock) { - DHT_delfriend(m->dht, m->friendlist[friendnumber].dht_temp_pk, m->friendlist[friendnumber].dht_lock); - } - - crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id); free(m->friendlist[friendnumber].statusmessage); free(m->friendlist[friendnumber].avatar_recv_data); remove_request_received(&(m->fr), m->friendlist[friendnumber].client_id); + friend_connection_callbacks(m->fr_c, m->friendlist[friendnumber].friendcon_id, MESSENGER_CALLBACK_INDEX, 0, 0, 0, 0, 0); + kill_friend_connection(m->fr_c, m->friendlist[friendnumber].friendcon_id); memset(&(m->friendlist[friendnumber]), 0, sizeof(Friend)); uint32_t i; @@ -867,16 +797,6 @@ static int send_user_istyping(const Messenger *m, int32_t friendnumber, uint8_t return write_cryptpacket_id(m, friendnumber, PACKET_ID_TYPING, &typing, sizeof(typing), 0); } -static int send_ping(const Messenger *m, int32_t friendnumber) -{ - int ret = write_cryptpacket_id(m, friendnumber, PACKET_ID_ALIVE, 0, 0, 0); - - if (ret == 1) - m->friendlist[friendnumber].ping_lastsent = unix_time(); - - return ret; -} - static int send_relays(const Messenger *m, int32_t friendnumber) { Node_format nodes[MAX_SHARED_RELAYS]; @@ -1023,8 +943,6 @@ static void check_friend_connectionstatus(Messenger *m, int32_t friendnumber, ui const uint8_t was_online = m->friendlist[friendnumber].status == FRIEND_ONLINE; const uint8_t is_online = status == FRIEND_ONLINE; - onion_set_friend_online(m->onion_c, m->friendlist[friendnumber].onion_friendnum, is_online); - if (is_online != was_online) { if (was_online) { break_files(m, friendnumber); @@ -1065,8 +983,8 @@ static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_ if (length != 0) memcpy(packet + 1, data, length); - return write_cryptpacket(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id, packet, length + 1, - congestion_control) != -1; + return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, + m->friendlist[friendnumber].friendcon_id), packet, length + 1, congestion_control) != -1; } /**********GROUP CHATS************/ @@ -1311,8 +1229,8 @@ int file_data(const Messenger *m, int32_t friendnumber, uint8_t filenumber, cons if (m->friendlist[friendnumber].file_sending[filenumber].status != FILESTATUS_TRANSFERRING) return -1; - /* Prevent file sending from filling up the entire buffer preventing messages from being sent. */ - if (crypto_num_free_sendqueue_slots(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id) < MIN_SLOTS_FREE) + /* Prevent file sending from filling up the entire buffer preventing messages from being sent. TODO: remove */ + if (crypto_num_free_sendqueue_slots(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, m->friendlist[friendnumber].friendcon_id)) < MIN_SLOTS_FREE) return -1; uint8_t packet[MAX_CRYPTO_DATA_SIZE]; @@ -1518,10 +1436,8 @@ int send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const uin if (m->friendlist[friendnumber].status != FRIEND_ONLINE) return -1; - if (m->friendlist[friendnumber].crypt_connection_id == -1) - return -1; - - return send_lossy_cryptpacket(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id, data, length); + return send_lossy_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, + m->friendlist[friendnumber].friendcon_id), data, length); } static int handle_custom_lossless_packet(void *object, int friend_num, const uint8_t *packet, uint16_t length) @@ -1579,10 +1495,8 @@ int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const if (m->friendlist[friendnumber].status != FRIEND_ONLINE) return -1; - if (m->friendlist[friendnumber].crypt_connection_id == -1) - return -1; - - if (write_cryptpacket(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id, data, length, 1) == -1) { + if (write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, + m->friendlist[friendnumber].friendcon_id), data, length, 1) == -1) { return -1; } else { return 0; @@ -1609,42 +1523,6 @@ static void LANdiscovery(Messenger *m) } } -static int handle_status(void *object, int i, uint8_t status); -static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len); - -static int handle_new_connections(void *object, New_Connection *n_c) -{ - Messenger *m = object; - int friend_id = getfriend_id(m, n_c->public_key); - - if (friend_id != -1) { - if (m->friendlist[friend_id].crypt_connection_id != -1) - return -1; - - int id = accept_crypto_connection(m->net_crypto, n_c); - connection_status_handler(m->net_crypto, id, &handle_status, m, friend_id); - connection_data_handler(m->net_crypto, id, &handle_packet, m, friend_id); - connection_lossy_data_handler(m->net_crypto, id, &handle_custom_lossy_packet, m, friend_id); - m->friendlist[friend_id].crypt_connection_id = id; - set_friend_status(m, friend_id, FRIEND_CONFIRMED); - - if (n_c->source.ip.family != AF_INET && n_c->source.ip.family != AF_INET6) { - set_direct_ip_port(m->net_crypto, m->friendlist[friend_id].crypt_connection_id, m->friendlist[friend_id].dht_ip_port); - } else { - m->friendlist[friend_id].dht_ip_port = n_c->source; - m->friendlist[friend_id].dht_ip_port_lastrecv = unix_time(); - } - - dht_pk_callback(m, friend_id, n_c->dht_public_key); - - nc_dht_pk_callback(m->net_crypto, id, &dht_pk_callback, m, friend_id); - return 0; - } - - return -1; -} - - /* Run this at startup. */ Messenger *new_messenger(Messenger_Options *options) { @@ -1691,13 +1569,13 @@ Messenger *new_messenger(Messenger_Options *options) return NULL; } - new_connection_handler(m->net_crypto, &handle_new_connections, m); - m->onion = new_onion(m->dht); m->onion_a = new_onion_announce(m->dht); m->onion_c = new_onion_client(m->net_crypto); + m->fr_c = new_friend_connections(m->onion_c); if (!(m->onion && m->onion_a && m->onion_c)) { + kill_friend_connections(m->fr_c); kill_onion(m->onion); kill_onion_announce(m->onion_a); kill_onion_client(m->onion_c); @@ -1722,6 +1600,7 @@ void kill_messenger(Messenger *m) { uint32_t i; + kill_friend_connections(m->fr_c); kill_onion(m->onion); kill_onion_announce(m->onion_a); kill_onion_client(m->onion_c); @@ -1769,10 +1648,6 @@ static int handle_status(void *object, int i, uint8_t status) m->friendlist[i].statusmessage_sent = 0; m->friendlist[i].ping_lastrecv = temp_time; } else { /* Went offline. */ - m->friendlist[i].crypt_connection_id = -1; - - m->friendlist[i].dht_ping_lastrecv = temp_time; - if (m->friendlist[i].status == FRIEND_ONLINE) { set_friend_status(m, i, FRIEND_CONFIRMED); } @@ -2072,11 +1947,6 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len) return -1; switch (packet_id) { - case PACKET_ID_ALIVE: { - m->friendlist[i].ping_lastrecv = temp_time; - break; - } - case PACKET_ID_NICKNAME: { if (data_length > MAX_NAME_LENGTH || data_length == 0) break; @@ -2359,29 +2229,6 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len) return 0; } -static int friend_new_connection(Messenger *m, int32_t friendnumber, const uint8_t *real_public_key) -{ - if (friend_not_valid(m, friendnumber)) - return -1; - - if (m->friendlist[friendnumber].crypt_connection_id != -1) { - return -1; - } - - int id = new_crypto_connection(m->net_crypto, real_public_key); - - if (id == -1) - return -1; - - m->friendlist[friendnumber].crypt_connection_id = id; - connection_status_handler(m->net_crypto, id, &handle_status, m, friendnumber); - connection_data_handler(m->net_crypto, id, &handle_packet, m, friendnumber); - connection_lossy_data_handler(m->net_crypto, id, &handle_custom_lossy_packet, m, friendnumber); - nc_dht_pk_callback(m->net_crypto, id, &dht_pk_callback, m, friendnumber); - - return 0; -} - /* TODO: Make this function not suck. */ void do_friends(Messenger *m) { @@ -2407,27 +2254,7 @@ void do_friends(Messenger *m) * unsuccessful so we set the status back to FRIEND_ADDED and try again. */ check_friend_request_timed_out(m, i, temp_time); - - } else { - if (m->friendlist[i].dht_ping_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) { - if (m->friendlist[i].dht_lock) { - DHT_delfriend(m->dht, m->friendlist[i].dht_temp_pk, m->friendlist[i].dht_lock); - m->friendlist[i].dht_lock = 0; - } - } - - if (m->friendlist[i].dht_ip_port_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) { - m->friendlist[i].dht_ip_port.ip.family = 0; - } } - - if (friend_new_connection(m, i, m->friendlist[i].client_id) == 0) { - if (m->friendlist[i].dht_lock) - set_connection_dht_public_key(m->net_crypto, m->friendlist[i].crypt_connection_id, m->friendlist[i].dht_temp_pk); - - set_direct_ip_port(m->net_crypto, m->friendlist[i].crypt_connection_id, m->friendlist[i].dht_ip_port); - } - } if (m->friendlist[i].status == FRIEND_ONLINE) { /* friend is online. */ @@ -2456,17 +2283,6 @@ void do_friends(Messenger *m) m->friendlist[i].user_istyping_sent = 1; } - if (m->friendlist[i].ping_lastsent + FRIEND_PING_INTERVAL < temp_time) { - send_ping(m, i); - } - - if (m->friendlist[i].ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) { - /* If we stopped receiving ping packets, kill it. */ - crypto_kill(m->net_crypto, m->friendlist[i].crypt_connection_id); - m->friendlist[i].crypt_connection_id = -1; - set_friend_status(m, i, FRIEND_CONFIRMED); - } - if (m->friendlist[i].share_relays_lastsent + FRIEND_SHARE_RELAYS_INTERVAL < temp_time) { send_relays(m, i); } @@ -2536,6 +2352,7 @@ void do_messenger(Messenger *m) do_net_crypto(m->net_crypto); do_onion_client(m->onion_c); + do_friend_connections(m->fr_c); do_friends(m); LANdiscovery(m); @@ -2616,8 +2433,8 @@ void do_messenger(Messenger *m) if (ping_lastrecv > 999) ping_lastrecv = 999; - LOGGER_INFO("F[%2u:%2u] <%s> %02i [%03u] %s", - dht2m[friend], friend, msgfptr->name, msgfptr->crypt_connection_id, + LOGGER_INFO("F[%2u:%2u] <%s> [%03u] %s", + dht2m[friend], friend, msgfptr->name, ping_lastrecv, ID2String(msgfptr->client_id)); } else { LOGGER_INFO("F[--:%2u] %s", friend, ID2String(dhtfptr->client_id)); diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h index 60d00225..454c31cb 100644 --- a/toxcore/Messenger.h +++ b/toxcore/Messenger.h @@ -26,11 +26,9 @@ #ifndef MESSENGER_H #define MESSENGER_H -#include "net_crypto.h" -#include "DHT.h" #include "friend_requests.h" #include "LAN_discovery.h" -#include "onion_client.h" +#include "friend_connection.h" #define MAX_NAME_LENGTH 128 /* TODO: this must depend on other variable. */ @@ -41,8 +39,7 @@ #define FRIEND_ADDRESS_SIZE (crypto_box_PUBLICKEYBYTES + sizeof(uint32_t) + sizeof(uint16_t)) -/* NOTE: Packet ids below 16 must never be used. */ -#define PACKET_ID_ALIVE 16 +/* NOTE: Packet ids below 17 must never be used. */ #define PACKET_ID_SHARE_RELAYS 17 #define PACKET_ID_NICKNAME 48 #define PACKET_ID_STATUSMESSAGE 49 @@ -104,15 +101,9 @@ enum { /* Default start timeout in seconds between friend requests. */ #define FRIENDREQUEST_TIMEOUT 5; -/* Interval between the sending of ping packets. */ -#define FRIEND_PING_INTERVAL 6 - /* Interval between the sending of tcp relay information */ #define FRIEND_SHARE_RELAYS_INTERVAL (5 * 60) -/* If no packets are received from friend in this time interval, kill the connection. */ -#define FRIEND_CONNECTION_TIMEOUT (FRIEND_PING_INTERVAL * 3) - /* Must be < MAX_CRYPTO_DATA_SIZE */ #define AVATAR_DATA_MAX_CHUNK_SIZE (MAX_CRYPTO_DATA_SIZE-1) @@ -120,7 +111,6 @@ enum { #define AVATAR_DATA_TRANSFER_LIMIT (10*AVATAR_MAX_DATA_LENGTH) #define AVATAR_DATA_TRANSFER_TIMEOUT (60) /* 164kB every 60 seconds is not a lot */ -#define FRIEND_DHT_TIMEOUT BAD_NODE_TIMEOUT /* Time before friend is removed from the DHT after last hearing about him. */ /* USERSTATUS - * Represents userstatuses someone can have. @@ -197,14 +187,8 @@ enum { typedef struct { uint8_t client_id[crypto_box_PUBLICKEYBYTES]; + int friendcon_id; - uint8_t dht_temp_pk[crypto_box_PUBLICKEYBYTES]; - uint16_t dht_lock; - IP_Port dht_ip_port; - uint64_t dht_ping_lastrecv, dht_ip_port_lastrecv; - - uint32_t onion_friendnum; - int crypt_connection_id; uint64_t friendrequest_lastsent; // Time at which the last friend request was sent. uint32_t friendrequest_timeout; // The timeout between successful friendrequest sending attempts. uint8_t status; // 0 if no friend, 1 if added, 2 if friend request sent, 3 if confirmed friend, 4 if online. @@ -225,8 +209,7 @@ typedef struct { uint32_t message_id; // a semi-unique id used in read receipts. uint8_t receives_read_receipts; // shall we send read receipts to this person? uint32_t friendrequest_nospam; // The nospam number used in the friend request. - uint64_t ping_lastrecv; - uint64_t ping_lastsent; + uint64_t ping_lastrecv;//TODO remove uint64_t share_relays_lastsent; struct File_Transfers file_sending[MAX_CONCURRENT_FILE_PIPES]; struct File_Transfers file_receiving[MAX_CONCURRENT_FILE_PIPES]; @@ -256,6 +239,8 @@ typedef struct Messenger { Onion_Announce *onion_a; Onion_Client *onion_c; + Friend_Connections *fr_c; + Friend_Requests fr; uint8_t name[MAX_NAME_LENGTH]; uint16_t name_length; diff --git a/toxcore/friend_connection.c b/toxcore/friend_connection.c new file mode 100644 index 00000000..09dea4c3 --- /dev/null +++ b/toxcore/friend_connection.c @@ -0,0 +1,565 @@ +/* friend_connection.c + * + * Connection to friends. + * + * 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 . + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "friend_connection.h" +#include "util.h" + +/* return 1 if the friendcon_id is not valid. + * return 0 if the friendcon_id is valid. + */ +static uint8_t friendconn_id_not_valid(const Friend_Connections *fr_c, int friendcon_id) +{ + if ((unsigned int)friendcon_id >= fr_c->num_cons) + return 1; + + if (fr_c->conns == NULL) + return 1; + + if (fr_c->conns[friendcon_id].status == FRIENDCONN_STATUS_NONE) + return 1; + + return 0; +} + + +/* Set the size of the friend connections list to num. + * + * return -1 if realloc fails. + * return 0 if it succeeds. + */ +static int realloc_friendconns(Friend_Connections *fr_c, uint32_t num) +{ + if (num == 0) { + free(fr_c->conns); + fr_c->conns = NULL; + return 0; + } + + Friend_Conn *newgroup_cons = realloc(fr_c->conns, num * sizeof(Friend_Conn)); + + if (newgroup_cons == NULL) + return -1; + + fr_c->conns = newgroup_cons; + return 0; +} + +/* Create a new empty friend connection. + * + * return -1 on failure. + * return friendcon_id on success. + */ +static int create_friend_conn(Friend_Connections *fr_c) +{ + uint32_t i; + + for (i = 0; i < fr_c->num_cons; ++i) { + if (fr_c->conns[i].status == FRIENDCONN_STATUS_NONE) + return i; + } + + int id = -1; + + if (realloc_friendconns(fr_c, fr_c->num_cons + 1) == 0) { + id = fr_c->num_cons; + ++fr_c->num_cons; + memset(&(fr_c->conns[id]), 0, sizeof(Friend_Conn)); + } + + return id; +} + +/* Wipe a friend connection. + * + * return -1 on failure. + * return 0 on success. + */ +static int wipe_friend_conn(Friend_Connections *fr_c, int friendcon_id) +{ + if (friendconn_id_not_valid(fr_c, friendcon_id)) + return -1; + + uint32_t i; + memset(&(fr_c->conns[friendcon_id]), 0 , sizeof(Friend_Conn)); + + for (i = fr_c->num_cons; i != 0; --i) { + if (fr_c->conns[i - 1].status != FRIENDCONN_STATUS_NONE) + break; + } + + if (fr_c->num_cons != i) { + fr_c->num_cons = i; + realloc_friendconns(fr_c, fr_c->num_cons); + } + + return 0; +} + +static Friend_Conn *get_conn(const Friend_Connections *fr_c, int friendcon_id) +{ + if (friendconn_id_not_valid(fr_c, friendcon_id)) + return 0; + + return &fr_c->conns[friendcon_id]; +} + +/* return friendcon_id corresponding to the real public key on success. + * return -1 on failure. + */ +int getfriend_conn_id_pk(Friend_Connections *fr_c, const uint8_t *real_pk) +{ + uint32_t i; + + for (i = 0; i < fr_c->num_cons; ++i) { + Friend_Conn *friend_con = get_conn(fr_c, i); + + if (friend_con) { + if (memcmp(friend_con->real_public_key, real_pk, crypto_box_PUBLICKEYBYTES) == 0) + return i; + } + } + + return -1; +} + +/* callback for recv TCP relay nodes. */ +static int tcp_relay_node_callback(void *object, uint32_t number, IP_Port ip_port, const uint8_t *public_key) +{ + Friend_Connections *fr_c = object; + Friend_Conn *friend_con = get_conn(fr_c, number); + + if (!friend_con) + return -1; + + if (friend_con->crypt_connection_id != -1) { + return add_tcp_relay_peer(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port, public_key); + } else { + return add_tcp_relay(fr_c->net_crypto, ip_port, public_key); + } +} + +static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id); +/* Callback for DHT ip_port changes. */ +static void dht_ip_callback(void *object, int32_t number, IP_Port ip_port) +{ + Friend_Connections *fr_c = object; + Friend_Conn *friend_con = get_conn(fr_c, number); + + if (!friend_con) + return; + + if (friend_con->crypt_connection_id == -1) { + friend_new_connection(fr_c, number); + } + + set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port); + friend_con->dht_ip_port = ip_port; + friend_con->dht_ip_port_lastrecv = unix_time(); +} + +/* Callback for dht public key changes. */ +static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_public_key) +{ + Friend_Connections *fr_c = object; + Friend_Conn *friend_con = get_conn(fr_c, number); + + if (!friend_con) + return; + + friend_con->dht_ping_lastrecv = unix_time(); + + if (memcmp(friend_con->dht_temp_pk, dht_public_key, crypto_box_PUBLICKEYBYTES) == 0) + return; + + if (friend_con->dht_lock) { + if (DHT_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock) != 0) { + printf("a. Could not delete dht peer. Please report this.\n"); + return; + } + + friend_con->dht_lock = 0; + } + + DHT_addfriend(fr_c->dht, dht_public_key, dht_ip_callback, object, number, &friend_con->dht_lock); + + if (friend_con->crypt_connection_id == -1) { + friend_new_connection(fr_c, number); + } + + set_connection_dht_public_key(fr_c->net_crypto, friend_con->crypt_connection_id, dht_public_key); + onion_set_friend_DHT_pubkey(fr_c->onion_c, friend_con->onion_friendnum, dht_public_key); + + memcpy(friend_con->dht_temp_pk, dht_public_key, crypto_box_PUBLICKEYBYTES); +} + +static int handle_status(void *object, int number, uint8_t status) +{ + Friend_Connections *fr_c = object; + Friend_Conn *friend_con = get_conn(fr_c, number); + + if (!friend_con) + return -1; + + if (status) { /* Went online. */ + friend_con->status = FRIENDCONN_STATUS_CONNECTED; + friend_con->ping_lastrecv = unix_time(); + onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status); + } else { /* Went offline. */ + friend_con->status = FRIENDCONN_STATUS_CONNECTING; + friend_con->crypt_connection_id = -1; + friend_con->dht_ping_lastrecv = unix_time(); + } + + unsigned int i; + + for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) { + if (friend_con->callbacks[i].status_callback) + friend_con->callbacks[i].status_callback(friend_con->callbacks[i].status_callback_object, + friend_con->callbacks[i].status_callback_id, status); + } + + return 0; +} + +static int handle_packet(void *object, int number, uint8_t *data, uint16_t length) +{ + if (length == 0) + return -1; + + Friend_Connections *fr_c = object; + Friend_Conn *friend_con = get_conn(fr_c, number); + + if (!friend_con) + return -1; + + if (data[0] == PACKET_ID_ALIVE) { + friend_con->ping_lastrecv = unix_time(); + return 0; + } + + unsigned int i; + + for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) { + if (friend_con->callbacks[i].data_callback) + friend_con->callbacks[i].data_callback(friend_con->callbacks[i].data_callback_object, + friend_con->callbacks[i].data_callback_id, data, length); + } + + return 0; +} + +static int handle_lossy_packet(void *object, int number, const uint8_t *data, uint16_t length) +{ + if (length == 0) + return -1; + + Friend_Connections *fr_c = object; + Friend_Conn *friend_con = get_conn(fr_c, number); + + if (!friend_con) + return -1; + + unsigned int i; + + for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) { + if (friend_con->callbacks[i].lossy_data_callback) + friend_con->callbacks[i].lossy_data_callback(friend_con->callbacks[i].lossy_data_callback_object, + friend_con->callbacks[i].lossy_data_callback_id, data, length); + } + + return 0; +} + +static int handle_new_connections(void *object, New_Connection *n_c) +{ + Friend_Connections *fr_c = object; + int friendcon_id = getfriend_conn_id_pk(fr_c, n_c->public_key); + Friend_Conn *friend_con = get_conn(fr_c, friendcon_id); + + if (friend_con) { + + if (friend_con->crypt_connection_id != -1) + return -1; + + int id = accept_crypto_connection(fr_c->net_crypto, n_c); + connection_status_handler(fr_c->net_crypto, id, &handle_status, fr_c, friendcon_id); + connection_data_handler(fr_c->net_crypto, id, &handle_packet, fr_c, friendcon_id); + connection_lossy_data_handler(fr_c->net_crypto, id, &handle_lossy_packet, fr_c, friendcon_id); + friend_con->crypt_connection_id = id; + + if (n_c->source.ip.family != AF_INET && n_c->source.ip.family != AF_INET6) { + set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_ip_port); + } else { + friend_con->dht_ip_port = n_c->source; + friend_con->dht_ip_port_lastrecv = unix_time(); + } + + dht_pk_callback(fr_c, friendcon_id, n_c->dht_public_key); + + nc_dht_pk_callback(fr_c->net_crypto, id, &dht_pk_callback, fr_c, friendcon_id); + return 0; + } + + return -1; +} + +static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id) +{ + Friend_Conn *friend_con = get_conn(fr_c, friendcon_id); + + if (!friend_con) + return -1; + + if (friend_con->crypt_connection_id != -1) { + return -1; + } + + int id = new_crypto_connection(fr_c->net_crypto, friend_con->real_public_key); + + if (id == -1) + return -1; + + friend_con->crypt_connection_id = id; + connection_status_handler(fr_c->net_crypto, id, &handle_status, fr_c, friendcon_id); + connection_data_handler(fr_c->net_crypto, id, &handle_packet, fr_c, friendcon_id); + connection_lossy_data_handler(fr_c->net_crypto, id, &handle_lossy_packet, fr_c, friendcon_id); + nc_dht_pk_callback(fr_c->net_crypto, id, &dht_pk_callback, fr_c, friendcon_id); + + return 0; +} + +static int send_ping(const Friend_Connections *fr_c, int friendcon_id) +{ + Friend_Conn *friend_con = get_conn(fr_c, friendcon_id); + + if (!friend_con) + return -1; + + uint8_t ping = PACKET_ID_ALIVE; + int64_t ret = write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, &ping, sizeof(ping), 0); + + if (ret != -1) { + friend_con->ping_lastsent = unix_time(); + return 0; + } + + return -1; +} + +/* Set the callbacks for the friend connection. + * index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array. + * + * return 0 on success. + * return -1 on failure + */ +int friend_connection_callbacks(Friend_Connections *fr_c, int friendcon_id, unsigned int index, + int (*status_callback)(void *object, int id, uint8_t status), int (*data_callback)(void *object, int id, uint8_t *data, + uint16_t length), int (*lossy_data_callback)(void *object, int id, const uint8_t *data, uint16_t length), void *object, + int number) +{ + Friend_Conn *friend_con = get_conn(fr_c, friendcon_id); + + if (!friend_con) + return -1; + + if (index >= MAX_FRIEND_CONNECTION_CALLBACKS) + return -1; + + friend_con->callbacks[index].status_callback = status_callback; + friend_con->callbacks[index].data_callback = data_callback; + friend_con->callbacks[index].lossy_data_callback = lossy_data_callback; + + friend_con->callbacks[index].status_callback_object = + friend_con->callbacks[index].data_callback_object = + friend_con->callbacks[index].lossy_data_callback_object = object; + + friend_con->callbacks[index].status_callback_id = + friend_con->callbacks[index].data_callback_id = + friend_con->callbacks[index].lossy_data_callback_id = number; + return 0; +} + +/* return the crypt_connection_id for the connection. + * + * return crypt_connection_id on success. + * return -1 on failure. + */ +int friend_connection_crypt_connection_id(Friend_Connections *fr_c, int friendcon_id) +{ + Friend_Conn *friend_con = get_conn(fr_c, friendcon_id); + + if (!friend_con) + return -1; + + return friend_con->crypt_connection_id; +} + +/* Create a new friend connection. + * If one to that real public key already exists, increase lock count and return it. + * + * return -1 on failure. + * return connection id on success. + */ +int new_friend_connection(Friend_Connections *fr_c, const uint8_t *real_public_key) +{ + int friendcon_id = getfriend_conn_id_pk(fr_c, real_public_key); + + if (friendcon_id != -1) { + ++fr_c->conns[friendcon_id].lock_count; + return friendcon_id; + } + + friendcon_id = create_friend_conn(fr_c); + + if (friendcon_id == -1) + return -1; + + int32_t onion_friendnum = onion_addfriend(fr_c->onion_c, real_public_key); + + if (onion_friendnum == -1) + return -1; + + Friend_Conn *friend_con = &fr_c->conns[friendcon_id]; + + friend_con->crypt_connection_id = -1; + friend_con->status = FRIENDCONN_STATUS_CONNECTING; + memcpy(friend_con->real_public_key, real_public_key, crypto_box_PUBLICKEYBYTES); + friend_con->onion_friendnum = onion_friendnum; + + recv_tcp_relay_handler(fr_c->onion_c, onion_friendnum, &tcp_relay_node_callback, fr_c, friendcon_id); + onion_dht_pk_callback(fr_c->onion_c, onion_friendnum, &dht_pk_callback, fr_c, friendcon_id); + + return friendcon_id; +} + +/* Kill a friend connection. + * + * return -1 on failure. + * return 0 on success. + */ +int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id) +{ + Friend_Conn *friend_con = get_conn(fr_c, friendcon_id); + + if (!friend_con) + return -1; + + if (friend_con->lock_count) { + --friend_con->lock_count; + return 0; + } + + onion_delfriend(fr_c->onion_c, friend_con->onion_friendnum); + crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id); + + if (friend_con->dht_lock) { + DHT_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock); + } + + return wipe_friend_conn(fr_c, friendcon_id); +} + + +/* Create new friend_connections instance. */ +Friend_Connections *new_friend_connections(Onion_Client *onion_c) +{ + if (!onion_c) + return NULL; + + Friend_Connections *temp = calloc(1, sizeof(Friend_Connections)); + + if (temp == NULL) + return NULL; + + temp->dht = onion_c->dht; + temp->net_crypto = onion_c->c; + temp->onion_c = onion_c; + + new_connection_handler(temp->net_crypto, &handle_new_connections, temp); + + return temp; +} + +/* main friend_connections loop. */ +void do_friend_connections(Friend_Connections *fr_c) +{ + uint32_t i; + uint64_t temp_time = unix_time(); + + for (i = 0; i < fr_c->num_cons; ++i) { + Friend_Conn *friend_con = get_conn(fr_c, i); + + if (friend_con) { + if (friend_con->status == FRIENDCONN_STATUS_CONNECTING) { + if (friend_con->dht_ping_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) { + if (friend_con->dht_lock) { + DHT_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock); + friend_con->dht_lock = 0; + } + } + + if (friend_con->dht_ip_port_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) { + friend_con->dht_ip_port.ip.family = 0; + } + + if (friend_con->dht_lock) { + if (friend_new_connection(fr_c, i) == 0) { + set_connection_dht_public_key(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_temp_pk); + set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_ip_port); + } + } + + } else if (friend_con->status == FRIENDCONN_STATUS_CONNECTED) { + if (friend_con->ping_lastsent + FRIEND_PING_INTERVAL < temp_time) { + send_ping(fr_c, i); + } + + if (friend_con->ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) { + /* If we stopped receiving ping packets, kill it. */ + crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id); + friend_con->crypt_connection_id = -1; + handle_status(fr_c, i, 0); /* Going offline. */ + } + } + } + } +} + +/* Free everything related with friend_connections. */ +void kill_friend_connections(Friend_Connections *fr_c) +{ + if (!fr_c) + return; + + uint32_t i; + + for (i = 0; i < fr_c->num_cons; ++i) { + kill_friend_connection(fr_c, i); + } + + free(fr_c); +} diff --git a/toxcore/friend_connection.h b/toxcore/friend_connection.h new file mode 100644 index 00000000..62b82dc2 --- /dev/null +++ b/toxcore/friend_connection.h @@ -0,0 +1,140 @@ +/* friend_connection.h + * + * Connection to friends. + * + * 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 FRIEND_CONNECTION_H +#define FRIEND_CONNECTION_H + +#include "net_crypto.h" +#include "DHT.h" +#include "LAN_discovery.h" +#include "onion_client.h" + + +#define MAX_FRIEND_CONNECTION_CALLBACKS 2 +#define MESSENGER_CALLBACK_INDEX 0 +#define GROUPCHAT_CALLBACK_INDEX 1 + +#define PACKET_ID_ALIVE 16 + +/* Interval between the sending of ping packets. */ +#define FRIEND_PING_INTERVAL 6 + +/* If no packets are received from friend in this time interval, kill the connection. */ +#define FRIEND_CONNECTION_TIMEOUT (FRIEND_PING_INTERVAL * 3) + +/* Time before friend is removed from the DHT after last hearing about him. */ +#define FRIEND_DHT_TIMEOUT BAD_NODE_TIMEOUT + + +enum { + FRIENDCONN_STATUS_NONE, + FRIENDCONN_STATUS_CONNECTING, + FRIENDCONN_STATUS_CONNECTED +}; + +typedef struct { + uint8_t status; + + uint8_t real_public_key[crypto_box_PUBLICKEYBYTES]; + uint8_t dht_temp_pk[crypto_box_PUBLICKEYBYTES]; + uint16_t dht_lock; + IP_Port dht_ip_port; + uint64_t dht_ping_lastrecv, dht_ip_port_lastrecv; + + int onion_friendnum; + int crypt_connection_id; + + uint64_t ping_lastrecv, ping_lastsent; + + struct { + int (*status_callback)(void *object, int id, uint8_t status); + void *status_callback_object; + int status_callback_id; + + int (*data_callback)(void *object, int id, uint8_t *data, uint16_t length); + void *data_callback_object; + int data_callback_id; + + int (*lossy_data_callback)(void *object, int id, const uint8_t *data, uint16_t length); + void *lossy_data_callback_object; + int lossy_data_callback_id; + } callbacks[MAX_FRIEND_CONNECTION_CALLBACKS]; + + uint16_t lock_count; +} Friend_Conn; + + +typedef struct { + Net_Crypto *net_crypto; + DHT *dht; + Onion_Client *onion_c; + + Friend_Conn *conns; + uint32_t num_cons; + +} Friend_Connections; + +/* Set the callbacks for the friend connection. + * index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array. + * + * return 0 on success. + * return -1 on failure + */ +int friend_connection_callbacks(Friend_Connections *fr_c, int friendcon_id, unsigned int index, + int (*status_callback)(void *object, int id, uint8_t status), int (*data_callback)(void *object, int id, uint8_t *data, + uint16_t length), int (*lossy_data_callback)(void *object, int id, const uint8_t *data, uint16_t length), void *object, + int number); + +/* return the crypt_connection_id for the connection. + * + * return crypt_connection_id on success. + * return -1 on failure. + */ +int friend_connection_crypt_connection_id(Friend_Connections *fr_c, int friendcon_id); + +/* Create a new friend connection. + * If one to that real public key already exists, increase lock count and return it. + * + * return -1 on failure. + * return connection id on success. + */ +int new_friend_connection(Friend_Connections *fr_c, const uint8_t *real_public_key); + +/* Kill a friend connection. + * + * return -1 on failure. + * return 0 on success. + */ +int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id); + +/* Create new friend_connections instance. */ +Friend_Connections *new_friend_connections(Onion_Client *onion_c); + +/* main friend_connections loop. */ +void do_friend_connections(Friend_Connections *fr_c); + +/* Free everything related with friend_connections. */ +void kill_friend_connections(Friend_Connections *fr_c); + +#endif