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.
This commit is contained in:
irungentoo 2014-09-27 18:25:03 -04:00
parent 834ffee47d
commit 6c71bb7e64
No known key found for this signature in database
GPG Key ID: 10349DC9BED89E98
6 changed files with 745 additions and 236 deletions

View File

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

View File

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

View File

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

View File

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

565
toxcore/friend_connection.c Normal file
View File

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

140
toxcore/friend_connection.h Normal file
View File

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