Merge branch 'master' into harden

This commit is contained in:
irungentoo 2013-12-06 22:51:17 -05:00
commit 7cd43ecc76
25 changed files with 2954 additions and 497 deletions

View File

@ -5,49 +5,54 @@ compiler:
before_script:
#installing libsodium, needed for Core
- git clone git://github.com/jedisct1/libsodium.git
- git clone git://github.com/jedisct1/libsodium.git > /dev/null
- cd libsodium
- git checkout tags/0.4.2
- ./autogen.sh
- ./configure && make check -j3
- sudo make install
- git checkout tags/0.4.2 > /dev/null
- ./autogen.sh > /dev/null
- ./configure > /dev/null
- make check -j3 > /dev/null
- sudo make install >/dev/null
- cd ..
#installing libconfig, needed for DHT_bootstrap_daemon
- wget http://www.hyperrealm.com/libconfig/libconfig-1.4.9.tar.gz
- tar -xvzf libconfig-1.4.9.tar.gz
- wget http://www.hyperrealm.com/libconfig/libconfig-1.4.9.tar.gz > /dev/null
- tar -xvzf libconfig-1.4.9.tar.gz > /dev/null
- cd libconfig-1.4.9
- ./configure && make -j3
- sudo make install
- ./configure > /dev/null
- make -j3 > /dev/null
- sudo make install > /dev/null
- cd ..
#installing libopus, needed for audio encoding/decoding
- wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz
- tar xzvf opus-1.0.3.tar.gz
- wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz > /dev/null
- tar xzvf opus-1.0.3.tar.gz > /dev/null
- cd opus-1.0.3
- ./configure && make -j3
- sudo make install
- ./configure > /dev/null
- make -j3 > /dev/null
- sudo make install > /dev/null
- cd ..
#installing libsdl1.2, needed for displaying video frames
- wget http://www.libsdl.org/release/SDL-1.2.15.tar.gz
- tar -xvzf SDL-1.2.15.tar.gz
- wget http://www.libsdl.org/release/SDL-1.2.15.tar.gz > /dev/null
- tar -xvzf SDL-1.2.15.tar.gz > /dev/null
- cd SDL-1.2.15
- ./configure && make -j3
- sudo make install
- ./configure > /dev/null
- make -j3 /dev/null
- sudo make install > /dev/null
- cd ..
#installing libopenal, needed for audio capture/playback
- sudo apt-get install libopenal-dev
- sudo apt-get install libopenal-dev > /dev/null
#installing yasm, needed for compiling ffmpeg
- sudo apt-get install yasm
- sudo apt-get install yasm > /dev/null
#installing ffmpeg, needed for capturing and encoding/decoding video
- wget https://www.ffmpeg.org/releases/ffmpeg-2.0.2.tar.gz
- tar -xvzf ffmpeg-2.0.2.tar.gz
- wget https://www.ffmpeg.org/releases/ffmpeg-2.0.2.tar.gz > /dev/null
- tar -xvzf ffmpeg-2.0.2.tar.gz > /dev/null
- cd ffmpeg-2.0.2
- ./configure && make -j3
- sudo make install
- ./configure > /dev/null
- make -j3 >/dev/null
- sudo make install > /dev/null
- cd ..
# creating librarys' links and updating cache
- sudo ldconfig
#creating libraries links and updating cache
- sudo ldconfig > /dev/null
#installing check, needed for unit tests
- sudo apt-get install check
- sudo apt-get install check > /dev/null
script:
- autoreconf -i

View File

@ -56,7 +56,7 @@ cd ..
```
If your default prefix is /usr/local and you happen to get an error that says "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run:
```
sudo echo "/usr/local/lib/" >> /etc/ld.so.conf.d/locallib.conf
echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
sudo ldconfig
```

View File

@ -1,8 +1,8 @@
if BUILD_TESTS
TESTS = messenger_autotest crypto_test network_test
TESTS = messenger_autotest crypto_test network_test assoc_test
check_PROGRAMS = messenger_autotest crypto_test network_test
check_PROGRAMS = messenger_autotest crypto_test network_test assoc_test
AUTOTEST_CFLAGS = \
$(LIBSODIUM_CFLAGS) \
@ -38,6 +38,13 @@ network_test_CFLAGS = $(AUTOTEST_CFLAGS)
network_test_LDADD = $(AUTOTEST_LDADD)
assoc_test_SOURCES = ../auto_tests/assoc_test.c
assoc_test_CFLAGS = $(AUTOTEST_CFLAGS)
assoc_test_LDADD = $(AUTOTEST_LDADD)
endif
EXTRA_DIST += $(top_srcdir)/auto_tests/friends_test.c

158
auto_tests/assoc_test.c Normal file
View File

@ -0,0 +1,158 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define AUTO_TEST
#include "../toxcore/DHT.h"
#include "../toxcore/assoc.h"
#include "../toxcore/util.h"
#include <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <check.h>
START_TEST(test_basics)
{
/* TODO: real test */
uint8_t id[CLIENT_ID_SIZE];
Assoc *assoc = new_Assoc_default(id);
ck_assert_msg(assoc != NULL, "failed to create default assoc");
kill_Assoc(assoc);
assoc = new_Assoc(17, 4, id); /* results in an assoc of 16/3 */
ck_assert_msg(assoc != NULL, "failed to create customized assoc");
IP_Port ipp;
ipp.ip.family = AF_INET;
ipp.ip.ip4.uint8[0] = 1;
ipp.port = htons(12345);
IPPTs ippts_send;
ippts_send.ip_port = ipp;
ippts_send.timestamp = unix_time();
IP_Port ipp_recv = ipp;
uint8_t res = Assoc_add_entry(assoc, id, &ippts_send, &ipp_recv, 0);
ck_assert_msg(res == 0, "stored self as entry: expected %u, got %u", 0, res);
id[0]++;
res = Assoc_add_entry(assoc, id, &ippts_send, &ipp_recv, 0);
ck_assert_msg(res == 1, "failed to store entry: expected %u, got %u", 1, res);
Assoc_close_entries close_entries;
memset(&close_entries, 0, sizeof(close_entries));
close_entries.count = 4;
close_entries.count_good = 2;
close_entries.wanted_id = id;
Client_data *entries[close_entries.count];
close_entries.result = entries;
uint8_t found = Assoc_get_close_entries(assoc, &close_entries);
ck_assert_msg(found == 1, "get_close_entries(): expected %u, got %u", 1, found);
}
END_TEST
START_TEST(test_fillup)
{
/* TODO: real test */
int i, j;
uint8_t id[CLIENT_ID_SIZE];
//uint32_t a = current_time();
uint32_t a = 2710106197;
srand(a);
for(i = 0; i < CLIENT_ID_SIZE; ++i) {
id[i] = rand();
}
Assoc *assoc = new_Assoc(6, 15, id);
ck_assert_msg(assoc != NULL, "failed to create default assoc");
struct entry {
uint8_t id[CLIENT_ID_SIZE];
IPPTs ippts_send;
IP_Port ipp_recv;
};
unsigned int fail = 0;
struct entry entries[128];
struct entry closest[8];
for(j = 0; j < 128; ++j) {
for(i = 0; i < CLIENT_ID_SIZE; ++i) {
entries[j].id[i] = rand();
}
IP_Port ipp;
ipp.ip.family = AF_INET;
ipp.ip.ip4.uint32 = rand();
ipp.port = rand();
entries[j].ippts_send.ip_port = ipp;
entries[j].ippts_send.timestamp = unix_time();
ipp.ip.ip4.uint32 = rand();
ipp.port = rand();
entries[j].ipp_recv = ipp;
if (j % 16 == 0) {
memcpy(entries[j].id, id, CLIENT_ID_SIZE - 30);
memcpy(&closest[j/16], &entries[j], sizeof(struct entry));
}
uint8_t res = Assoc_add_entry(assoc, entries[j].id, &entries[j].ippts_send, &entries[j].ipp_recv, 1);
ck_assert_msg(res == 1, "failed to store entry: expected %u, got %u, j = %u", 1, res, j);
}
int good = 0;
Assoc_close_entries close_entries;
memset(&close_entries, 0, sizeof(close_entries));
close_entries.count = 8;
close_entries.count_good = 8;
close_entries.wanted_id = id;
Client_data *entri[close_entries.count];
close_entries.result = entri;
uint8_t found = Assoc_get_close_entries(assoc, &close_entries);
ck_assert_msg(found == 8, "get_close_entries(): expected %u, got %u", 1, found);
for (i = 0; i < 8; ++i) {
for (j = 0; j < 8; ++j) {
if (id_equal(entri[j]->client_id, closest[i].id))
++good;
}
}ck_assert_msg(good == 8, "Entries found were not the closest ones. Only %u/8 were.", good);
//printf("good: %u %u %u\n", good, a, ((uint32_t)current_time() - a));
}
END_TEST
#define DEFTESTCASE(NAME) \
TCase *tc_##NAME = tcase_create(#NAME); \
tcase_add_test(tc_##NAME, test_##NAME); \
suite_add_tcase(s, tc_##NAME);
#define DEFTESTCASE_SLOW(NAME, TIMEOUT) \
DEFTESTCASE(NAME) \
tcase_set_timeout(tc_##NAME, TIMEOUT);
Suite *Assoc_suite(void)
{
Suite *s = suite_create("Assoc");
DEFTESTCASE(basics);
DEFTESTCASE(fillup);
return s;
}
int main(int argc, char *argv[])
{
unix_time_update();
Suite *Assoc = Assoc_suite();
SRunner *test_runner = srunner_create(Assoc);
srunner_set_fork_status(test_runner, CK_NOFORK);
srunner_run_all(test_runner, CK_NORMAL);
int number_failed = srunner_ntests_failed(test_runner);
srunner_free(test_runner);
return number_failed;
}

File diff suppressed because it is too large Load Diff

View File

@ -24,24 +24,19 @@
#ifndef NTOX_H
#define NTOX_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curses.h>
/*
* module actually exports nothing for the outside
*/
#include <ctype.h>
#include <curses.h>
#include "../toxcore/tox.h"
#define STRING_LENGTH 256
#define HISTORY 50
#define PUB_KEY_BYTES 32
uint32_t resolve_addr(const char *address);
void new_lines(char *line);
void line_eval(Tox *m, char *line);
void wrap(char output[STRING_LENGTH], char input[STRING_LENGTH], int line_width) ;
int count_lines(char *string) ;
char *appender(char *str, const char c);
void do_refresh();
#endif

View File

@ -62,11 +62,11 @@ void send_filesenders(Tox *m)
continue;
while (1) {
if (!tox_file_senddata(m, file_senders[i].friendnum, file_senders[i].filenumber, file_senders[i].nextpiece,
if (!tox_file_send_data(m, file_senders[i].friendnum, file_senders[i].filenumber, file_senders[i].nextpiece,
file_senders[i].piecelength))
break;
file_senders[i].piecelength = fread(file_senders[i].nextpiece, 1, tox_filedata_size(m, file_senders[i].friendnum),
file_senders[i].piecelength = fread(file_senders[i].nextpiece, 1, tox_file_data_size(m, file_senders[i].friendnum),
file_senders[i].file);
if (file_senders[i].piecelength == 0) {
@ -74,7 +74,7 @@ void send_filesenders(Tox *m)
file_senders[i].file = 0;
printf("[t] %u file transfer: %u completed %i\n", file_senders[i].friendnum, file_senders[i].filenumber,
tox_file_sendcontrol(m, file_senders[i].friendnum, 0, file_senders[i].filenumber, TOX_FILECONTROL_FINISHED, 0, 0));
tox_file_send_control(m, file_senders[i].friendnum, 0, file_senders[i].filenumber, TOX_FILECONTROL_FINISHED, 0, 0));
break;
}
}
@ -90,13 +90,13 @@ int add_filesender(Tox *m, uint16_t friendnum, char *filename)
fseek(tempfile, 0, SEEK_END);
uint64_t filesize = ftell(tempfile);
fseek(tempfile, 0, SEEK_SET);
int filenum = tox_new_filesender(m, friendnum, filesize, (uint8_t *)filename, strlen(filename) + 1);
int filenum = tox_new_file_sender(m, friendnum, filesize, (uint8_t *)filename, strlen(filename) + 1);
if (filenum == -1)
return -1;
file_senders[numfilesenders].file = tempfile;
file_senders[numfilesenders].piecelength = fread(file_senders[numfilesenders].nextpiece, 1, tox_filedata_size(m,
file_senders[numfilesenders].piecelength = fread(file_senders[numfilesenders].nextpiece, 1, tox_file_data_size(m,
file_senders[numfilesenders].friendnum),
file_senders[numfilesenders].file);
file_senders[numfilesenders].friendnum = friendnum;
@ -149,18 +149,18 @@ void file_request_accept(Tox *m, int friendnumber, uint8_t filenumber, uint64_t
if (tempfile != 0) {
fclose(tempfile);
tox_file_sendcontrol(m, friendnumber, 1, filenumber, TOX_FILECONTROL_KILL, 0, 0);
tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_KILL, 0, 0);
return;
}
file_recv[filenumber].file = fopen(fullpath, "wb");
if (file_recv[filenumber].file == 0) {
tox_file_sendcontrol(m, friendnumber, 1, filenumber, TOX_FILECONTROL_KILL, 0, 0);
tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_KILL, 0, 0);
return;
}
if (tox_file_sendcontrol(m, friendnumber, 1, filenumber, TOX_FILECONTROL_ACCEPT, 0, 0)) {
if (tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_ACCEPT, 0, 0)) {
printf("Accepted file transfer. (file: %s)\n", fullpath);
}
@ -215,8 +215,8 @@ int main(int argc, char *argv[])
Tox *tox = tox_new(ipv6enabled);
tox_callback_file_data(tox, write_file, NULL);
tox_callback_file_control(tox, file_print_control, NULL);
tox_callback_file_sendrequest(tox, file_request_accept, NULL);
tox_callback_connectionstatus(tox, print_online, NULL);
tox_callback_file_send_request(tox, file_request_accept, NULL);
tox_callback_connection_status(tox, print_online, NULL);
uint16_t port = htons(atoi(argv[argvoffset + 2]));
unsigned char *binary_string = hex_string_to_bin(argv[argvoffset + 3]);
@ -228,7 +228,7 @@ int main(int argc, char *argv[])
}
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
tox_getaddress(tox, address);
tox_get_address(tox, address);
uint32_t i;
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
@ -242,7 +242,7 @@ int main(int argc, char *argv[])
return 1;
}
int num = tox_addfriend(tox, hex_string_to_bin(temp_id), (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"));
int num = tox_add_friend(tox, hex_string_to_bin(temp_id), (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"));
if (num < 0) {
printf("\nSomething went wrong when adding friend.\n");
@ -260,7 +260,7 @@ int main(int argc, char *argv[])
notconnected = 0;
}
if (not_sending() && tox_get_friend_connectionstatus(tox, num)) {
if (not_sending() && tox_get_friend_connection_status(tox, num)) {
d = opendir(path);
if (d) {

View File

@ -28,34 +28,27 @@
#endif
#include "DHT.h"
#include "network.h"
#include "assoc.h"
#include "ping.h"
#include "network.h"
#include "LAN_discovery.h"
#include "misc_tools.h"
#include "util.h"
#include "LAN_discovery.h"
/* The number of seconds for a non responsive node to become bad. */
#define BAD_NODE_TIMEOUT 70
/* The max number of nodes to send with send nodes. */
#define MAX_SENT_NODES 8
/* Ping timeout in seconds */
#define PING_TIMEOUT 3
/* The timeout after which a node is discarded completely. */
#define KILL_NODE_TIMEOUT 300
/* Ping interval in seconds for each node in our lists. */
#define PING_INTERVAL 60
/* Ping interval in seconds for each random sending of a get nodes request. */
#define GET_NODE_INTERVAL 5
#define MAX_PUNCHING_PORTS 128
#define MAX_PUNCHING_PORTS 32
/* Interval in seconds between punching attempts*/
#define PUNCH_INTERVAL 10
#define PUNCH_INTERVAL 3
#define NAT_PING_REQUEST 0
#define NAT_PING_RESPONSE 1
@ -115,21 +108,6 @@ static int client_id_cmp(ClientPair p1, ClientPair p2)
return c;
}
static int client_in_list(Client_data *list, uint32_t length, uint8_t *client_id)
{
uint32_t i;
for (i = 0; i < length; i++)
/* Dead nodes are considered dead (not in the list)*/
if (!is_timeout(list[i].assoc4.timestamp, KILL_NODE_TIMEOUT) ||
!is_timeout(list[i].assoc6.timestamp, KILL_NODE_TIMEOUT))
if (id_equal(list[i].client_id, client_id))
return 1;
return 0;
}
/* Check if client with client_id is already in list of length length.
* If it is then set its corresponding timestamp to current time.
* If the id is already in the list with a different ip_port, update it.
@ -373,8 +351,7 @@ static void get_close_nodes_inner(DHT *dht, uint8_t *client_id, Node_format *nod
*
* want_good : do we want only good nodes as checked with the hardening returned or not?
*/
static uint32_t get_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list, sa_family_t sa_family,
uint8_t is_LAN, uint8_t want_good)
static int get_somewhat_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list, sa_family_t sa_family, uint8_t is_LAN, uint8_t want_good)
{
uint32_t num_nodes = 0, i;
get_close_nodes_inner(dht, client_id, nodes_list, sa_family,
@ -388,6 +365,53 @@ static uint32_t get_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes
return num_nodes;
}
static int get_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list, sa_family_t sa_family, uint8_t is_LAN, uint8_t want_good)
{
if (!dht->assoc)
return get_somewhat_close_nodes(dht, client_id, nodes_list, sa_family, is_LAN, want_good);
Client_data *result[MAX_SENT_NODES];
Assoc_close_entries request;
memset(&request, 0, sizeof(request));
request.count = MAX_SENT_NODES;
request.count_good = MAX_SENT_NODES;
request.result = result;
request.wanted_id = client_id;
request.flags = (is_LAN ? LANOk : 0) + (sa_family == AF_INET ? ProtoIPv4 : ProtoIPv6);
uint8_t num_found = Assoc_get_close_entries(dht->assoc, &request);
if (!num_found)
return get_somewhat_close_nodes(dht, client_id, nodes_list, sa_family, is_LAN, want_good);
uint8_t i, num_returned = 0;
for (i = 0; i < num_found; i++) {
Client_data *client = result[i];
if (client) {
id_copy(nodes_list[i].client_id, client->client_id);
if (sa_family == AF_INET)
if (ipport_isset(&client->assoc4.ip_port)) {
nodes_list[i].ip_port = client->assoc4.ip_port;
num_returned++;
continue;
}
if (sa_family == AF_INET6)
if (ipport_isset(&client->assoc6.ip_port)) {
nodes_list[i].ip_port = client->assoc6.ip_port;
num_returned++;
continue;
}
}
}
return num_returned;
}
/* Replace first bad (or empty) node with this one.
*
* return 0 if successful.
@ -406,21 +430,31 @@ static int replace_bad( Client_data *list,
for (i = 0; i < length; ++i) {
/* If node is bad */
Client_data *client = &list[i];
IPPTsPng *ipptp = NULL;
if (ip_port.ip.family == AF_INET)
ipptp = &client->assoc4;
else
ipptp = &client->assoc6;
if (is_timeout(client->assoc4.timestamp, BAD_NODE_TIMEOUT) &&
is_timeout(client->assoc6.timestamp, BAD_NODE_TIMEOUT)) {
IPPTsPng *ipptp_write = NULL;
IPPTsPng *ipptp_clear = NULL;
if (ip_port.ip.family == AF_INET) {
ipptp_write = &client->assoc4;
ipptp_clear = &client->assoc6;
} else {
ipptp_write = &client->assoc6;
ipptp_clear = &client->assoc4;
}
if (is_timeout(ipptp->timestamp, BAD_NODE_TIMEOUT)) {
memcpy(client->client_id, client_id, CLIENT_ID_SIZE);
ipptp->ip_port = ip_port;
ipptp->timestamp = unix_time();
ipptp_write->ip_port = ip_port;
ipptp_write->timestamp = unix_time();
ip_reset(&ipptp->ret_ip_port.ip);
ipptp->ret_ip_port.port = 0;
ipptp->ret_timestamp = 0;
ip_reset(&ipptp_write->ret_ip_port.ip);
ipptp_write->ret_ip_port.port = 0;
ipptp_write->ret_timestamp = 0;
/* zero out other address */
memset(ipptp_clear, 0, sizeof(*ipptp_clear));
return 0;
}
@ -451,7 +485,9 @@ static void sort_list(Client_data *list, uint32_t length, uint8_t *comp_client_i
list[i] = pairs[i].c2;
}
/* Replace the first good node that is further to the comp_client_id than that of the client_id in the list */
/* Replace the first good node that is further to the comp_client_id than that of the client_id in the list
*
* returns 0 when the item was stored, 1 otherwise */
static int replace_good( Client_data *list,
uint32_t length,
uint8_t *client_id,
@ -482,20 +518,28 @@ static int replace_good( Client_data *list,
assert(replace >= 0 && replace < length);
#endif
Client_data *client = &list[replace];
IPPTsPng *ipptp = NULL;
IPPTsPng *ipptp_write = NULL;
IPPTsPng *ipptp_clear = NULL;
if (ip_port.ip.family == AF_INET)
ipptp = &client->assoc4;
else
ipptp = &client->assoc6;
if (ip_port.ip.family == AF_INET) {
ipptp_write = &client->assoc4;
ipptp_clear = &client->assoc6;
} else {
ipptp_write = &client->assoc6;
ipptp_clear = &client->assoc4;
}
memcpy(client->client_id, client_id, CLIENT_ID_SIZE);
ipptp->ip_port = ip_port;
ipptp->timestamp = unix_time();
ipptp_write->ip_port = ip_port;
ipptp_write->timestamp = unix_time();
ip_reset(&ipptp_write->ret_ip_port.ip);
ipptp_write->ret_ip_port.port = 0;
ipptp_write->ret_timestamp = 0;
/* zero out other address */
memset(ipptp_clear, 0, sizeof(*ipptp_clear));
ip_reset(&ipptp->ret_ip_port.ip);
ipptp->ret_ip_port.port = 0;
ipptp->ret_timestamp = 0;
return 0;
}
@ -504,10 +548,12 @@ static int replace_good( Client_data *list,
/* Attempt to add client with ip_port and client_id to the friends client list
* and close_clientlist.
*
* returns 1+ if the item is used in any list, 0 else
*/
void addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id)
int addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id)
{
uint32_t i;
uint32_t i, used = 0;
/* convert IPv4-in-IPv6 to IPv4 */
if ((ip_port.ip.family == AF_INET6) && IN6_IS_ADDR_V4MAPPED(&ip_port.ip.ip6.in6_addr)) {
@ -521,10 +567,13 @@ void addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id)
if (!client_or_ip_port_in_list(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) {
if (replace_bad(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) {
/* If we can't replace bad nodes we try replacing good ones. */
replace_good(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port,
dht->c->self_public_key);
}
}
if (!replace_good(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port,
dht->c->self_public_key))
used++;
} else
used++;
} else
used++;
for (i = 0; i < dht->num_friends; ++i) {
if (!client_or_ip_port_in_list(dht->friends_list[i].client_list,
@ -533,17 +582,22 @@ void addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id)
if (replace_bad(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS,
client_id, ip_port)) {
/* If we can't replace bad nodes we try replacing good ones. */
replace_good(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS,
client_id, ip_port, dht->friends_list[i].client_id);
}
}
if (!replace_good(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS,
client_id, ip_port, dht->friends_list[i].client_id))
used++;
} else
used++;
} else
used++;
}
return used;
}
/* If client_id is a friend or us, update ret_ip_port
* nodeclient_id is the id of the node that sent us this info.
*/
static void returnedip_ports(DHT *dht, IP_Port ip_port, uint8_t *client_id, uint8_t *nodeclient_id)
static int returnedip_ports(DHT *dht, IP_Port ip_port, uint8_t *client_id, uint8_t *nodeclient_id)
{
uint32_t i, j;
uint64_t temp_time = unix_time();
@ -565,10 +619,9 @@ static void returnedip_ports(DHT *dht, IP_Port ip_port, uint8_t *client_id, uint
dht->close_clientlist[i].assoc6.ret_timestamp = temp_time;
}
return;
return 1;
}
}
} else {
for (i = 0; i < dht->num_friends; ++i) {
if (id_equal(client_id, dht->friends_list[i].client_id)) {
@ -582,13 +635,14 @@ static void returnedip_ports(DHT *dht, IP_Port ip_port, uint8_t *client_id, uint
dht->friends_list[i].client_list[j].assoc6.ret_timestamp = temp_time;
}
return;
return 1;
}
}
}
}
}
}
return 0;
}
#define NODES_ENCRYPTED_MESSAGE_LENGTH (crypto_secretbox_NONCEBYTES + sizeof(uint64_t) + sizeof(Node_format) + sizeof(Node_format) + crypto_secretbox_MACBYTES)
@ -870,7 +924,16 @@ static int handle_sendnodes_core(void *object, IP_Port source, uint8_t *packet,
return 1;
/* store the address the *request* was sent to */
addto_lists(dht, source, packet + 1);
int used = addto_lists(dht, source, packet + 1);
if (dht->assoc) {
IPPTs ippts;
ippts.ip_port = source;
ippts.timestamp = unix_time();
Assoc_add_entry(dht->assoc, packet + 1, &ippts, &source, used ? 1 : 0);
}
*num_nodes_out = num_nodes;
@ -890,24 +953,30 @@ static int handle_sendnodes(void *object, IP_Port source, uint8_t *packet, uint3
&sendback_node))
return 1;
Node4_format *nodes4_list = (Node4_format *)(plain);
uint32_t i;
IP_Port ipp;
ipp.ip.family = AF_INET;
Node4_format *nodes4_list = (Node4_format *)(plain);
uint64_t time_now = unix_time();
IPPTs ippts;
ippts.ip_port.ip.family = AF_INET;
ippts.timestamp = time_now;
uint32_t i;
Node_format nodes_list[MAX_SENT_NODES];
for (i = 0; i < num_nodes; i++)
if ((nodes4_list[i].ip_port.ip.uint32 != 0) && (nodes4_list[i].ip_port.ip.uint32 != (uint32_t)~0)) {
ipp.ip.ip4.uint32 = nodes4_list[i].ip_port.ip.uint32;
ipp.port = nodes4_list[i].ip_port.port;
ippts.ip_port.ip.ip4.uint32 = nodes4_list[i].ip_port.ip.uint32;
ippts.ip_port.port = nodes4_list[i].ip_port.port;
send_ping_request(dht->ping, ipp, nodes4_list[i].client_id);
returnedip_ports(dht, ipp, nodes4_list[i].client_id, packet + 1);
send_ping_request(dht->ping, ippts.ip_port, nodes4_list[i].client_id);
int used = returnedip_ports(dht, ippts.ip_port, nodes4_list[i].client_id, packet + 1);
memcpy(nodes_list[i].client_id, nodes4_list[i].client_id, CLIENT_ID_SIZE);
ipport_copy(&nodes_list[i].ip_port, &ipp);
ipport_copy(&nodes_list[i].ip_port, &ippts.ip_port);
if (dht->assoc)
Assoc_add_entry(dht->assoc, nodes4_list[i].client_id, &ippts, NULL, used ? 1 : 0);
}
send_hardening_getnode_res(dht, &sendback_node, packet + 1, nodes_list, num_nodes);
@ -928,13 +997,22 @@ static int handle_sendnodes_ipv6(void *object, IP_Port source, uint8_t *packet,
return 1;
Node_format *nodes_list = (Node_format *)(plain);
uint64_t time_now = unix_time();
uint32_t i;
send_hardening_getnode_res(dht, &sendback_node, packet + 1, nodes_list, num_nodes);
for (i = 0; i < num_nodes; i++)
if (ipport_isset(&nodes_list[i].ip_port)) {
send_ping_request(dht->ping, nodes_list[i].ip_port, nodes_list[i].client_id);
returnedip_ports(dht, nodes_list[i].ip_port, nodes_list[i].client_id, packet + 1);
int used = returnedip_ports(dht, nodes_list[i].ip_port, nodes_list[i].client_id, packet + 1);
if (dht->assoc) {
IPPTs ippts;
ippts.ip_port = nodes_list[i].ip_port;
ippts.timestamp = time_now;
Assoc_add_entry(dht->assoc, nodes_list[i].client_id, &ippts, NULL, used ? 1 : 0);
}
}
return 0;
@ -983,7 +1061,38 @@ int DHT_addfriend(DHT *dht, uint8_t *client_id)
dht->friends_list[dht->num_friends].nat.NATping_id = ((uint64_t)random_int() << 32) + random_int();
++dht->num_friends;
get_bunchnodes(dht, dht->close_clientlist, LCLIENT_LIST, MAX_FRIEND_CLIENTS, client_id);/*TODO: make this better?*/
if (dht->assoc) {
/* get up to MAX_FRIEND_CLIENTS connectable nodes */
DHT_Friend *friend = &dht->friends_list[dht->num_friends - 1];
Assoc_close_entries close_entries;
memset(&close_entries, 0, sizeof(close_entries));
close_entries.wanted_id = client_id;
close_entries.count_good = MAX_FRIEND_CLIENTS / 2;
close_entries.count = MAX_FRIEND_CLIENTS;
close_entries.result = calloc(MAX_FRIEND_CLIENTS, sizeof(*close_entries.result));
uint8_t i, found = Assoc_get_close_entries(dht->assoc, &close_entries);
for (i = 0; i < found; i++)
memcpy(&friend->client_list[i], close_entries.result[i], sizeof(*close_entries.result[i]));
if (found) {
/* send getnodes to the "best" entry */
Client_data *client = &friend->client_list[0];
if (ipport_isset(&client->assoc4.ip_port))
getnodes(dht, client->assoc4.ip_port, client->client_id, friend->client_id, NULL);
if (ipport_isset(&client->assoc6.ip_port))
getnodes(dht, client->assoc6.ip_port, client->client_id, friend->client_id, NULL);
}
}
/*TODO: make this better?*/
get_bunchnodes(dht, dht->close_clientlist, LCLIENT_LIST, MAX_FRIEND_CLIENTS, client_id);
return 0;
}
@ -1055,10 +1164,12 @@ int DHT_getfriendip(DHT *dht, uint8_t *client_id, IP_Port *ip_port)
return -1;
}
static void do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, uint8_t *client_id,
/* returns number of nodes not in kill-timeout */
static uint8_t do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, uint8_t *client_id,
Client_data *list, uint32_t list_count)
{
uint32_t i;
uint8_t not_kill = 0;
uint64_t temp_time = unix_time();
uint32_t num_nodes = 0;
@ -1073,6 +1184,8 @@ static void do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, uint8
for (a = 0, assoc = &client->assoc6; a < 2; a++, assoc = &client->assoc4)
if (!is_timeout(assoc->timestamp, KILL_NODE_TIMEOUT)) {
not_kill++;
if (is_timeout(assoc->last_pinged, PING_INTERVAL)) {
send_ping_request(dht->ping, assoc->ip_port, client->client_id );
assoc->last_pinged = temp_time;
@ -1093,6 +1206,8 @@ static void do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, uint8
client_id, NULL);
*lastgetnode = temp_time;
}
return not_kill;
}
/* Ping each client in the "friends" list every PING_INTERVAL seconds. Send a get nodes request
@ -1112,12 +1227,40 @@ static void do_DHT_friends(DHT *dht)
*/
static void do_Close(DHT *dht)
{
do_ping_and_sendnode_requests(dht, &dht->close_lastgetnodes, dht->c->self_public_key,
uint8_t not_killed = do_ping_and_sendnode_requests(dht, &dht->close_lastgetnodes, dht->c->self_public_key,
dht->close_clientlist, LCLIENT_LIST);
if (!not_killed) {
/* all existing nodes are at least KILL_NODE_TIMEOUT,
* which means we are mute, as we only send packets to
* nodes NOT in KILL_NODE_TIMEOUT
*
* so: reset all nodes to be BAD_NODE_TIMEOUT, but not
* KILL_NODE_TIMEOUT, so we at least keep trying pings */
uint64_t badonly = unix_time() - BAD_NODE_TIMEOUT;
size_t i, a;
for (i = 0; i < LCLIENT_LIST; i++) {
Client_data *client = &dht->close_clientlist[i];
IPPTsPng *assoc;
for (a = 0, assoc = &client->assoc4; a < 2; a++, assoc = &client->assoc6)
if (assoc->timestamp)
assoc->timestamp = badonly;
}
}
}
void DHT_bootstrap(DHT *dht, IP_Port ip_port, uint8_t *public_key)
{
if (dht->assoc) {
IPPTs ippts;
ippts.ip_port = ip_port;
ippts.timestamp = 0;
Assoc_add_entry(dht->assoc, public_key, &ippts, NULL, 0);
}
getnodes(dht, ip_port, public_key, dht->c->self_public_key, NULL);
}
int DHT_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enabled,
@ -1191,29 +1334,24 @@ static int friend_iplist(DHT *dht, IP_Port *ip_portlist, uint16_t friend_num)
int num_ipv4s = 0;
IP_Port ipv6s[MAX_FRIEND_CLIENTS];
int num_ipv6s = 0;
uint8_t connected;
int i;
for (i = 0; i < MAX_FRIEND_CLIENTS; ++i) {
client = &(friend->client_list[i]);
connected = 0;
/* If ip is not zero and node is good. */
if (ip_isset(&client->assoc4.ret_ip_port.ip) && !is_timeout(client->assoc4.ret_timestamp, BAD_NODE_TIMEOUT)) {
ipv4s[num_ipv4s] = client->assoc4.ret_ip_port;
++num_ipv4s;
connected = 1;
}
if (ip_isset(&client->assoc6.ret_ip_port.ip) && !is_timeout(client->assoc6.ret_timestamp, BAD_NODE_TIMEOUT)) {
ipv6s[num_ipv6s] = client->assoc6.ret_ip_port;
++num_ipv6s;
connected = 1;
}
if (connected && id_equal(client->client_id, friend->client_id))
if (id_equal(client->client_id, friend->client_id))
if (!is_timeout(client->assoc6.timestamp, BAD_NODE_TIMEOUT) || !is_timeout(client->assoc4.timestamp, BAD_NODE_TIMEOUT))
return 0; /* direct connectivity */
}
@ -1498,7 +1636,7 @@ static void punch_holes(DHT *dht, IP ip, uint16_t *port_list, uint16_t numports,
uint16_t firstport = port_list[0];
for (i = 0; i < numports; ++i) {
if (firstport != port_list[0])
if (firstport != port_list[i])
break;
}
@ -1844,6 +1982,8 @@ DHT *new_DHT(Net_Crypto *c)
cryptopacket_registerhandler(c, CRYPTO_PACKET_HARDENING, &handle_hardening, dht);
new_symmetric_key(dht->secret_symmetric_key);
dht->assoc = new_Assoc_default(dht->c->self_public_key);
return dht;
}
@ -1851,14 +1991,20 @@ void do_DHT(DHT *dht)
{
unix_time_update();
if (dht->last_run == unix_time()) {
return;
}
do_Close(dht);
do_DHT_friends(dht);
do_NAT(dht);
do_toping(dht->ping);
do_hardening(dht);
dht->last_run = unix_time();
}
void kill_DHT(DHT *dht)
{
kill_Assoc(dht->assoc);
kill_ping(dht->ping);
free(dht->friends_list);
free(dht);

View File

@ -38,6 +38,22 @@
/* Maximum newly announced nodes to ping per TIME_TOPING seconds. */
#define MAX_TOPING 16
/* Ping timeout in seconds */
#define PING_TIMEOUT 3
/* Ping interval in seconds for each node in our lists. */
#define PING_INTERVAL 60
/* The number of seconds for a non responsive node to become bad. */
#define PINGS_MISSED_NODE_GOES_BAD 3
#define PING_ROUNDTRIP 2
#define BAD_NODE_TIMEOUT (PING_INTERVAL + PINGS_MISSED_NODE_GOES_BAD * PING_INTERVAL + PING_ROUNDTRIP)
typedef struct {
IP_Port ip_port;
uint64_t timestamp;
} IPPTs;
typedef struct {
/* Node routes request correctly (true (1) or false/didn't check (0)) */
uint8_t routes_requests_ok;
@ -114,20 +130,22 @@ typedef struct {
Net_Crypto *c;
Client_data close_clientlist[LCLIENT_LIST];
DHT_Friend *friends_list;
uint16_t num_friends;
uint64_t close_lastgetnodes;
void *ping;
/* Note: this key should not be/is not used to transmit any sensitive materials */
uint8_t secret_symmetric_key[crypto_secretbox_KEYBYTES];
DHT_Friend *friends_list;
uint16_t num_friends;
struct PING *ping;
struct Assoc *assoc;
uint64_t last_run;
} DHT;
/*----------------------------------------------------------------------------------*/
Client_data *DHT_get_close_list(DHT *dht);
/* Add a new friend to the friends list.
* client_id must be CLIENT_ID_SIZE bytes long.
*
@ -247,7 +265,7 @@ void kill_DHT(DHT *dht);
*/
int DHT_isconnected(DHT *dht);
void addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id);
int addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id);
#endif

View File

@ -109,7 +109,7 @@ static uint32_t send_broadcasts(Networking_Core *net, uint16_t port, uint8_t *da
int i;
for (i = 0; i < broadcast_count; i++)
sendpacket(net, broadcast_ip_port[i], data, 1 + crypto_box_PUBLICKEYBYTES);
sendpacket(net, broadcast_ip_port[i], data, length);
return 1;
}
@ -176,6 +176,11 @@ int LAN_ip(IP ip)
&& ip4.uint8[2] != 255)
return 0;
/* RFC 6598: 100.64.0.0 to 100.127.255.255 (100.64.0.0/10)
* (shared address space to stack another layer of NAT) */
if ((ip4.uint8[0] == 100) && ((ip4.uint8[1] & 0xC0) == 0x40))
return 0;
} else if (ip.family == AF_INET6) {
/* autogenerated for each interface: FE80::* (up to FEBF::*)
@ -191,6 +196,10 @@ int LAN_ip(IP ip)
ip4.ip4.uint32 = ip.ip6.uint32[3];
return LAN_ip(ip4);
}
/* localhost in IPv6 (::1) */
if (IN6_IS_ADDR_LOOPBACK(&ip.ip6.in6_addr))
return 0;
}
return -1;

View File

@ -27,6 +27,8 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \
../toxcore/util.c \
../toxcore/group_chats.h \
../toxcore/group_chats.c \
../toxcore/assoc.h \
../toxcore/assoc.c \
../toxcore/misc_tools.h
libtoxcore_la_CFLAGS = -I$(top_srcdir) \

View File

@ -26,9 +26,11 @@
#endif
#include "Messenger.h"
#include "assoc.h"
#include "network.h"
#include "util.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
@ -435,6 +437,10 @@ int setname(Messenger *m, uint8_t *name, uint16_t length)
for (i = 0; i < m->numfriends; ++i)
m->friendlist[i].name_sent = 0;
for (i = 0; i < m->numchats; i++)
if (m->chats[i] != NULL)
set_nick(m->chats[i], name, length); /* TODO: remove this (group nicks should not be tied to the global one) */
return 0;
}
@ -701,6 +707,23 @@ int write_cryptpacket_id(Messenger *m, int friendnumber, uint8_t packet_id, uint
/**********GROUP CHATS************/
/* return 1 if the groupnumber is not valid.
* return 0 if the groupnumber is valid.
*/
static uint8_t groupnumber_not_valid(Messenger *m, int groupnumber)
{
if ((unsigned int)groupnumber >= m->numchats)
return 1;
if (m->chats == NULL)
return 1;
if (m->chats[groupnumber] == NULL)
return 1;
return 0;
}
/* returns valid ip port of connected friend on success
* returns zeroed out IP_Port on failure
*/
@ -728,6 +751,7 @@ static int group_num(Messenger *m, uint8_t *group_public_key)
uint32_t i;
for (i = 0; i < m->numchats; ++i) {
if (m->chats[i] != NULL)
if (id_equal(m->chats[i]->self_public_key, group_public_key))
return i;
}
@ -755,20 +779,51 @@ void m_callback_group_message(Messenger *m, void (*function)(Messenger *m, int,
m->group_message = function;
m->group_message_userdata = userdata;
}
/* Set callback function for peer name list changes.
*
* It gets called every time the name list changes(new peer/name, deleted peer)
* Function(Tox *tox, int groupnumber, void *userdata)
*/
void m_callback_group_namelistchange(Messenger *m, void (*function)(Messenger *m, int, int, uint8_t, void *), void *userdata)
{
m->group_namelistchange = function;
m->group_namelistchange_userdata = userdata;
}
static int get_chat_num(Messenger *m, Group_Chat *chat)
{
uint32_t i;
for (i = 0; i < m->numchats; ++i) { //TODO: remove this
if (m->chats[i] == chat)
return i;
}
return -1;
}
static void group_message_function(Group_Chat *chat, int peer_number, uint8_t *message, uint16_t length, void *userdata)
{
Messenger *m = userdata;
uint32_t i;
for (i = 0; i < m->numchats; ++i) { //TODO: remove this
if (m->chats[i] == chat)
break;
}
int i = get_chat_num(m, chat);
if (i == -1)
return;
if (m->group_message)
(*m->group_message)(m, i, peer_number, message, length, m->group_message_userdata);
}
static void group_namelistchange_function(Group_Chat *chat, int peer, uint8_t change, void *userdata)
{
Messenger *m = userdata;
int i = get_chat_num(m, chat);
if (i == -1)
return;
if (m->group_namelistchange)
(*m->group_namelistchange)(m, i, peer, change, m->group_namelistchange_userdata);
}
/* Creates a new groupchat and puts it in the chats array.
*
* return group number on success.
@ -786,6 +841,9 @@ int add_groupchat(Messenger *m)
return -1;
callback_groupmessage(newchat, &group_message_function, m);
callback_namelistchange(newchat, &group_namelistchange_function, m);
/* TODO: remove this (group nicks should not be tied to the global one) */
set_nick(newchat, m->name, m->name_length);
m->chats[i] = newchat;
return i;
}
@ -804,6 +862,9 @@ int add_groupchat(Messenger *m)
m->chats = temp;
callback_groupmessage(temp[m->numchats], &group_message_function, m);
callback_namelistchange(temp[m->numchats], &group_namelistchange_function, m);
/* TODO: remove this (group nicks should not be tied to the global one) */
set_nick(temp[m->numchats], m->name, m->name_length);
++m->numchats;
return (m->numchats - 1);
}
@ -953,6 +1014,7 @@ int join_groupchat(Messenger *m, int friendnumber, uint8_t *friend_group_public_
return -1;
}
/* send a group message
* return 0 on success
* return -1 on failure
@ -960,13 +1022,7 @@ int join_groupchat(Messenger *m, int friendnumber, uint8_t *friend_group_public_
int group_message_send(Messenger *m, int groupnumber, uint8_t *message, uint32_t length)
{
if ((unsigned int)groupnumber >= m->numchats)
return -1;
if (m->chats == NULL)
return -1;
if (m->chats[groupnumber] == NULL)
if (groupnumber_not_valid(m, groupnumber))
return -1;
if (group_sendmessage(m->chats[groupnumber], message, length) > 0)
@ -975,6 +1031,33 @@ int group_message_send(Messenger *m, int groupnumber, uint8_t *message, uint32_t
return -1;
}
/* Return the number of peers in the group chat on success.
* return -1 on failure
*/
int group_number_peers(Messenger *m, int groupnumber)
{
if (groupnumber_not_valid(m, groupnumber))
return -1;
return group_numpeers(m->chats[groupnumber]);
}
/* List all the peers in the group chat.
*
* Copies the names of the peers to the name[length][MAX_NICK_BYTES] array.
*
* returns the number of peers on success.
*
* return -1 on failure.
*/
int group_names(Messenger *m, int groupnumber, uint8_t names[][MAX_NICK_BYTES], uint16_t length)
{
if (groupnumber_not_valid(m, groupnumber))
return -1;
return group_client_names(m->chats[groupnumber], names, length);
}
static int handle_group(void *object, IP_Port source, uint8_t *packet, uint32_t length)
{
Messenger *m = object;
@ -1078,7 +1161,7 @@ int file_sendrequest(Messenger *m, int friendnumber, uint8_t filenumber, uint64_
int new_filesender(Messenger *m, int friendnumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length)
{
if (friend_not_valid(m, friendnumber))
return 0;
return -1;
uint32_t i;
@ -1102,28 +1185,28 @@ int new_filesender(Messenger *m, int friendnumber, uint64_t filesize, uint8_t *f
/* Send a file control request.
* send_receive is 0 if we want the control packet to target a sending file, 1 if it targets a receiving file.
*
* return 1 on success
* return 0 on failure
* return 0 on success
* return -1 on failure
*/
int file_control(Messenger *m, int friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t message_id,
uint8_t *data, uint16_t length)
{
if (length > MAX_DATA_SIZE - 3)
return 0;
return -1;
if (friend_not_valid(m, friendnumber))
return 0;
return -1;
if (send_receive == 1) {
if (m->friendlist[friendnumber].file_receiving[filenumber].status == FILESTATUS_NONE)
return 0;
return -1;
} else {
if (m->friendlist[friendnumber].file_sending[filenumber].status == FILESTATUS_NONE)
return 0;
return -1;
}
if (send_receive > 1)
return 0;
return -1;
uint8_t packet[MAX_DATA_SIZE];
packet[0] = send_receive;
@ -1133,7 +1216,7 @@ int file_control(Messenger *m, int friendnumber, uint8_t send_receive, uint8_t f
if (message_id == FILECONTROL_RESUME_BROKEN) {
if (length != sizeof(uint64_t))
return 0;
return -1;
uint8_t remaining[sizeof(uint64_t)];
memcpy(remaining, data, sizeof(uint64_t));
@ -1181,32 +1264,32 @@ int file_control(Messenger *m, int friendnumber, uint8_t send_receive, uint8_t f
break;
}
return 1;
} else {
return 0;
} else {
return -1;
}
}
#define MIN_SLOTS_FREE 4
/* Send file data.
*
* return 1 on success
* return 0 on failure
* return 0 on success
* return -1 on failure
*/
int file_data(Messenger *m, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length)
{
if (length > MAX_DATA_SIZE - 1)
return 0;
return -1;
if (friend_not_valid(m, friendnumber))
return 0;
return -1;
if (m->friendlist[friendnumber].file_sending[filenumber].status != FILESTATUS_TRANSFERRING)
return 0;
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)
return 0;
return -1;
uint8_t packet[MAX_DATA_SIZE];
packet[0] = filenumber;
@ -1214,10 +1297,10 @@ int file_data(Messenger *m, int friendnumber, uint8_t filenumber, uint8_t *data,
if (write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_DATA, packet, length + 1)) {
m->friendlist[friendnumber].file_sending[filenumber].transferred += length;
return 1;
return 0;
}
return 0;
return -1;
}
@ -1371,6 +1454,17 @@ int m_msi_packet(Messenger *m, int friendnumber, uint8_t *data, uint16_t length)
return write_cryptpacket_id(m, friendnumber, PACKET_ID_MSI, data, length);
}
/* Function to filter out some friend requests*/
static int friend_already_added(uint8_t *client_id, void *data)
{
Messenger *m = data;
if (getfriend_id(m, client_id) == -1)
return 0;
return -1;
}
/* Send a LAN discovery packet every LAN_DISCOVERY_INTERVAL seconds. */
static void LANdiscovery(Messenger *m)
{
@ -1420,6 +1514,8 @@ Messenger *new_messenger(uint8_t ipv6enabled)
friendreq_init(&(m->fr), m->net_crypto);
LANdiscovery_init(m->dht);
set_nospam(&(m->fr), random_int());
set_filter_function(&(m->fr), &friend_already_added, m);
networking_registerhandler(m->net, NET_PACKET_GROUP_CHATS, &handle_group, m);
return m;
@ -1431,6 +1527,10 @@ void kill_messenger(Messenger *m)
/* FIXME TODO: ideally cleanupMessenger will mirror initMessenger.
* This requires the other modules to expose cleanup functions.
*/
uint32_t i, numchats = m->numchats;
for (i = 0; i < numchats; ++i)
del_groupchat(m, i);
kill_DHT(m->dht);
kill_net_crypto(m->net_crypto);
kill_networking(m->net);
@ -1438,6 +1538,24 @@ void kill_messenger(Messenger *m)
free(m);
}
/* Check for and handle a timed-out friend request. If the request has
* timed-out then the friend status is set back to FRIEND_ADDED.
* i: friendlist index of the timed-out friend
* t: time
*/
static void check_friend_request_timed_out(Messenger *m, uint32_t i, uint64_t t)
{
Friend *f = &m->friendlist[i];
if (f->friendrequest_lastsent + f->friendrequest_timeout < t) {
set_friend_status(m, i, FRIEND_ADDED);
/* Double the default timeout everytime if friendrequest is assumed
* to have been sent unsuccessfully.
*/
f->friendrequest_timeout *= 2;
}
}
/* TODO: Make this function not suck. */
void do_friends(Messenger *m)
{
@ -1465,13 +1583,7 @@ void do_friends(Messenger *m)
/* If we didn't connect to friend after successfully sending him a friend request the request is deemed
* unsuccessful so we set the status back to FRIEND_ADDED and try again.
*/
if (m->friendlist[i].friendrequest_lastsent + m->friendlist[i].friendrequest_timeout < temp_time) {
set_friend_status(m, i, FRIEND_ADDED);
/* Double the default timeout everytime if friendrequest is assumed to have been
* sent unsuccessfully.
*/
m->friendlist[i].friendrequest_timeout *= 2;
}
check_friend_request_timed_out(m, i, temp_time);
}
IP_Port friendip;
@ -1539,12 +1651,15 @@ void do_friends(Messenger *m)
if (data_length >= MAX_NAME_LENGTH || data_length == 0)
break;
/* Make sure the NULL terminator is present. */
data[data_length - 1] = 0;
/* inform of namechange before we overwrite the old name */
if (m->friend_namechange)
m->friend_namechange(m, i, data, data_length, m->friend_namechange_userdata);
memcpy(m->friendlist[i].name, data, data_length);
m->friendlist[i].name_length = data_length;
m->friendlist[i].name[data_length - 1] = 0; /* Make sure the NULL terminator is present. */
if (m->friend_namechange)
m->friend_namechange(m, i, m->friendlist[i].name, data_length, m->friend_namechange_userdata);
break;
}
@ -1658,7 +1773,7 @@ void do_friends(Messenger *m)
break;
group_newpeer(m->chats[groupnum], data + crypto_box_PUBLICKEYBYTES);
chat_bootstrap(m->chats[groupnum], get_friend_ipport(m, i), data + crypto_box_PUBLICKEYBYTES);
break;
}
@ -1807,6 +1922,18 @@ void do_messenger(Messenger *m)
#ifdef LOGGING
if (unix_time() > lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) {
loglog(" = = = = = = = = \n");
Assoc_status(m->dht->assoc);
if (m->numchats > 0) {
size_t c;
for (c = 0; c < m->numchats; c++) {
loglog("---------------- \n");
Assoc_status(m->chats[c]->assoc);
}
}
loglog(" = = = = = = = = \n");
lastdump = unix_time();
@ -1833,7 +1960,30 @@ void do_messenger(Messenger *m)
loglog(" = = = = = = = = \n");
uint32_t num_friends = MIN(m->numfriends, m->dht->num_friends);
uint32_t friend, dhtfriend;
/* dht contains additional "friends" (requests) */
uint32_t num_dhtfriends = m->dht->num_friends;
int32_t m2dht[num_dhtfriends];
int32_t dht2m[num_dhtfriends];
for (friend = 0; friend < num_dhtfriends; friend++) {
m2dht[friend] = -1;
dht2m[friend] = -1;
if (friend >= m->numfriends)
continue;
for (dhtfriend = 0; dhtfriend < m->dht->num_friends; dhtfriend++)
if (id_equal(m->friendlist[friend].client_id, m->dht->friends_list[dhtfriend].client_id)) {
m2dht[friend] = dhtfriend;
break;
}
}
for (friend = 0; friend < num_dhtfriends; friend++)
if (m2dht[friend] >= 0)
dht2m[m2dht[friend]] = friend;
if (m->numfriends != m->dht->num_friends) {
sprintf(logbuffer, "Friend num in DHT %u != friend num in msger %u\n",
@ -1841,33 +1991,33 @@ void do_messenger(Messenger *m)
loglog(logbuffer);
}
uint32_t friend, ping_lastrecv;
uint32_t ping_lastrecv;
Friend *msgfptr;
DHT_Friend *dhtfptr;
for (friend = 0; friend < num_friends; friend++) {
Friend *msgfptr = &m->friendlist[friend];
DHT_Friend *dhtfptr = &m->dht->friends_list[friend];
for (friend = 0; friend < num_dhtfriends; friend++) {
if (dht2m[friend] >= 0)
msgfptr = &m->friendlist[dht2m[friend]];
else
msgfptr = NULL;
if (memcmp(msgfptr->client_id, dhtfptr->client_id, CLIENT_ID_SIZE)) {
if (sizeof(logbuffer) > 2 * CLIENT_ID_SIZE + 64) {
sprintf(logbuffer, "F[%2u] ID(m) %s != ID(d) ", friend,
ID2String(msgfptr->client_id));
strcat(logbuffer + strlen(logbuffer), ID2String(dhtfptr->client_id));
strcat(logbuffer + strlen(logbuffer), "\n");
} else
sprintf(logbuffer, "F[%2u] ID(m) != ID(d) ", friend);
loglog(logbuffer);
}
dhtfptr = &m->dht->friends_list[friend];
if (msgfptr) {
ping_lastrecv = lastdump - msgfptr->ping_lastrecv;
if (ping_lastrecv > 999)
ping_lastrecv = 999;
snprintf(logbuffer, sizeof(logbuffer), "F[%2u] <%s> %02u [%03u] %s\n",
friend, msgfptr->name, msgfptr->crypt_connection_id,
snprintf(logbuffer, sizeof(logbuffer), "F[%2u:%2u] <%s> %02i [%03u] %s\n",
dht2m[friend], friend, msgfptr->name, msgfptr->crypt_connection_id,
ping_lastrecv, ID2String(msgfptr->client_id));
loglog(logbuffer);
} else {
snprintf(logbuffer, sizeof(logbuffer), "F[--:%2u] %s\n",
friend, ID2String(dhtfptr->client_id));
loglog(logbuffer);
}
for (client = 0; client < MAX_FRIEND_CLIENTS; client++) {
Client_data *cptr = &dhtfptr->client_list[client];
@ -1995,6 +2145,9 @@ static int messenger_load_state_callback(void *outer, uint8_t *data, uint32_t le
if (length == crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t)) {
set_nospam(&(m->fr), *(uint32_t *)data);
load_keys(m->net_crypto, &data[sizeof(uint32_t)]);
if (m->dht->assoc)
Assoc_self_client_id_changed(m->dht->assoc, m->net_crypto->self_public_key);
} else
return -1; /* critical */
@ -2100,12 +2253,12 @@ uint32_t copy_friendlist(Messenger *m, int *out_list, uint32_t list_size)
uint32_t ret = 0;
for (i = 0; i < m->numfriends; i++) {
if (i >= list_size) {
if (ret >= list_size) {
break; /* Abandon ship */
}
if (m->friendlist[i].status > 0) {
out_list[i] = i;
out_list[ret] = i;
ret++;
}
}
@ -2146,3 +2299,51 @@ int get_friendlist(Messenger *m, int **out_list, uint32_t *out_list_length)
return 0;
}
/* Return the number of chats in the instance m.
* You should use this to determine how much memory to allocate
* for copy_chatlist. */
uint32_t count_chatlist(Messenger *m)
{
uint32_t ret = 0;
uint32_t i;
for (i = 0; i < m->numchats; i++) {
if (m->chats[i]) {
ret++;
}
}
return ret;
}
/* Copy a list of valid chat IDs into the array out_list.
* If out_list is NULL, returns 0.
* Otherwise, returns the number of elements copied.
* If the array was too small, the contents
* of out_list will be truncated to list_size. */
uint32_t copy_chatlist(Messenger *m, int *out_list, uint32_t list_size)
{
if (!out_list)
return 0;
if (m->numchats == 0) {
return 0;
}
uint32_t i;
uint32_t ret = 0;
for (i = 0; i < m->numchats; i++) {
if (ret >= list_size) {
break; /* Abandon ship */
}
if (m->chats[i]) {
out_list[ret] = i;
ret++;
}
}
return ret;
}

View File

@ -198,6 +198,8 @@ typedef struct Messenger {
void *group_invite_userdata;
void (*group_message)(struct Messenger *m, int, int, uint8_t *, uint16_t, void *);
void *group_message_userdata;
void (*group_namelistchange)(struct Messenger *m, int, int, uint8_t, void *);
void *group_namelistchange_userdata;
void (*file_sendrequest)(struct Messenger *m, int, uint8_t, uint64_t, uint8_t *, uint16_t, void *);
void *file_sendrequest_userdata;
@ -455,6 +457,13 @@ void m_callback_group_invite(Messenger *m, void (*function)(Messenger *m, int, u
void m_callback_group_message(Messenger *m, void (*function)(Messenger *m, int, int, uint8_t *, uint16_t, void *),
void *userdata);
/* Set callback function for peer name list changes.
*
* It gets called every time the name list changes(new peer/name, deleted peer)
* Function(Tox *tox, int groupnumber, void *userdata)
*/
void m_callback_group_namelistchange(Messenger *m, void (*function)(Messenger *m, int, int, uint8_t, void *), void *userdata);
/* Creates a new groupchat and puts it in the chats array.
*
* return group number on success.
@ -497,6 +506,21 @@ int join_groupchat(Messenger *m, int friendnumber, uint8_t *friend_group_public_
int group_message_send(Messenger *m, int groupnumber, uint8_t *message, uint32_t length);
/* Return the number of peers in the group chat on success.
* return -1 on failure
*/
int group_number_peers(Messenger *m, int groupnumber);
/* List all the peers in the group chat.
*
* Copies the names of the peers to the name[length][MAX_NICK_BYTES] array.
*
* returns the number of peers on success.
*
* return -1 on failure.
*/
int group_names(Messenger *m, int groupnumber, uint8_t names[][MAX_NICK_BYTES], uint16_t length);
/****************FILE SENDING*****************/
@ -633,5 +657,17 @@ uint32_t copy_friendlist(Messenger *m, int *out_list, uint32_t list_size);
*/
int get_friendlist(Messenger *m, int **out_list, uint32_t *out_list_length);
/* Return the number of chats in the instance m.
* You should use this to determine how much memory to allocate
* for copy_chatlist. */
uint32_t count_chatlist(Messenger *m);
/* Copy a list of valid chat IDs into the array out_list.
* If out_list is NULL, returns 0.
* Otherwise, returns the number of elements copied.
* If the array was too small, the contents
* of out_list will be truncated to list_size. */
uint32_t copy_chatlist(Messenger *m, int *out_list, uint32_t list_size);
#endif

926
toxcore/assoc.c Normal file
View File

@ -0,0 +1,926 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "DHT.h"
#include "assoc.h"
#include "ping.h"
#include "LAN_discovery.h"
#include <assert.h>
#include "util.h"
/*
* BASIC OVERVIEW:
*
* Hash: The client_id is hashed with a local hash function.
* Hashes are used in multiple places for searching.
* Bucket: The first n bits of the client_id are used to
* select a bucket. This speeds up sorting, but the more
* important reason is to enforce a spread in the space of
* client_ids available.
*
*
* Candidates:
*
* Candidates are kept in buckets of hash tables. The hash
* function is calculated from the client_id. Up to
* HASH_COLLIDE_COUNT alternative positions are tried if
* the inital position is already used by a different entry.
* The collision function is multiplicative, not additive.
*
* A new candidate can bump an existing candidate, if it is
* more "desirable": Seen beats Heard.
*/
/* candidates: alternative places for the same hash value */
#define HASH_COLLIDE_COUNT 5
/* bucket size shall be co-prime to this */
#define HASH_COLLIDE_PRIME 101
/* candidates: bump entries: timeout values for seen/heard to be considered of value */
#define CANDIDATES_SEEN_TIMEOUT 1800
#define CANDIDATES_HEARD_TIMEOUT 600
/* distance/index: index size & access mask */
#define DISTANCE_INDEX_INDEX_BITS (64 - DISTANCE_INDEX_DISTANCE_BITS)
#define DISTANCE_INDEX_INDEX_MASK ((1 << DISTANCE_INDEX_INDEX_BITS) - 1)
/* types to stay consistent */
typedef uint16_t bucket_t;
typedef uint32_t hash_t;
typedef uint16_t usecnt_t;
/* abbreviations ... */
typedef Assoc_distance_relative_callback dist_rel_cb;
typedef Assoc_distance_absolute_callback dist_abs_cb;
/*
* Client_data wrapped with additional data
*/
typedef struct Client_entry {
hash_t hash;
/* shortcuts & rumors: timers and data */
uint64_t used_at;
uint64_t seen_at;
uint64_t heard_at;
uint16_t seen_family;
uint16_t heard_family;
IP_Port assoc_heard4;
IP_Port assoc_heard6;
Client_data client;
} Client_entry;
typedef struct candidates_bucket {
Client_entry *list; /* hashed list */
} candidates_bucket;
struct Assoc {
hash_t self_hash; /* hash of self_client_id */
uint8_t self_client_id[CLIENT_ID_SIZE]; /* don't store entries for this */
/* association centralization: clients not in use */
size_t candidates_bucket_bits;
size_t candidates_bucket_count;
size_t candidates_bucket_size;
candidates_bucket *candidates;
};
/*****************************************************************************/
/* HELPER FUNCTIONS */
/*****************************************************************************/
/* the complete distance would be CLIENT_ID_SIZE long...
* returns DISTANCE_INDEX_DISTANCE_BITS valid bits */
static uint64_t id_distance(Assoc *assoc, void *callback_data, uint8_t *id_ref, uint8_t *id_test)
{
/* with BIG_ENDIAN, this would be a one-liner... */
uint64_t retval = 0;
uint8_t pos = 0, bits = DISTANCE_INDEX_DISTANCE_BITS;
while (bits > 8) {
uint8_t distance = abs((int8_t)id_ref[pos] ^ (int8_t)id_test[pos]);
retval = (retval << 8) | distance;
bits -= 8;
pos++;
}
return (retval << bits) | ((id_ref[pos] ^ id_test[pos]) >> (8 - bits));
}
/* qsort() callback for a sorting by id_distance() values */
static int dist_index_comp(const void *a, const void *b)
{
const uint64_t *_a = a;
const uint64_t *_b = b;
if (*_a < *_b)
return -1;
if (*_a > *_b)
return 1;
return 0;
}
/* get actual entry to a distance_index */
static Client_entry *dist_index_entry(Assoc *assoc, uint64_t dist_ind)
{
if ((dist_ind & DISTANCE_INDEX_INDEX_MASK) == DISTANCE_INDEX_INDEX_MASK)
return NULL;
size_t total = assoc->candidates_bucket_count * assoc->candidates_bucket_size;
uint32_t index = dist_ind & DISTANCE_INDEX_INDEX_MASK;
if (index < total) {
bucket_t b_id = index / assoc->candidates_bucket_size;
candidates_bucket *cnd_bckt = &assoc->candidates[b_id];
size_t b_ix = index % assoc->candidates_bucket_size;
Client_entry *entry = &cnd_bckt->list[b_ix];
if (entry->hash)
return entry;
}
return NULL;
}
/* get actual entry's client_id to a distance_index */
static uint8_t *dist_index_id(Assoc *assoc, uint64_t dist_ind)
{
Client_entry *entry = dist_index_entry(assoc, dist_ind);
if (entry)
return entry->client.client_id;
return NULL;
}
/* sorts first .. last, i.e. last is included */
static void dist_index_bubble(Assoc *assoc, uint64_t *dist_list, size_t first, size_t last, uint8_t *id,
void *custom_data, Assoc_distance_relative_callback dist_rel_func)
{
size_t i, k;
for (i = first; i <= last; i++) {
uint8_t *id1 = dist_index_id(assoc, dist_list[i]);
for (k = i + 1; k <= last; k++) {
uint8_t *id2 = dist_index_id(assoc, dist_list[k]);
if (id1 && id2)
if (dist_rel_func(assoc, custom_data, id, id1, id2) == 2) {
uint64_t swap = dist_list[i];
dist_list[i] = dist_list[k];
dist_list[k] = swap;
}
}
}
}
/* TODO: Check that there isn't a function like this elsewhere hidden.
* E.g. the one which creates a handshake_id isn't usable for this, it must
* always map the same ID to the same hash.
*
* Result is NOT MAPPED to CANDIDATES_TO_KEEP range, i.e. map before using
* it for list access. */
static hash_t id_hash(Assoc *assoc, uint8_t *id)
{
uint32_t i, res = 0x19a64e82;
for (i = 0; i < CLIENT_ID_SIZE; i++)
res = ((res << 1) ^ id[i]) + (res >> 31);
/* can't have zero as hash, a) marks an unused spot,
* b) collision function is multiplicative */
if (!(res % assoc->candidates_bucket_size))
res++;
return res;
}
/* up to HASH_COLLIDE_COUNT calls to different spots,
* result IS mapped to CANDIDATES_TO_KEEP range */
static hash_t hash_collide(Assoc *assoc, hash_t hash)
{
uint64_t hash64 = hash % assoc->candidates_bucket_size;
hash64 = (hash64 * HASH_COLLIDE_PRIME) % assoc->candidates_bucket_size;
hash_t retval = hash64;
/* this should never happen when CANDIDATES_TO_KEEP is prime and hash not a multiple
* (id_hash() checks for a multiple and returns a different hash in that case)
*
* ( 1 .. (prime - 1) is a group over multiplication and every number has its inverse
* in the group, so no multiplication should ever end on zero as long neither
* of the two factors was zero-equivalent )
*
* BUT: because the usage of the word "never" invokes Murphy's law, catch it */
if (!retval) {
#ifdef DEBUG
fprintf(stderr, "assoc::hash_collide: hash %u, bucket size %u => %u!", hash, (uint)assoc->candidates_bucket_size,
retval);
assert(retval != 0);
#endif
retval = 1;
}
return retval;
}
/* returns the "seen" assoc related to the ipp */
static IPPTsPng *entry_assoc(Client_entry *cl_entry, IP_Port *ipp)
{
if (!cl_entry)
return NULL;
if (ipp->ip.family == AF_INET)
return &cl_entry->client.assoc4;
if (ipp->ip.family == AF_INET6)
return &cl_entry->client.assoc6;
return NULL;
}
/* returns the "heard" assoc related to the ipp */
static IP_Port *entry_heard_get(Client_entry *entry, IP_Port *ipp)
{
if (ipp->ip.family == AF_INET)
return &entry->assoc_heard4;
else if (ipp->ip.family == AF_INET6)
return &entry->assoc_heard6;
else
return NULL;
}
/* store a "heard" entry
* overwrites empty entry, does NOT overwrite non-LAN ip with
* LAN ip
*
* returns 1 if the entry did change */
static int entry_heard_store(Client_entry *entry, IPPTs *ippts)
{
if (!entry || !ippts)
return 0;
if (!ipport_isset(&ippts->ip_port))
return 0;
IP_Port *heard, *ipp = &ippts->ip_port;
if (ipp->ip.family == AF_INET)
heard = &entry->assoc_heard4;
else if (ipp->ip.family == AF_INET6)
heard = &entry->assoc_heard6;
else
return 0;
if (ipport_equal(ipp, heard))
return 0;
if (!ipport_isset(heard)) {
*heard = *ipp;
entry->heard_at = ippts->timestamp;
entry->heard_family = ipp->ip.family;
return 1;
}
/* don't destroy a good address with a crappy one
* (unless we're very timed out) */
uint8_t LAN_ipp = LAN_ip(ipp->ip) == 0;
uint8_t LAN_entry = LAN_ip(heard->ip) == 0;
if (LAN_ipp && !LAN_entry && !is_timeout(entry->heard_at, CANDIDATES_HEARD_TIMEOUT))
return 0;
*heard = *ipp;
entry->heard_at = ippts->timestamp;
entry->heard_family = ipp->ip.family;
return 1;
}
/* maps Assoc callback signature to id_closest() */
static int assoc_id_closest(Assoc *assoc, void *callback_data, uint8_t *client_id, uint8_t *client_id1,
uint8_t *client_id2)
{
return id_closest(client_id, client_id1, client_id2);
}
static bucket_t id_bucket(uint8_t *id, uint8_t bits)
{
/* return the first "bits" bits of id */
bucket_t retval = 0;
uint8_t pos = 0;
while (bits > 8) {
retval = (retval << 8) | id[pos++];
bits -= 8;
}
return (retval << bits) | (id[pos] >> (8 - bits));
}
/*****************************************************************************/
/* CANDIDATES FUNCTIONS */
/*****************************************************************************/
static bucket_t candidates_id_bucket(Assoc *assoc, uint8_t *id)
{
return id_bucket(id, assoc->candidates_bucket_bits);
}
static uint8_t candidates_search(Assoc *assoc, uint8_t *id, hash_t hash, Client_entry **entryptr)
{
bucket_t bucket = candidates_id_bucket(assoc, id);
candidates_bucket *cnd_bckt = &assoc->candidates[bucket];
size_t coll, pos = hash % assoc->candidates_bucket_size;
for (coll = 0; coll < HASH_COLLIDE_COUNT; pos = hash_collide(assoc, pos) , coll++) {
Client_entry *entry = &cnd_bckt->list[pos];
if (entry->hash == hash)
if (id_equal(entry->client.client_id, id)) {
*entryptr = entry;
return 1;
}
}
*entryptr = NULL;
return 0;
}
static void candidates_update_assoc(Assoc *assoc, Client_entry *entry, uint8_t used, IPPTs *ippts_send,
IP_Port *ipp_recv)
{
if (!assoc || !entry || !ippts_send)
return;
IPPTsPng *ipptsp = entry_assoc(entry, &ippts_send->ip_port);
if (!ipptsp)
return;
if (used)
entry->used_at = unix_time();
/* do NOT do anything related to wanted, that's handled outside,
* just update the assoc (in the most sensible way)
*/
if (ipp_recv) {
ipptsp->ip_port = ippts_send->ip_port;
ipptsp->timestamp = ippts_send->timestamp;
ipptsp->ret_ip_port = *ipp_recv;
ipptsp->ret_timestamp = unix_time();
entry->seen_at = unix_time();
entry->seen_family = ippts_send->ip_port.ip.family;
return;
}
entry_heard_store(entry, ippts_send);
}
static uint8_t candidates_create_internal(Assoc *assoc, hash_t hash, uint8_t *id, uint8_t seen,
uint8_t used, bucket_t *bucketptr, size_t *posptr)
{
if (!assoc || !id || !bucketptr || !posptr)
return 0;
bucket_t bucket = candidates_id_bucket(assoc, id);
candidates_bucket *cnd_bckt = &assoc->candidates[bucket];
size_t coll, pos = hash % assoc->candidates_bucket_size, check;
size_t pos_check[6];
memset(pos_check, 0, sizeof(pos_check));
for (coll = 0; coll < HASH_COLLIDE_COUNT; pos = hash_collide(assoc, pos) , coll++) {
Client_entry *entry = &cnd_bckt->list[pos];
/* unset */
if (!entry->hash) {
*bucketptr = bucket;
*posptr = pos;
return 1;
}
/* 0. bad
* 1. seen bad, heard good
* 2. seen good
* 3. used */
if (!is_timeout(entry->used_at, BAD_NODE_TIMEOUT))
check = 3;
if (!is_timeout(entry->seen_at, CANDIDATES_SEEN_TIMEOUT))
check = 2;
else if (!is_timeout(entry->heard_at, CANDIDATES_HEARD_TIMEOUT))
check = 1;
else
check = 0;
if (!pos_check[check])
pos_check[check] = pos + 1;
}
/* used > seen > heard > bad */
size_t i, pos_max = used ? 3 : (seen ? 2 : 1);
for (i = 0; i < pos_max; i++)
if (pos_check[i]) {
*bucketptr = bucket;
*posptr = pos_check[i] - 1;
return 1;
}
return 0;
}
static uint8_t candidates_create_new(Assoc *assoc, hash_t hash, uint8_t *id, uint8_t used,
IPPTs *ippts_send, IP_Port *ipp_recv)
{
if (!assoc || !id || !ippts_send)
return 0;
bucket_t bucket;
size_t pos;
if (!candidates_create_internal(assoc, hash, id, ipp_recv != NULL, used, &bucket, &pos))
return 0;
candidates_bucket *cnd_bckt = &assoc->candidates[bucket];
Client_entry *entry = &cnd_bckt->list[pos];
memset(entry, 0, sizeof(*entry));
IPPTsPng *ipptsp = entry_assoc(entry, &ippts_send->ip_port);
if (!ipptsp)
return 0;
entry->hash = hash;
id_copy(entry->client.client_id, id);
if (used)
entry->used_at = unix_time();
if (ipp_recv && !ipport_isset(ipp_recv))
ipp_recv = NULL;
if (ipp_recv) {
entry->seen_at = ippts_send->timestamp;
entry->seen_family = ippts_send->ip_port.ip.family;
ipptsp->ip_port = ippts_send->ip_port;
ipptsp->timestamp = ippts_send->timestamp;
ipptsp->ret_ip_port = *ipp_recv;
ipptsp->ret_timestamp = unix_time();
} else {
IP_Port *heard = entry_heard_get(entry, &ippts_send->ip_port);
if (heard) {
entry->heard_at = ippts_send->timestamp;
entry->heard_family = ippts_send->ip_port.ip.family;
*heard = ippts_send->ip_port;
}
}
return 1;
}
/*****************************************************************************/
static void client_id_self_update(Assoc *assoc)
{
if (assoc->self_hash || !assoc->self_client_id)
return;
if (!assoc->self_hash) {
size_t i, sum = 0;
for (i = 0; i < crypto_box_PUBLICKEYBYTES; i++)
sum |= assoc->self_client_id[i];
if (!sum)
return;
assoc->self_hash = id_hash(assoc, assoc->self_client_id);
}
#ifdef LOGGING
loglog("assoc: id is now set, purging cache of self-references...\n");
#endif
/* if we already added some (or loaded some) entries,
* look and remove if we find a match
*/
bucket_t b_id = candidates_id_bucket(assoc, assoc->self_client_id);
candidates_bucket *cnd_bckt = &assoc->candidates[b_id];
size_t i, pos = assoc->self_hash % assoc->candidates_bucket_size;
for (i = 0; i < HASH_COLLIDE_COUNT; pos = hash_collide(assoc, pos), i++) {
Client_entry *entry = &cnd_bckt->list[pos];
if (entry->hash == assoc->self_hash)
if (id_equal(entry->client.client_id, assoc->self_client_id))
entry->hash = 0;
}
}
/*****************************************************************************/
/* TRIGGER FUNCTIONS */
/*****************************************************************************/
/* Central entry point for new associations: add a new candidate to the cache
* seen should be 0 (zero), if the candidate was announced by someone else,
* seen should be 1 (one), if there is confirmed connectivity (a definite response)
*/
uint8_t Assoc_add_entry(Assoc *assoc, uint8_t *id, IPPTs *ippts_send, IP_Port *ipp_recv, uint8_t used)
{
if (!assoc || !id || !ippts_send)
return 0;
if (!assoc->self_hash) {
client_id_self_update(assoc);
if (!assoc->self_hash)
return 0;
}
if (!ipport_isset(&ippts_send->ip_port))
return 0;
if (ipp_recv && !ipport_isset(ipp_recv))
ipp_recv = NULL;
hash_t hash = id_hash(assoc, id);
if (hash == assoc->self_hash)
if (id_equal(id, assoc->self_client_id))
return 0;
/* if it's new:
* callback, if there's desire, add to clients, else to candidates
*
* if it's "old":
* if it's client: refresh
* if it's candidate:
* if !ipp_recv, refresh
* if ipp_recv: callback, if there's desire, move to candidates
*/
Client_entry *cnd_entry;
if (!candidates_search(assoc, id, hash, &cnd_entry)) {
if (candidates_create_new(assoc, hash, id, used, ippts_send, ipp_recv))
return 1;
else
return 0;
} else {
candidates_update_assoc(assoc, cnd_entry, used, ippts_send, ipp_recv);
return 2;
}
}
/*****************************************************************************/
/* MAIN USE */
/*****************************************************************************/
uint8_t Assoc_get_close_entries(Assoc *assoc, Assoc_close_entries *state)
{
if (!assoc || !state || !state->wanted_id || !state->result)
return 0;
if (!assoc->self_hash) {
client_id_self_update(assoc);
if (!assoc->self_hash)
return 0;
}
if (!state->distance_relative_func)
state->distance_relative_func = assoc_id_closest;
if (!state->distance_absolute_func)
state->distance_absolute_func = id_distance;
size_t dist_list_len = assoc->candidates_bucket_count * assoc->candidates_bucket_size;
uint64_t dist_list[dist_list_len];
memset(dist_list, ~0, dist_list_len * sizeof(dist_list[0]));
bucket_t b;
size_t i;
for (b = 0; b < assoc->candidates_bucket_count; b++) {
candidates_bucket *cnd_bckt = &assoc->candidates[b];
for (i = 0; i < assoc->candidates_bucket_size; i++) {
Client_entry *entry = &cnd_bckt->list[i];
if (entry->hash) {
if (state->flags & ProtoIPv4) {
if (!ipport_isset(&entry->client.assoc4.ip_port))
continue;
if (!(state->flags & LANOk))
if (!LAN_ip(entry->client.assoc4.ip_port.ip))
continue;
}
if (state->flags & ProtoIPv6) {
if (!ipport_isset(&entry->client.assoc6.ip_port))
continue;
if (!(state->flags & LANOk))
if (!LAN_ip(entry->client.assoc6.ip_port.ip))
continue;
}
uint64_t dist = state->distance_absolute_func(assoc, state->custom_data, state->wanted_id, entry->client.client_id);
uint32_t index = b * assoc->candidates_bucket_size + i;
dist_list[index] = (dist << DISTANCE_INDEX_INDEX_BITS) | index;
}
}
}
qsort(dist_list, dist_list_len, sizeof(dist_list[0]), dist_index_comp);
/* ok, ok, it's not *perfectly* sorted, because we used an absolute distance
* go over the result and see if we need to "smoothen things out"
* because those should be only very few and short streaks, the worst regularly
* used sorting function aka bubble sort is used */
uint64_t dist_prev = ~0;
size_t ind_prev = ~0, ind_curr;
size_t len = 1;
for (ind_curr = 0; ind_curr < dist_list_len; ind_curr++) {
/* sorted increasingly, so an invalid entry marks the end */
if ((dist_list[ind_curr] & DISTANCE_INDEX_INDEX_MASK) == DISTANCE_INDEX_INDEX_MASK)
break;
uint64_t dist_curr = dist_list[ind_curr] >> DISTANCE_INDEX_INDEX_BITS;
if (dist_prev == dist_curr)
len++;
else {
if (len > 1)
dist_index_bubble(assoc, dist_list, ind_prev, ind_curr - 1, state->wanted_id, state->custom_data,
state->distance_relative_func);
dist_prev = dist_curr;
ind_prev = ind_curr;
len = 1;
}
}
if (len > 1)
dist_index_bubble(assoc, dist_list, ind_prev, ind_curr - 1, state->wanted_id, state->custom_data,
state->distance_relative_func);
/* ok, now dist_list is a strictly ascending sorted list of nodes
* a) extract CLOSE_QUOTA_USED clients, not timed out
* b) extract (1 - QUOTA) (better!) clients & candidates, not timed out
* c) save candidates which would be better, if contact can be established */
size_t client_quota_good = 0, pos = 0;
size_t client_quota_max = state->count_good;
ssize_t taken_last = - 1;
for (i = 0; (i < dist_list_len) && (pos < state->count); i++) {
Client_entry *entry = dist_index_entry(assoc, dist_list[i]);
if (entry && entry->hash) {
if (client_quota_good >= client_quota_max) {
state->result[pos++] = &entry->client;
taken_last = i;
} else {
if (state->flags & (ProtoIPv4 | ProtoIPv6)) {
if ((state->flags & ProtoIPv4) && is_timeout(entry->client.assoc4.timestamp, BAD_NODE_TIMEOUT))
continue;
if ((state->flags & ProtoIPv6) && is_timeout(entry->client.assoc6.timestamp, BAD_NODE_TIMEOUT))
continue;
} else if (is_timeout(entry->seen_at, BAD_NODE_TIMEOUT))
continue;
state->result[pos++] = &entry->client;
client_quota_good++;
taken_last = i;
}
}
}
/* if we had not enough valid entries the list might still not be filled.
*
* start again from last taken client, but leave out any requirement
*/
if (pos < state->count) {
for (i = taken_last + 1; (i < dist_list_len) && (pos < state->count); i++) {
Client_entry *entry = dist_index_entry(assoc, dist_list[i]);
if (entry && entry->hash)
state->result[pos++] = &entry->client;
}
}
return pos;
}
/*****************************************************************************/
/* GLOBAL STRUCTURE FUNCTIONS */
/*****************************************************************************/
static uint8_t odd_min9_is_prime(size_t value)
{
size_t i = 3;
while (i * i <= value) {
if (!(value % i))
return 0;
i += 2;
}
return 1;
}
static size_t prime_upto_min9(size_t limit)
{
/* even => odd */
limit = limit - (1 - (limit % 2));
while (!odd_min9_is_prime(limit))
limit -= 2;
return limit;
}
/* create */
Assoc *new_Assoc(size_t bits, size_t entries, uint8_t *public_id)
{
if (!public_id)
return NULL;
Assoc *assoc = calloc(1, sizeof(*assoc));
if (!assoc)
return NULL;
/*
* bits must be in [ 2 .. 15 ]
* entries must be a prime
*/
if (bits < 2)
bits = 2;
else if (bits > 15)
bits = 15;
assoc->candidates_bucket_bits = bits;
assoc->candidates_bucket_count = 1U << bits;
if (entries < 25) {
if (entries <= 6)
entries = 5;
else {
entries = entries - (1 - (entries % 2)); /* even => odd */
/* 7..23: all odds but 9&15 are prime */
if (!(entries % 3)) /* 9, 15 */
entries -= 2; /* 7, 13 */
}
} else if (entries > ((1 << 17) - 1)) /* 130k+ */
entries = (1 << 17) - 1;
else {
/* 9+: test and find a prime less or equal */
size_t entries_test = prime_upto_min9(entries);
if (entries_test == HASH_COLLIDE_PRIME) /* disallowed */
entries_test = prime_upto_min9(entries_test - 1);
if (entries_test != entries) {
#ifdef LOGGING
sprintf(logbuffer, "new_Assoc(): trimmed %i to %i.\n", (int)entries, (int)entries_test);
loglog(logbuffer);
#endif
entries = (size_t)entries_test;
}
}
assoc->candidates_bucket_size = entries;
/* allocation: preferably few blobs */
size_t bckt, cix;
Client_entry *clients = malloc(sizeof(*clients) * assoc->candidates_bucket_count * assoc->candidates_bucket_size);
candidates_bucket *lists = malloc(sizeof(*lists) * assoc->candidates_bucket_count);
for (bckt = 0; bckt < assoc->candidates_bucket_count; bckt++) {
candidates_bucket *list = &lists[bckt];
list->list = &clients[bckt * assoc->candidates_bucket_size];
for (cix = 0; cix < assoc->candidates_bucket_size; cix++)
list->list[cix].hash = 0;
}
assoc->candidates = lists;
id_copy(assoc->self_client_id, public_id);
client_id_self_update(assoc);
return assoc;
}
Assoc *new_Assoc_default(uint8_t *public_id)
{
/* original 8, 251 averages to ~32k entries... probably the whole DHT :D
* 320 entries is fine, hopefully */
return new_Assoc(6, 15, public_id);
}
/* own client_id, assocs for this have to be ignored */
void Assoc_self_client_id_changed(Assoc *assoc, uint8_t *id)
{
if (assoc && id) {
assoc->self_hash = 0;
id_copy(assoc->self_client_id, id);
client_id_self_update(assoc);
}
}
/* destroy */
void kill_Assoc(Assoc *assoc)
{
if (assoc) {
free(assoc->candidates->list);
free(assoc->candidates);
free(assoc);
}
}
#ifdef LOGGING
static char buffer[CLIENT_ID_SIZE * 2 + 1];
static char *idpart2str(uint8_t *id, size_t len)
{
if (len > CLIENT_ID_SIZE)
len = CLIENT_ID_SIZE;
size_t i;
for (i = 0; i < len; i++)
sprintf(buffer + i * 2, "%02hhx", id[i]);
buffer[len * 2] = 0;
return buffer;
}
void Assoc_status(Assoc *assoc)
{
if (!assoc) {
loglog("Assoc status: no assoc\n");
return;
}
loglog("[b:p] hash => [id...] used, seen, heard\n");
size_t bid, cid, total = 0;
for (bid = 0; bid < assoc->candidates_bucket_count; bid++) {
candidates_bucket *bucket = &assoc->candidates[bid];
for (cid = 0; cid < assoc->candidates_bucket_size; cid++) {
Client_entry *entry = &bucket->list[cid];
if (entry->hash) {
sprintf(logbuffer, "[%3i:%3i] %08x => [%s...] %i, %i(%c), %i(%c)\n",
(int)bid, (int)cid, entry->hash, idpart2str(entry->client.client_id, 8),
entry->used_at ? (int)(unix_time() - entry->used_at) : 0,
entry->seen_at ? (int)(unix_time() - entry->seen_at) : 0,
entry->seen_at ? (entry->seen_family == AF_INET ? '4' : (entry->seen_family == AF_INET6 ? '6' : '?')) : '?',
entry->heard_at ? (int)(unix_time() - entry->heard_at) : 0,
entry->heard_at ? (entry->heard_family == AF_INET ? '4' : (entry->heard_family == AF_INET6 ? '6' : '?')) : '?');
loglog(logbuffer);
total++;
}
}
}
if (total) {
sprintf(logbuffer, "Total: %i entries, table usage %i%%.\n", (int)total,
(int)(total * 100 / (assoc->candidates_bucket_count * assoc->candidates_bucket_size)));
loglog(logbuffer);
}
}
#endif

93
toxcore/assoc.h Normal file
View File

@ -0,0 +1,93 @@
#ifndef __ASSOC_H__
#define __ASSOC_H__
/* used by rendezvous */
#define ASSOC_AVAILABLE
/* For the legalese parts, see tox.h. */
/*
* Module to store currently unused ID <=> IP associations
* for a potential future use
*/
typedef struct Assoc Assoc;
/*****************************************************************************/
/* custom distance handler, if it's not ID-distance based
* return values exactly like id_closest() */
typedef int (*Assoc_distance_relative_callback)(Assoc *assoc, void *callback_data, uint8_t *client_id,
uint8_t *client_id1, uint8_t *client_id2);
#define DISTANCE_INDEX_DISTANCE_BITS 44
/* absolute distance: can be same for different client_id_check values
* return value should have DISTANCE_INDEX_DISTANCE_BITS valid bits */
typedef uint64_t (*Assoc_distance_absolute_callback)(Assoc *assoc, void *callback_data,
uint8_t *client_id_ref, uint8_t *client_id_check);
/*****************************************************************************/
/* Central entry point for new associations: add a new candidate to the cache
* returns 1 if entry is stored, 2 if existing entry was updated, 0 else */
uint8_t Assoc_add_entry(Assoc *assoc, uint8_t *id, IPPTs *ippts_send, IP_Port *ipp_recv, uint8_t used);
/*****************************************************************************/
typedef enum AssocCloseEntriesFlags {
ProtoIPv4 = 1,
ProtoIPv6 = 2,
LANOk = 4,
} AssocCloseEntriesFlags;
typedef struct Assoc_close_entries {
void *custom_data; /* given to distance functions */
uint8_t *wanted_id; /* the target client_id */
uint8_t flags; /* additional flags */
Assoc_distance_relative_callback distance_relative_func;
Assoc_distance_absolute_callback distance_absolute_func;
uint8_t count_good; /* that many should be "good" w.r.t. timeout */
uint8_t count; /* allocated number of close_indices */
Client_data **result;
} Assoc_close_entries;
/* find up to close_count nodes to put into close_nodes_used of ID_Nodes
* the distance functions can be NULL, then standard distance functions will be used
* the caller is responsible for allocating close_indices of sufficient size
*
* returns 0 on error
* returns the number of found nodes and the list of indices usable by Assoc_client()
* the caller is assumed to be registered from Assoc_register_callback()
* if they aren't, they should copy the Client_data and call Assoc_client_drop()
*/
uint8_t Assoc_get_close_entries(Assoc *assoc, Assoc_close_entries *close_entries);
/*****************************************************************************/
/* create: default sizes (6, 5 => 320 entries) */
Assoc *new_Assoc_default(uint8_t *public_id);
/* create: customized sizes
* total is (2^bits) * entries
* bits should be between 2 and 15 (else it's trimmed)
* entries will be reduced to the closest prime smaller or equal
*
* preferably bits should be large and entries small to ensure spread
* in the search space (e. g. 5, 5 is preferable to 2, 41) */
Assoc *new_Assoc(size_t bits, size_t entries, uint8_t *public_id);
/* public_id changed (loaded), update which entry isn't stored */
void Assoc_self_client_id_changed(Assoc *assoc, uint8_t *public_id);
/* destroy */
void kill_Assoc(Assoc *assoc);
#ifdef LOGGING
void Assoc_status(Assoc *assoc);
#endif
#endif /* !__ASSOC_H__ */

View File

@ -96,6 +96,12 @@ void callback_friendrequest(Friend_Requests *fr, void (*function)(uint8_t *, uin
fr->handle_friendrequest_isset = 1;
fr->handle_friendrequest_userdata = userdata;
}
/* Set the function used to check if a friend request should be displayed to the user or not. */
void set_filter_function(Friend_Requests *fr, int (*function)(uint8_t *, void *), void *userdata)
{
fr->filter_function = function;
fr->filter_function_userdata = userdata;
}
/* Add to list of received friend requests. */
static void addto_receivedlist(Friend_Requests *fr, uint8_t *client_id)
@ -141,6 +147,10 @@ static int friendreq_handlepacket(void *object, IP_Port source, uint8_t *source_
if (memcmp(packet, &fr->nospam, sizeof(fr->nospam)) != 0)
return 1;
if (fr->filter_function)
if ((*fr->filter_function)(source_pubkey, fr->filter_function_userdata) != 0)
return 1;
addto_receivedlist(fr, source_pubkey);
(*fr->handle_friendrequest)(source_pubkey, packet + 4, length - 4, fr->handle_friendrequest_userdata);
return 0;

View File

@ -34,6 +34,8 @@ typedef struct {
uint8_t handle_friendrequest_isset;
void *handle_friendrequest_userdata;
int (*filter_function)(uint8_t *, void *);
void *filter_function_userdata;
/* NOTE: The following is just a temporary fix for the multiple friend requests received at the same time problem.
* TODO: Make this better (This will most likely tie in with the way we will handle spam.)
*/
@ -53,11 +55,17 @@ void set_nospam(Friend_Requests *fr, uint32_t num);
uint32_t get_nospam(Friend_Requests *fr);
/* Set the function that will be executed when a friend request for us is received.
* Function format is function(uint8_t * public_key, uint8_t * data, uint16_t length)
* Function format is function(uint8_t * public_key, uint8_t * data, uint16_t length, void * userdata)
*/
void callback_friendrequest(Friend_Requests *fr, void (*function)(uint8_t *, uint8_t *, uint16_t, void *),
void *userdata);
/* Set the function used to check if a friend request should be displayed to the user or not.
* Function format is int function(uint8_t * public_key, void * userdata)
* It must return 0 if the request is ok (anything else if it is bad.)
*/
void set_filter_function(Friend_Requests *fr, int (*function)(uint8_t *, void *), void *userdata);
/* Sets up friendreq packet handlers. */
void friendreq_init(Friend_Requests *fr, Net_Crypto *c);

View File

@ -27,6 +27,8 @@
#endif
#include "group_chats.h"
#include "assoc.h"
#include "LAN_discovery.h"
#include "util.h"
#define GROUPCHAT_MAXDATA_LENGTH (MAX_DATA_SIZE - (1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES))
@ -53,7 +55,6 @@ typedef struct {
} sendnodes_data;
/*
* check if peer with client_id is in peer array.
*
@ -211,31 +212,48 @@ static int addpeer(Group_Chat *chat, uint8_t *client_id)
memset(&(temp[chat->numpeers]), 0, sizeof(Group_Peer));
chat->group = temp;
id_copy(chat->group[chat->numpeers].client_id, client_id);
chat->group[chat->numpeers].last_recv = unix_time();
chat->group[chat->numpeers].last_recv_msgping = unix_time();
++chat->numpeers;
if (chat->peer_namelistchange != NULL)
(*chat->peer_namelistchange)(chat, chat->numpeers - 1, CHAT_CHANGE_PEER_ADD, chat->group_namelistchange_userdata);
return (chat->numpeers - 1);
}
/*
* Delete a peer to the group chat.
* Delete a peer from the group chat.
*
* return 0 if success
* return -1 if error.
*/
static int delpeer(Group_Chat *chat, uint8_t *client_id)
static int delpeer(Group_Chat *chat, int peernum)
{
uint32_t i;
Group_Peer *temp;
if ((uint32_t)peernum >= chat->numpeers)
return -1;
for (i = 0; i < chat->numpeers; ++i) {
/* Equal */
if (id_equal(chat->group[i].client_id, client_id)) {
uint32_t i;
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* If peer is in close list, time it out forcefully. */
if (id_equal(chat->close[i].client_id, chat->group[peernum].client_id)) {
chat->close[i].last_recv = 0;
break;
}
}
Group_Peer *temp;
--chat->numpeers;
if (chat->numpeers != i)
id_copy(chat->group[i].client_id, chat->group[chat->numpeers].client_id);
if (chat->numpeers == 0) {
free(chat->group);
chat->group = NULL;
return 0;
}
if (chat->numpeers != (uint32_t)peernum)
memcpy(&chat->group[peernum], &chat->group[chat->numpeers], sizeof(Group_Peer));
temp = realloc(chat->group, sizeof(Group_Peer) * (chat->numpeers));
@ -243,11 +261,12 @@ static int delpeer(Group_Chat *chat, uint8_t *client_id)
return -1;
chat->group = temp;
return 0;
}
if (chat->peer_namelistchange != NULL) {
(*chat->peer_namelistchange)(chat, peernum, CHAT_CHANGE_PEER_DEL, chat->group_namelistchange_userdata);
}
return -1;
return 0;
}
/* Copy the name of peernum to name.
@ -262,25 +281,44 @@ int group_peername(Group_Chat *chat, int peernum, uint8_t *name)
return -1;
if (chat->group[peernum].nick_len == 0) {
memcpy(name, "NSA Agent", 10); /* Kindly remind the user that someone with no name might be an NSA agent.*/
return 10;
/* memcpy(name, "NSA agent", 10); */ /* Srsly? */ /* Kindly remind the user that someone with no name might be a moronic NSA agent.*/
name[0] = 0;
return 0;
}
memcpy(name, chat->group[peernum].nick, chat->group[peernum].nick_len);
return chat->group[peernum].nick_len;
}
static void setnick(Group_Chat *chat, int peernum, uint8_t *contents, uint16_t contents_len)
{
if (contents_len > MAX_NICK_BYTES || contents_len == 0)
return;
/* same name as already stored? */
if (chat->group[peernum].nick_len == contents_len)
if (!memcmp(chat->group[peernum].nick, contents, contents_len))
return;
memcpy(chat->group[peernum].nick, contents, contents_len);
/* Force null termination */
chat->group[peernum].nick[contents_len - 1] = 0;
chat->group[peernum].nick_len = contents_len;
if (chat->peer_namelistchange != NULL)
(*chat->peer_namelistchange)(chat, peernum, CHAT_CHANGE_PEER_NAME, chat->group_namelistchange_userdata);
}
/* min time between pings sent to one peer in seconds */
/* TODO: move this to global section */
#define PING_TIMEOUT 5
#define GROUP_PING_TIMEOUT 5
static int send_getnodes(Group_Chat *chat, IP_Port ip_port, int peernum)
{
if ((uint32_t)peernum >= chat->numpeers)
return -1;
if (!is_timeout(chat->group[peernum].last_pinged, PING_TIMEOUT))
if (!is_timeout(chat->group[peernum].last_pinged, GROUP_PING_TIMEOUT))
return -1;
getnodes_data contents;
@ -288,6 +326,15 @@ static int send_getnodes(Group_Chat *chat, IP_Port ip_port, int peernum)
chat->group[peernum].last_pinged = unix_time();
chat->group[peernum].pingid = contents.pingid;
chat->group[peernum].ping_via = ip_port;
if (chat->assoc) {
IPPTs ippts;
ippts.timestamp = unix_time();
ippts.ip_port = ip_port;
Assoc_add_entry(chat->assoc, chat->group[peernum].client_id, &ippts, NULL, 1);
}
return send_groupchatpacket(chat, ip_port, chat->group[peernum].client_id, (uint8_t *)&contents, sizeof(contents),
CRYPTO_PACKET_GROUP_CHAT_GET_NODES);
@ -343,7 +390,7 @@ static int handle_sendnodes(Group_Chat *chat, IP_Port source, int peernum, uint8
if ((len - sizeof(uint64_t)) % sizeof(groupchat_nodes) != 0)
return 1;
if (is_timeout(chat->group[peernum].last_pinged, PING_TIMEOUT))
if (is_timeout(chat->group[peernum].last_pinged, GROUP_PING_TIMEOUT))
return 1;
sendnodes_data contents;
@ -355,6 +402,9 @@ static int handle_sendnodes(Group_Chat *chat, IP_Port source, int peernum, uint8
uint16_t numnodes = (len - sizeof(contents.pingid)) / sizeof(groupchat_nodes);
uint32_t i;
IPPTs ippts_send;
ippts_send.timestamp = unix_time();
for (i = 0; i < numnodes; ++i) {
if (peer_okping(chat, contents.nodes[i].client_id) > 0) {
int peern = peer_in_chat(chat, contents.nodes[i].client_id);
@ -367,14 +417,31 @@ static int handle_sendnodes(Group_Chat *chat, IP_Port source, int peernum, uint8
continue;
send_getnodes(chat, contents.nodes[i].ip_port, peern);
if (chat->assoc) {
ippts_send.ip_port = contents.nodes[i].ip_port;
Assoc_add_entry(chat->assoc, contents.nodes[i].client_id, &ippts_send, NULL, 0);
}
}
}
add_closepeer(chat, chat->group[peernum].client_id, source);
int ok = add_closepeer(chat, chat->group[peernum].client_id, source);
if (chat->assoc) {
ippts_send.ip_port = chat->group[peernum].ping_via;
ippts_send.timestamp = chat->group[peernum].last_pinged;
IP_Port ipp_recv;
ipp_recv = source;
Assoc_add_entry(chat->assoc, contents.nodes[i].client_id, &ippts_send, &ipp_recv, ok == 0 ? 1 : 0);
}
return 0;
}
#define GROUP_DATA_MIN_SIZE (crypto_box_PUBLICKEYBYTES + sizeof(uint32_t) + 1)
static void send_names_new_peer(Group_Chat *chat);
static int handle_data(Group_Chat *chat, uint8_t *data, uint32_t len)
{
@ -385,6 +452,7 @@ static int handle_data(Group_Chat *chat, uint8_t *data, uint32_t len)
int peernum = peer_in_chat(chat, data);
if (peernum == -1) { /*NOTE: This is just for testing and will be removed later.*/
if (data[crypto_box_PUBLICKEYBYTES + sizeof(uint32_t)] != GROUP_CHAT_QUIT)
peernum = addpeer(chat, data);
}
@ -427,6 +495,21 @@ static int handle_data(Group_Chat *chat, uint8_t *data, uint32_t len)
return 1;
addpeer(chat, contents);
send_names_new_peer(chat);
break;
case GROUP_CHAT_QUIT: /* If peer tells us he is quitting */
if (contents_len != 0)
return 1;
delpeer(chat, peernum);
break;
case GROUP_CHAT_PEER_NICK:
if (contents_len > MAX_NICK_BYTES || contents_len == 0)
return 1;
setnick(chat, peernum, contents, contents_len);
break;
case GROUP_CHAT_CHAT_MESSAGE: /* If message is chat message */
@ -521,6 +604,30 @@ uint32_t group_sendmessage(Group_Chat *chat, uint8_t *message, uint32_t length)
return send_data(chat, message, length, GROUP_CHAT_CHAT_MESSAGE); //TODO: better return values?
}
/*
* Send id/nick combo to the group.
*
* returns the number of peers it has sent it to.
*/
static uint32_t group_send_nick(Group_Chat *chat, uint8_t *nick, uint16_t nick_len)
{
if (nick_len > MAX_NICK_BYTES)
return 0;
return send_data(chat, nick, nick_len, GROUP_CHAT_PEER_NICK);
}
int set_nick(Group_Chat *chat, uint8_t *nick, uint16_t nick_len)
{
if (nick_len > MAX_NICK_BYTES || nick_len == 0)
return -1;
memcpy(chat->nick, nick, nick_len);
chat->nick_len = nick_len;
group_send_nick(chat, chat->nick, chat->nick_len);
return 0;
}
uint32_t group_newpeer(Group_Chat *chat, uint8_t *client_id)
{
addpeer(chat, client_id);
@ -534,6 +641,26 @@ void callback_groupmessage(Group_Chat *chat, void (*function)(Group_Chat *chat,
chat->group_message_userdata = userdata;
}
void callback_namelistchange(Group_Chat *chat, void (*function)(Group_Chat *chat, int peer, uint8_t change, void *), void *userdata)
{
chat->peer_namelistchange = function;
chat->group_namelistchange_userdata = userdata;
}
uint32_t group_numpeers(Group_Chat *chat)
{
return chat->numpeers;
}
uint32_t group_client_names(Group_Chat *chat, uint8_t names[][MAX_NICK_BYTES], uint16_t length)
{
uint32_t i;
for (i = 0; i < chat->numpeers && i < length; ++i) {
group_peername(chat, i, names[i]);
}
return i;
}
Group_Chat *new_groupchat(Networking_Core *net)
{
unix_time_update();
@ -544,6 +671,10 @@ Group_Chat *new_groupchat(Networking_Core *net)
Group_Chat *chat = calloc(1, sizeof(Group_Chat));
chat->net = net;
crypto_box_keypair(chat->self_public_key, chat->self_secret_key);
/* (2^4) * 5 = 80 entries seems to be a moderate size */
chat->assoc = new_Assoc(4, 5, chat->self_public_key);
return chat;
}
@ -554,7 +685,6 @@ static void ping_close(Group_Chat *chat)
uint32_t i;
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
/* previous condition was always true, assuming this is the wanted one: */
if (!is_timeout(chat->close[i].last_recv, BAD_GROUPNODE_TIMEOUT)) {
int peernum = peer_in_chat(chat, chat->close[i].client_id);
@ -583,12 +713,32 @@ static void del_dead_peers(Group_Chat *chat)
uint32_t i;
for (i = 0; i < chat->numpeers; ++i) {
if (is_timeout(chat->group[i].last_recv_msgping, GROUP_PING_INTERVAL * 2)) {
delpeer(chat, chat->group[i].client_id);
if (is_timeout(chat->group[i].last_recv_msgping, GROUP_PING_INTERVAL * 4)) {
delpeer(chat, i);
}
}
}
#define NICK_SEND_INTERVAL 180
static void send_names_new_peer(Group_Chat *chat)
{
group_send_nick(chat, chat->nick, chat->nick_len);
chat->last_sent_nick = (unix_time() - NICK_SEND_INTERVAL) + 10;
}
static void send_names(Group_Chat *chat)
{
/* send own nick from time to time, to let newly added peers be informed
* first time only: use a shorter timeframe, because we might not be in our own
* peer list yet */
if (is_timeout(chat->last_sent_nick, 180))
if (group_send_nick(chat, chat->nick, chat->nick_len) > 0) {
if (!chat->last_sent_nick)
chat->last_sent_nick = (unix_time() - NICK_SEND_INTERVAL) + 10;
else
chat->last_sent_nick = unix_time();
}
}
void do_groupchat(Group_Chat *chat)
{
unix_time_update();
@ -596,10 +746,12 @@ void do_groupchat(Group_Chat *chat)
ping_group(chat);
/* TODO: Maybe run this less? */
del_dead_peers(chat);
send_names(chat);
}
void kill_groupchat(Group_Chat *chat)
{
send_data(chat, 0, 0, GROUP_CHAT_QUIT);
free(chat->group);
free(chat);
}

View File

@ -33,6 +33,7 @@ typedef struct {
uint8_t client_id[crypto_box_PUBLICKEYBYTES];
uint64_t pingid;
uint64_t last_pinged;
IP_Port ping_via;
uint64_t last_recv;
uint64_t last_recv_msgping;
@ -46,7 +47,6 @@ typedef struct {
uint8_t client_id[crypto_box_PUBLICKEYBYTES];
IP_Port ip_port;
uint64_t last_recv;
} Group_Close;
#define GROUP_CLOSE_CONNECTIONS 6
@ -63,12 +63,22 @@ typedef struct Group_Chat {
uint32_t message_number;
void (*group_message)(struct Group_Chat *m, int, uint8_t *, uint16_t, void *);
void *group_message_userdata;
void (*peer_namelistchange)(struct Group_Chat *m, int peer, uint8_t change, void *);
void *group_namelistchange_userdata;
uint64_t last_sent_ping;
uint8_t nick[MAX_NICK_BYTES];
uint16_t nick_len;
uint64_t last_sent_nick;
struct Assoc *assoc;
} Group_Chat;
#define GROUP_CHAT_PING 0
#define GROUP_CHAT_NEW_PEER 16
#define GROUP_CHAT_QUIT 24
#define GROUP_CHAT_PEER_NICK 48
#define GROUP_CHAT_CHAT_MESSAGE 64
/* Copy the name of peernum to name.
@ -84,9 +94,22 @@ int group_peername(Group_Chat *chat, int peernum, uint8_t *name);
*
* format of function is: function(Group_Chat *chat, peer number, message, message length, userdata)
*/
void callback_groupmessage(Group_Chat *chat, void (*function)(Group_Chat *chat, int, uint8_t *, uint16_t, void *),
void *userdata);
/*
* Set callback function for peer name list changes.
*
* It gets called every time the name list changes(new peer/name, deleted peer)
*
* format of function is: function(Group_Chat *chat, userdata)
*/
typedef enum {
CHAT_CHANGE_PEER_ADD,
CHAT_CHANGE_PEER_DEL,
CHAT_CHANGE_PEER_NAME,
} CHAT_CHANGE;
void callback_namelistchange(Group_Chat *chat, void (*function)(Group_Chat *chat, int peer, uint8_t change, void *), void *userdata);
/*
* Send a message to the group.
@ -95,6 +118,12 @@ void callback_groupmessage(Group_Chat *chat, void (*function)(Group_Chat *chat,
*/
uint32_t group_sendmessage(Group_Chat *chat, uint8_t *message, uint32_t length);
/*
* Set our nick for this group.
*
* returns -1 on failure, 0 on success.
*/
int set_nick(Group_Chat *chat, uint8_t *nick, uint16_t nick_len);
/*
* Tell everyone about a new peer (a person we are inviting for example.)
@ -112,6 +141,18 @@ uint32_t group_newpeer(Group_Chat *chat, uint8_t *client_id);
Group_Chat *new_groupchat(Networking_Core *net);
/* Return the number of peers in the group chat.
*/
uint32_t group_numpeers(Group_Chat *chat);
/* List all the peers in the group chat.
*
* Copies the names of the peers to the name[length][MAX_NICK_BYTES] array.
*
* returns the number of peers.
*/
uint32_t group_client_names(Group_Chat *chat, uint8_t names[][MAX_NICK_BYTES], uint16_t length);
/* Kill a group chat
*
* Frees the memory and everything.

View File

@ -310,9 +310,12 @@ typedef struct {
int networking_wait_prepare(Networking_Core *net, uint32_t sendqueue_length, uint8_t *data, uint16_t *lenptr)
{
if ((data == NULL) || (*lenptr < sizeof(select_info))) {
if ((data == NULL) || !lenptr || (*lenptr < sizeof(select_info))) {
if (lenptr) {
*lenptr = sizeof(select_info);
return 0;
} else
return -1;
}
*lenptr = sizeof(select_info);
@ -404,6 +407,10 @@ static int at_startup(void)
if (at_startup_ran != 0)
return 0;
#ifndef VANILLA_NACL
sodium_init();
#endif
#ifdef WIN32
WSADATA wsaData;
@ -678,12 +685,7 @@ int ip_equal(IP *a, IP *b)
if (a->family == AF_INET)
return (a->ip4.in_addr.s_addr == b->ip4.in_addr.s_addr);
else if (a->family == AF_INET6)
#ifdef WIN32
return IN6_ADDR_EQUAL(&a->ip6.in6_addr, &b->ip6.in6_addr);
#else
return IN6_ARE_ADDR_EQUAL(&a->ip6.in6_addr, &b->ip6.in6_addr);
#endif
else
return 0;
}

View File

@ -43,6 +43,18 @@ typedef unsigned int sock_t;
/* sa_family_t is the sockaddr_in / sockaddr_in6 family field */
typedef short sa_family_t;
#ifndef IN6_ARE_ADDR_EQUAL
#ifdef IN6_ADDR_EQUAL
#define IN6_ARE_ADDR_EQUAL(a,b) IN6_ADDR_EQUAL(a,b)
#else
#define IN6_ARE_ADDR_EQUAL(a,b) \
((((__const uint32_t *) (a))[0] == ((__const uint32_t *) (b))[0]) \
&& (((__const uint32_t *) (a))[1] == ((__const uint32_t *) (b))[1]) \
&& (((__const uint32_t *) (a))[2] == ((__const uint32_t *) (b))[2]) \
&& (((__const uint32_t *) (a))[3] == ((__const uint32_t *) (b))[3]))
#endif
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
@ -134,7 +146,7 @@ typedef union {
uint8_t uint8[8];
} IP4_Port;
typedef struct {
typedef struct IP_Port {
IP ip;
uint16_t port;
} IP_Port;

View File

@ -27,14 +27,16 @@
#include "config.h"
#endif
#include <stdbool.h>
#include <stdint.h>
#include "net_crypto.h"
#include "DHT.h"
#include "assoc.h"
#include "ping.h"
#include "network.h"
#include "util.h"
#define PING_NUM_MAX 384
#define PING_TIMEOUT 5 // 5s
/* Ping newly announced nodes to ping per TIME_TOPING seconds*/
#define TIME_TOPING 5
@ -45,7 +47,7 @@ typedef struct {
uint64_t timestamp;
} pinged_t;
typedef struct {
struct PING {
Net_Crypto *c;
pinged_t pings[PING_NUM_MAX];
@ -54,15 +56,9 @@ typedef struct {
Node_format toping[MAX_TOPING];
uint64_t last_toping;
} PING;
};
#define __PING_C__
#include "network.h"
#include "util.h"
#include "ping.h"
static bool is_ping_timeout(uint64_t time)
static int is_ping_timeout(uint64_t time)
{
return is_timeout(time, PING_TIMEOUT);
}
@ -265,7 +261,16 @@ static int handle_ping_response(void *_dht, IP_Port source, uint8_t *packet, uin
return 1;
/* Associate client_id with the ip the request was sent to */
addto_lists(dht, ping->pings[ping_index - 1].ip_port, packet + 1);
int used = addto_lists(dht, ping->pings[ping_index - 1].ip_port, packet + 1);
if (dht->assoc) {
IPPTs ippts;
ippts.ip_port = ping->pings[ping_index - 1].ip_port;
ippts.timestamp = ping->pings[ping_index - 1].timestamp;
Assoc_add_entry(dht->assoc, packet + 1, &ippts, &source, used > 0 ? 1 : 0);
}
return 0;
}

View File

@ -24,11 +24,7 @@
#ifndef __PING_H__
#define __PING_H__
#include <stdbool.h>
#ifndef __PING_C__
typedef struct PING PING;
#endif
/* Add nodes to the toping list.
* All nodes in this list are pinged every TIME_TOPING seconds

View File

@ -37,7 +37,7 @@ typedef struct Messenger Tox;
* Format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]
*
*/
void tox_getaddress(Tox *tox, uint8_t *address)
void tox_get_address(Tox *tox, uint8_t *address)
{
Messenger *m = tox;
getaddress(m, address);
@ -60,7 +60,7 @@ void tox_getaddress(Tox *tox, uint8_t *address)
* (the nospam for that friend was set to the new one).
* return FAERR_NOMEM if increasing the friend list size fails.
*/
int tox_addfriend(Tox *tox, uint8_t *address, uint8_t *data, uint16_t length)
int tox_add_friend(Tox *tox, uint8_t *address, uint8_t *data, uint16_t length)
{
Messenger *m = tox;
return m_addfriend(m, address, data, length);
@ -71,7 +71,7 @@ int tox_addfriend(Tox *tox, uint8_t *address, uint8_t *data, uint16_t length)
* return the friend number if success.
* return -1 if failure.
*/
int tox_addfriend_norequest(Tox *tox, uint8_t *client_id)
int tox_add_friend_norequest(Tox *tox, uint8_t *client_id)
{
Messenger *m = tox;
return m_addfriend_norequest(m, client_id);
@ -80,7 +80,7 @@ int tox_addfriend_norequest(Tox *tox, uint8_t *client_id)
/* return the friend id associated to that client id.
* return -1 if no such friend.
*/
int tox_getfriend_id(Tox *tox, uint8_t *client_id)
int tox_get_friend_id(Tox *tox, uint8_t *client_id)
{
Messenger *m = tox;
return getfriend_id(m, client_id);
@ -92,14 +92,14 @@ int tox_getfriend_id(Tox *tox, uint8_t *client_id)
* return 0 if success.
* return -1 if failure.
*/
int tox_getclient_id(Tox *tox, int friend_id, uint8_t *client_id)
int tox_get_client_id(Tox *tox, int friend_id, uint8_t *client_id)
{
Messenger *m = tox;
return getclient_id(m, friend_id, client_id);
}
/* Remove a friend. */
int tox_delfriend(Tox *tox, int friendnumber)
int tox_del_friend(Tox *tox, int friendnumber)
{
Messenger *m = tox;
return m_delfriend(m, friendnumber);
@ -111,7 +111,7 @@ int tox_delfriend(Tox *tox, int friendnumber)
* return 0 if friend is not connected to us (Offline).
* return -1 on failure.
*/
int tox_get_friend_connectionstatus(Tox *tox, int friendnumber)
int tox_get_friend_connection_status(Tox *tox, int friendnumber)
{
Messenger *m = tox;
return m_get_friend_connectionstatus(m, friendnumber);
@ -137,13 +137,13 @@ int tox_friend_exists(Tox *tox, int friendnumber)
* m_sendmessage_withid will send a message with the id of your choosing,
* however we can generate an id for you by calling plain m_sendmessage.
*/
uint32_t tox_sendmessage(Tox *tox, int friendnumber, uint8_t *message, uint32_t length)
uint32_t tox_send_message(Tox *tox, int friendnumber, uint8_t *message, uint32_t length)
{
Messenger *m = tox;
return m_sendmessage(m, friendnumber, message, length);
}
uint32_t tox_sendmessage_withid(Tox *tox, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length)
uint32_t tox_send_message_withid(Tox *tox, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length)
{
Messenger *m = tox;
return m_sendmessage_withid(m, friendnumber, theid, message, length);
@ -159,13 +159,13 @@ uint32_t tox_sendmessage_withid(Tox *tox, int friendnumber, uint32_t theid, uint
* m_sendaction_withid will send an action message with the id of your choosing,
* however we can generate an id for you by calling plain m_sendaction.
*/
uint32_t tox_sendaction(Tox *tox, int friendnumber, uint8_t *action, uint32_t length)
uint32_t tox_send_action(Tox *tox, int friendnumber, uint8_t *action, uint32_t length)
{
Messenger *m = tox;
return m_sendaction(m, friendnumber, action, length);
}
uint32_t tox_sendaction_withid(Tox *tox, int friendnumber, uint32_t theid, uint8_t *action, uint32_t length)
uint32_t tox_send_action_withid(Tox *tox, int friendnumber, uint32_t theid, uint8_t *action, uint32_t length)
{
Messenger *m = tox;
return m_sendaction_withid(m, friendnumber, theid, action, length);
@ -179,7 +179,7 @@ uint32_t tox_sendaction_withid(Tox *tox, int friendnumber, uint32_t theid, uint8
* return 0 if success.
* return -1 if failure.
*/
int tox_setname(Tox *tox, uint8_t *name, uint16_t length)
int tox_set_name(Tox *tox, uint8_t *name, uint16_t length)
{
Messenger *m = tox;
return setname(m, name, length);
@ -193,7 +193,7 @@ int tox_setname(Tox *tox, uint8_t *name, uint16_t length)
* return length of the name.
* return 0 on error.
*/
uint16_t tox_getselfname(Tox *tox, uint8_t *name, uint16_t nlen)
uint16_t tox_get_self_name(Tox *tox, uint8_t *name, uint16_t nlen)
{
Messenger *m = tox;
return getself_name(m, name, nlen);
@ -205,7 +205,7 @@ uint16_t tox_getselfname(Tox *tox, uint8_t *name, uint16_t nlen)
* return length of name (with the NULL terminator) if success.
* return -1 if failure.
*/
int tox_getname(Tox *tox, int friendnumber, uint8_t *name)
int tox_get_name(Tox *tox, int friendnumber, uint8_t *name)
{
Messenger *m = tox;
return getname(m, friendnumber, name);
@ -216,13 +216,13 @@ int tox_getname(Tox *tox, int friendnumber, uint8_t *name)
*
* return 0 on success, -1 on failure.
*/
int tox_set_statusmessage(Tox *tox, uint8_t *status, uint16_t length)
int tox_set_status_message(Tox *tox, uint8_t *status, uint16_t length)
{
Messenger *m = tox;
return m_set_statusmessage(m, status, length);
}
int tox_set_userstatus(Tox *tox, TOX_USERSTATUS status)
int tox_set_user_status(Tox *tox, TOX_USERSTATUS status)
{
Messenger *m = tox;
return m_set_userstatus(m, (USERSTATUS)status);
@ -231,7 +231,7 @@ int tox_set_userstatus(Tox *tox, TOX_USERSTATUS status)
/* return the length of friendnumber's status message, including null.
* Pass it into malloc.
*/
int tox_get_statusmessage_size(Tox *tox, int friendnumber)
int tox_get_status_message_size(Tox *tox, int friendnumber)
{
Messenger *m = tox;
return m_get_statusmessage_size(m, friendnumber);
@ -241,13 +241,13 @@ int tox_get_statusmessage_size(Tox *tox, int friendnumber)
* Get the size you need to allocate from m_get_statusmessage_size.
* The self variant will copy our own status message.
*/
int tox_copy_statusmessage(Tox *tox, int friendnumber, uint8_t *buf, uint32_t maxlen)
int tox_get_status_message(Tox *tox, int friendnumber, uint8_t *buf, uint32_t maxlen)
{
Messenger *m = tox;
return m_copy_statusmessage(m, friendnumber, buf, maxlen);
}
int tox_copy_self_statusmessage(Tox *tox, uint8_t *buf, uint32_t maxlen)
int tox_get_self_status_message(Tox *tox, uint8_t *buf, uint32_t maxlen)
{
Messenger *m = tox;
return m_copy_self_statusmessage(m, buf, maxlen);
@ -258,13 +258,13 @@ int tox_copy_self_statusmessage(Tox *tox, uint8_t *buf, uint32_t maxlen)
* As above, the self variant will return our own USERSTATUS.
* If friendnumber is invalid, this shall return USERSTATUS_INVALID.
*/
TOX_USERSTATUS tox_get_userstatus(Tox *tox, int friendnumber)
TOX_USERSTATUS tox_get_user_status(Tox *tox, int friendnumber)
{
Messenger *m = tox;
return (TOX_USERSTATUS)m_get_userstatus(m, friendnumber);
}
TOX_USERSTATUS tox_get_selfuserstatus(Tox *tox)
TOX_USERSTATUS tox_get_self_user_status(Tox *tox)
{
Messenger *m = tox;
return (TOX_USERSTATUS)m_get_self_userstatus(m);
@ -294,7 +294,7 @@ uint32_t tox_count_friendlist(Tox *tox)
* Otherwise, returns the number of elements copied.
* If the array was too small, the contents
* of out_list will be truncated to list_size. */
uint32_t tox_copy_friendlist(Tox *tox, int *out_list, uint32_t list_size)
uint32_t tox_get_friendlist(Tox *tox, int *out_list, uint32_t list_size)
{
Messenger *m = tox;
return copy_friendlist(m, out_list, list_size);
@ -303,7 +303,7 @@ uint32_t tox_copy_friendlist(Tox *tox, int *out_list, uint32_t list_size)
/* Set the function that will be executed when a friend request is received.
* Function format is function(uint8_t * public_key, uint8_t * data, uint16_t length)
*/
void tox_callback_friendrequest(Tox *tox, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata)
void tox_callback_friend_request(Tox *tox, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata)
{
Messenger *m = tox;
m_callback_friendrequest(m, function, userdata);
@ -313,7 +313,7 @@ void tox_callback_friendrequest(Tox *tox, void (*function)(uint8_t *, uint8_t *,
/* Set the function that will be executed when a message from a friend is received.
* Function format is: function(int friendnumber, uint8_t * message, uint32_t length)
*/
void tox_callback_friendmessage(Tox *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *),
void tox_callback_friend_message(Tox *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *),
void *userdata)
{
Messenger *m = tox;
@ -333,7 +333,7 @@ void tox_callback_action(Tox *tox, void (*function)(Messenger *tox, int, uint8_t
* function(int friendnumber, uint8_t *newname, uint16_t length)
* You are not responsible for freeing newname.
*/
void tox_callback_namechange(Tox *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *),
void tox_callback_name_change(Tox *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *),
void *userdata)
{
Messenger *m = tox;
@ -344,7 +344,7 @@ void tox_callback_namechange(Tox *tox, void (*function)(Messenger *tox, int, uin
* function(int friendnumber, uint8_t *newstatus, uint16_t length)
* You are not responsible for freeing newstatus.
*/
void tox_callback_statusmessage(Tox *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *),
void tox_callback_status_message(Tox *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *),
void *userdata)
{
Messenger *m = tox;
@ -354,7 +354,7 @@ void tox_callback_statusmessage(Tox *tox, void (*function)(Messenger *tox, int,
/* Set the callback for status type changes.
* function(int friendnumber, USERSTATUS kind)
*/
void tox_callback_userstatus(Tox *tox, void (*_function)(Tox *tox, int, TOX_USERSTATUS, void *), void *userdata)
void tox_callback_user_status(Tox *tox, void (*_function)(Tox *tox, int, TOX_USERSTATUS, void *), void *userdata)
{
Messenger *m = tox;
typedef void (*function_type)(Messenger *, int, USERSTATUS, void *);
@ -388,7 +388,7 @@ void tox_callback_read_receipt(Tox *tox, void (*function)(Messenger *tox, int, u
* being previously online" part. It's assumed that when adding friends,
* their connection status is offline.
*/
void tox_callback_connectionstatus(Tox *tox, void (*function)(Messenger *tox, int, uint8_t, void *), void *userdata)
void tox_callback_connection_status(Tox *tox, void (*function)(Messenger *tox, int, uint8_t, void *), void *userdata)
{
Messenger *m = tox;
m_callback_connectionstatus(m, function, userdata);
@ -415,6 +415,19 @@ void tox_callback_group_message(Tox *tox, void (*function)(Messenger *tox, int,
Messenger *m = tox;
m_callback_group_message(m, function, userdata);
}
/* Set callback function for peer name list changes.
*
* It gets called every time the name list changes(new peer/name, deleted peer)
* Function(Tox *tox, int groupnumber, void *userdata)
*/
void tox_callback_group_namelist_change(Tox *tox, void (*function)(Tox *tox, int, int, uint8_t, void *), void *userdata)
{
Messenger *m = tox;
m_callback_group_namelistchange(m, function, userdata);
}
/* Creates a new groupchat and puts it in the chats array.
*
* return group number on success.
@ -477,6 +490,48 @@ int tox_group_message_send(Tox *tox, int groupnumber, uint8_t *message, uint32_t
return group_message_send(m, groupnumber, message, length);
}
/* Return the number of peers in the group chat on success.
* return -1 on failure
*/
int tox_group_number_peers(Tox *tox, int groupnumber)
{
Messenger *m = tox;
return group_number_peers(m, groupnumber);
}
/* List all the peers in the group chat.
*
* Copies the names of the peers to the name[length][MAX_NICK_BYTES] array.
*
* returns the number of peers on success.
*
* return -1 on failure.
*/
int tox_group_get_names(Tox *tox, int groupnumber, uint8_t names[][TOX_MAX_NAME_LENGTH], uint16_t length)
{
Messenger *m = tox;
return group_names(m, groupnumber, names, length);
}
/* Return the number of chats in the instance m.
* You should use this to determine how much memory to allocate
* for copy_chatlist. */
uint32_t tox_count_chatlist(Tox *tox)
{
Messenger *m = tox;
return count_chatlist(m);
}
/* Copy a list of valid chat IDs into the array out_list.
* If out_list is NULL, returns 0.
* Otherwise, returns the number of elements copied.
* If the array was too small, the contents
* of out_list will be truncated to list_size. */
uint32_t tox_get_chatlist(Tox *tox, int *out_list, uint32_t list_size)
{
Messenger *m = tox;
return copy_chatlist(m, out_list, list_size);
}
/****************FILE SENDING FUNCTIONS*****************/
@ -486,7 +541,7 @@ int tox_group_message_send(Tox *tox, int groupnumber, uint8_t *message, uint32_t
*
* Function(Tox *tox, int friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length, void *userdata)
*/
void tox_callback_file_sendrequest(Tox *tox, void (*function)(Messenger *tox, int, uint8_t, uint64_t, uint8_t *,
void tox_callback_file_send_request(Tox *tox, void (*function)(Messenger *tox, int, uint8_t, uint64_t, uint8_t *,
uint16_t,
void *), void *userdata)
{
@ -522,7 +577,7 @@ void tox_callback_file_data(Tox *tox, void (*function)(Messenger *tox, int, uint
* return file number on success
* return -1 on failure
*/
int tox_new_filesender(Tox *tox, int friendnumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length)
int tox_new_file_sender(Tox *tox, int friendnumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length)
{
Messenger *m = tox;
return new_filesender(m, friendnumber, filesize, filename, filename_length);
@ -530,10 +585,10 @@ int tox_new_filesender(Tox *tox, int friendnumber, uint64_t filesize, uint8_t *f
/* Send a file control request.
* send_receive is 0 if we want the control packet to target a sending file, 1 if it targets a receiving file.
*
* return 1 on success
* return 0 on failure
* return 0 on success
* return -1 on failure
*/
int tox_file_sendcontrol(Tox *tox, int friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t message_id,
int tox_file_send_control(Tox *tox, int friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t message_id,
uint8_t *data, uint16_t length)
{
Messenger *m = tox;
@ -541,21 +596,21 @@ int tox_file_sendcontrol(Tox *tox, int friendnumber, uint8_t send_receive, uint8
}
/* Send file data.
*
* return 1 on success
* return 0 on failure
* return 0 on success
* return -1 on failure
*/
int tox_file_senddata(Tox *tox, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length)
int tox_file_send_data(Tox *tox, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length)
{
Messenger *m = tox;
return file_data(m, friendnumber, filenumber, data, length);
}
/* Returns the recommended/maximum size of the filedata you send with tox_file_senddata()
/* Returns the recommended/maximum size of the filedata you send with tox_file_send_data()
*
* return size on success
* return 0 on failure (currently will never return 0)
* return -1 on failure (currently will never return -1)
*/
int tox_filedata_size(Tox *tox, int friendnumber)
int tox_file_data_size(Tox *tox, int friendnumber)
{
return MAX_DATA_SIZE - crypto_box_MACBYTES - 3;
}
@ -567,7 +622,7 @@ int tox_filedata_size(Tox *tox, int friendnumber)
* return number of bytes remaining to be sent/received on success
* return 0 on failure
*/
uint64_t tox_file_dataremaining(Tox *tox, int friendnumber, uint8_t filenumber, uint8_t send_receive)
uint64_t tox_file_data_remaining(Tox *tox, int friendnumber, uint8_t filenumber, uint8_t send_receive)
{
Messenger *m = tox;
return file_dataremaining(m, friendnumber, filenumber, send_receive);

View File

@ -129,7 +129,7 @@ typedef struct Tox Tox;
/* return FRIEND_ADDRESS_SIZE byte address to give to others.
* format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]
*/
void tox_getaddress(Tox *tox, uint8_t *address);
void tox_get_address(Tox *tox, uint8_t *address);
/* Add a friend.
* Set the data that will be sent along with friend request.
@ -147,28 +147,28 @@ void tox_getaddress(Tox *tox, uint8_t *address);
* (the nospam for that friend was set to the new one).
* return TOX_FAERR_NOMEM if increasing the friend list size fails.
*/
int tox_addfriend(Tox *tox, uint8_t *address, uint8_t *data, uint16_t length);
int tox_add_friend(Tox *tox, uint8_t *address, uint8_t *data, uint16_t length);
/* Add a friend without sending a friendrequest.
* return the friend number if success.
* return -1 if failure.
*/
int tox_addfriend_norequest(Tox *tox, uint8_t *client_id);
int tox_add_friend_norequest(Tox *tox, uint8_t *client_id);
/* return the friend id associated to that client id.
return -1 if no such friend */
int tox_getfriend_id(Tox *tox, uint8_t *client_id);
int tox_get_friend_id(Tox *tox, uint8_t *client_id);
/* Copies the public key associated to that friend id into client_id buffer.
* Make sure that client_id is of size CLIENT_ID_SIZE.
* return 0 if success.
* return -1 if failure.
*/
int tox_getclient_id(Tox *tox, int friend_id, uint8_t *client_id);
int tox_get_client_id(Tox *tox, int friend_id, uint8_t *client_id);
/* Remove a friend. */
int tox_delfriend(Tox *tox, int friendnumber);
int tox_del_friend(Tox *tox, int friendnumber);
/* Checks friend's connecting status.
*
@ -176,7 +176,7 @@ int tox_delfriend(Tox *tox, int friendnumber);
* return 0 if friend is not connected to us (Offline).
* return -1 on failure.
*/
int tox_get_friend_connectionstatus(Tox *tox, int friendnumber);
int tox_get_friend_connection_status(Tox *tox, int friendnumber);
/* Checks if there exists a friend with given friendnumber.
*
@ -195,8 +195,8 @@ int tox_friend_exists(Tox *tox, int friendnumber);
* m_sendmessage_withid will send a message with the id of your choosing,
* however we can generate an id for you by calling plain m_sendmessage.
*/
uint32_t tox_sendmessage(Tox *tox, int friendnumber, uint8_t *message, uint32_t length);
uint32_t tox_sendmessage_withid(Tox *tox, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length);
uint32_t tox_send_message(Tox *tox, int friendnumber, uint8_t *message, uint32_t length);
uint32_t tox_send_message_withid(Tox *tox, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length);
/* Send an action to an online friend.
*
@ -208,8 +208,8 @@ uint32_t tox_sendmessage_withid(Tox *tox, int friendnumber, uint32_t theid, uint
* m_sendaction_withid will send an action message with the id of your choosing,
* however we can generate an id for you by calling plain m_sendaction.
*/
uint32_t tox_sendaction(Tox *tox, int friendnumber, uint8_t *action, uint32_t length);
uint32_t tox_sendaction_withid(Tox *tox, int friendnumber, uint32_t theid, uint8_t *action, uint32_t length);
uint32_t tox_send_action(Tox *tox, int friendnumber, uint8_t *action, uint32_t length);
uint32_t tox_send_action_withid(Tox *tox, int friendnumber, uint32_t theid, uint8_t *action, uint32_t length);
/* Set our nickname.
* name must be a string of maximum MAX_NAME_LENGTH length.
@ -219,7 +219,7 @@ uint32_t tox_sendaction_withid(Tox *tox, int friendnumber, uint32_t theid, uint8
* return 0 if success.
* return -1 if failure.
*/
int tox_setname(Tox *tox, uint8_t *name, uint16_t length);
int tox_set_name(Tox *tox, uint8_t *name, uint16_t length);
/*
* Get your nickname.
@ -230,7 +230,7 @@ int tox_setname(Tox *tox, uint8_t *name, uint16_t length);
* return length of name.
* return 0 on error.
*/
uint16_t tox_getselfname(Tox *tox, uint8_t *name, uint16_t nlen);
uint16_t tox_get_self_name(Tox *tox, uint8_t *name, uint16_t nlen);
/* Get name of friendnumber and put it in name.
* name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes.
@ -238,7 +238,7 @@ uint16_t tox_getselfname(Tox *tox, uint8_t *name, uint16_t nlen);
* return length of name (with the NULL terminator) if success.
* return -1 if failure.
*/
int tox_getname(Tox *tox, int friendnumber, uint8_t *name);
int tox_get_name(Tox *tox, int friendnumber, uint8_t *name);
/* Set our user status.
* You are responsible for freeing status after.
@ -246,13 +246,13 @@ int tox_getname(Tox *tox, int friendnumber, uint8_t *name);
* returns 0 on success.
* returns -1 on failure.
*/
int tox_set_statusmessage(Tox *tox, uint8_t *status, uint16_t length);
int tox_set_userstatus(Tox *tox, TOX_USERSTATUS status);
int tox_set_status_message(Tox *tox, uint8_t *status, uint16_t length);
int tox_set_user_status(Tox *tox, TOX_USERSTATUS status);
/* return the length of friendnumber's status message, including null.
* Pass it into malloc
*/
int tox_get_statusmessage_size(Tox *tox, int friendnumber);
int tox_get_status_message_size(Tox *tox, int friendnumber);
/* Copy friendnumber's status message into buf, truncating if size is over maxlen.
* Get the size you need to allocate from m_get_statusmessage_size.
@ -261,16 +261,16 @@ int tox_get_statusmessage_size(Tox *tox, int friendnumber);
* returns the length of the copied data on success
* retruns -1 on failure.
*/
int tox_copy_statusmessage(Tox *tox, int friendnumber, uint8_t *buf, uint32_t maxlen);
int tox_copy_self_statusmessage(Tox *tox, uint8_t *buf, uint32_t maxlen);
int tox_get_status_message(Tox *tox, int friendnumber, uint8_t *buf, uint32_t maxlen);
int tox_get_self_status_message(Tox *tox, uint8_t *buf, uint32_t maxlen);
/* return one of USERSTATUS values.
* Values unknown to your application should be represented as USERSTATUS_NONE.
* As above, the self variant will return our own USERSTATUS.
* If friendnumber is invalid, this shall return USERSTATUS_INVALID.
*/
TOX_USERSTATUS tox_get_userstatus(Tox *tox, int friendnumber);
TOX_USERSTATUS tox_get_selfuserstatus(Tox *tox);
TOX_USERSTATUS tox_get_user_status(Tox *tox, int friendnumber);
TOX_USERSTATUS tox_get_self_user_status(Tox *tox);
/* Sets whether we send read receipts for friendnumber.
* This function is not lazy, and it will fail if yesno is not (0 or 1).
@ -287,17 +287,17 @@ uint32_t tox_count_friendlist(Tox *tox);
* Otherwise, returns the number of elements copied.
* If the array was too small, the contents
* of out_list will be truncated to list_size. */
uint32_t tox_copy_friendlist(Tox *tox, int *out_list, uint32_t list_size);
uint32_t tox_get_friendlist(Tox *tox, int *out_list, uint32_t list_size);
/* Set the function that will be executed when a friend request is received.
* Function format is function(uint8_t * public_key, uint8_t * data, uint16_t length)
*/
void tox_callback_friendrequest(Tox *tox, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata);
void tox_callback_friend_request(Tox *tox, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata);
/* Set the function that will be executed when a message from a friend is received.
* Function format is: function(int friendnumber, uint8_t * message, uint32_t length)
*/
void tox_callback_friendmessage(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *),
void tox_callback_friend_message(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *),
void *userdata);
/* Set the function that will be executed when an action from a friend is received.
@ -309,20 +309,20 @@ void tox_callback_action(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, ui
* function(int friendnumber, uint8_t *newname, uint16_t length)
* You are not responsible for freeing newname
*/
void tox_callback_namechange(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *),
void tox_callback_name_change(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *),
void *userdata);
/* Set the callback for status message changes.
* function(int friendnumber, uint8_t *newstatus, uint16_t length)
* You are not responsible for freeing newstatus.
*/
void tox_callback_statusmessage(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *),
void tox_callback_status_message(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *),
void *userdata);
/* Set the callback for status type changes.
* function(int friendnumber, USERSTATUS kind)
*/
void tox_callback_userstatus(Tox *tox, void (*function)(Tox *tox, int, TOX_USERSTATUS, void *), void *userdata);
void tox_callback_user_status(Tox *tox, void (*function)(Tox *tox, int, TOX_USERSTATUS, void *), void *userdata);
/* Set the callback for read receipts.
* function(int friendnumber, uint32_t receipt)
@ -346,7 +346,7 @@ void tox_callback_read_receipt(Tox *tox, void (*function)(Tox *tox, int, uint32_
* being previously online" part. it's assumed that when adding friends,
* their connection status is offline.
*/
void tox_callback_connectionstatus(Tox *tox, void (*function)(Tox *tox, int, uint8_t, void *), void *userdata);
void tox_callback_connection_status(Tox *tox, void (*function)(Tox *tox, int, uint8_t, void *), void *userdata);
/**********GROUP CHAT FUNCTIONS: WARNING WILL BREAK A LOT************/
@ -363,6 +363,19 @@ void tox_callback_group_invite(Tox *tox, void (*function)(Tox *tox, int, uint8_t
void tox_callback_group_message(Tox *tox, void (*function)(Tox *tox, int, int, uint8_t *, uint16_t, void *),
void *userdata);
/* Set callback function for peer name list changes.
*
* It gets called every time the name list changes(new peer/name, deleted peer)
* Function(Tox *tox, int groupnumber, int peernumber, TOX_CHAT_CHANGE change, void *userdata)
*/
typedef enum {
TOX_CHAT_CHANGE_PEER_ADD,
TOX_CHAT_CHANGE_PEER_DEL,
TOX_CHAT_CHANGE_PEER_NAME,
} TOX_CHAT_CHANGE;
void tox_callback_group_namelist_change(Tox *tox, void (*function)(Tox *tox, int, int, uint8_t, void *), void *userdata);
/* Creates a new groupchat and puts it in the chats array.
*
* return group number on success.
@ -398,31 +411,57 @@ int tox_invite_friend(Tox *tox, int friendnumber, int groupnumber);
*/
int tox_join_groupchat(Tox *tox, int friendnumber, uint8_t *friend_group_public_key);
/* send a group message
* return 0 on success
* return -1 on failure
*/
int tox_group_message_send(Tox *tox, int groupnumber, uint8_t *message, uint32_t length);
/* Return the number of peers in the group chat on success.
* return -1 on failure
*/
int tox_group_number_peers(Tox *tox, int groupnumber);
/* List all the peers in the group chat.
*
* Copies the names of the peers to the name[length][TOX_MAX_NAME_LENGTH] array.
*
* returns the number of peers on success.
*
* return -1 on failure.
*/
int tox_group_get_names(Tox *tox, int groupnumber, uint8_t names[][TOX_MAX_NAME_LENGTH], uint16_t length);
/* Return the number of chats in the instance m.
* You should use this to determine how much memory to allocate
* for copy_chatlist. */
uint32_t tox_count_chatlist(Tox *tox);
/* Copy a list of valid chat IDs into the array out_list.
* If out_list is NULL, returns 0.
* Otherwise, returns the number of elements copied.
* If the array was too small, the contents
* of out_list will be truncated to list_size. */
uint32_t tox_get_chatlist(Tox *tox, int *out_list, uint32_t list_size);
/****************FILE SENDING FUNCTIONS*****************/
/* NOTE: This how to will be updated.
*
* HOW TO SEND FILES CORRECTLY:
* 1. Use tox_new_filesender(...) to create a new file sender.
* 1. Use tox_new_file_sender(...) to create a new file sender.
* 2. Wait for the callback set with tox_callback_file_control(...) to be called with receive_send == 1 and control_type == TOX_FILECONTROL_ACCEPT
* 3. Send the data with tox_file_senddata(...) with chunk size tox_filedata_size(...)
* 4. When sending is done, send a tox_file_sendcontrol(...) with send_receive = 0 and message_id = TOX_FILECONTROL_FINISHED
* 3. Send the data with tox_file_send_data(...) with chunk size tox_file_data_size(...)
* 4. When sending is done, send a tox_file_send_control(...) with send_receive = 0 and message_id = TOX_FILECONTROL_FINISHED
*
* HOW TO RECEIVE FILES CORRECTLY:
* 1. wait for the callback set with tox_callback_file_sendrequest(...)
* 2. accept or refuse the connection with tox_file_sendcontrol(...) with send_receive = 1 and message_id = TOX_FILECONTROL_ACCEPT or TOX_FILECONTROL_KILL
* 1. wait for the callback set with tox_callback_file_send_request(...)
* 2. accept or refuse the connection with tox_file_send_control(...) with send_receive = 1 and message_id = TOX_FILECONTROL_ACCEPT or TOX_FILECONTROL_KILL
* 3. save all the data received with the callback set with tox_callback_file_data(...) to a file.
* 4. when the callback set with tox_callback_file_control(...) is called with receive_send == 0 and control_type == TOX_FILECONTROL_FINISHED
* the file is done transferring.
*
* tox_file_dataremaining(...) can be used to know how many bytes are left to send/receive.
* tox_file_data_remaining(...) can be used to know how many bytes are left to send/receive.
*
* If the connection breaks during file sending (The other person goes offline without pausing the sending and then comes back)
* the reciever must send a control packet with receive_send == 0 message_id = TOX_FILECONTROL_RESUME_BROKEN and the data being
@ -445,7 +484,7 @@ enum {
*
* Function(Tox *tox, int friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length, void *userdata)
*/
void tox_callback_file_sendrequest(Tox *tox, void (*function)(Tox *m, int, uint8_t, uint64_t, uint8_t *, uint16_t,
void tox_callback_file_send_request(Tox *tox, void (*function)(Tox *m, int, uint8_t, uint64_t, uint8_t *, uint16_t,
void *), void *userdata);
/* Set the callback for file control requests.
@ -473,32 +512,32 @@ void tox_callback_file_data(Tox *tox, void (*function)(Tox *m, int, uint8_t, uin
* return file number on success
* return -1 on failure
*/
int tox_new_filesender(Tox *tox, int friendnumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length);
int tox_new_file_sender(Tox *tox, int friendnumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length);
/* Send a file control request.
*
* send_receive is 0 if we want the control packet to target a file we are currently sending,
* 1 if it targets a file we are currently receiving.
*
* return 1 on success
* return 0 on failure
* return 0 on success
* return -1 on failure
*/
int tox_file_sendcontrol(Tox *tox, int friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t message_id,
int tox_file_send_control(Tox *tox, int friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t message_id,
uint8_t *data, uint16_t length);
/* Send file data.
*
* return 1 on success
* return 0 on failure
* return 0 on success
* return -1 on failure
*/
int tox_file_senddata(Tox *tox, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length);
int tox_file_send_data(Tox *tox, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length);
/* Returns the recommended/maximum size of the filedata you send with tox_file_senddata()
/* Returns the recommended/maximum size of the filedata you send with tox_file_send_data()
*
* return size on success
* return 0 on failure (currently will never return 0)
* return -1 on failure (currently will never return -1)
*/
int tox_filedata_size(Tox *tox, int friendnumber);
int tox_file_data_size(Tox *tox, int friendnumber);
/* Give the number of bytes left to be sent/received.
*
@ -507,7 +546,7 @@ int tox_filedata_size(Tox *tox, int friendnumber);
* return number of bytes remaining to be sent/received on success
* return 0 on failure
*/
uint64_t tox_file_dataremaining(Tox *tox, int friendnumber, uint8_t filenumber, uint8_t send_receive);
uint64_t tox_file_data_remaining(Tox *tox, int friendnumber, uint8_t filenumber, uint8_t send_receive);
/***************END OF FILE SENDING FUNCTIONS******************/
@ -567,10 +606,11 @@ void tox_do(Tox *tox);
* Prepares the data required to call tox_wait_execute() asynchronously
*
* data[] is reserved and kept by the caller
* len is in/out: in = reserved data[], out = required data[]
* *lenptr is in/out: in = reserved data[], out = required data[]
*
* returns 1 on success
* returns 0 on failure (length is insufficient)
* returns 0 if *lenptr is insufficient
* returns -1 if lenptr is NULL
*
*
* tox_wait_execute(): function can be called asynchronously