Updated with upstream

This commit is contained in:
mannol 2015-04-17 19:19:58 +02:00
commit d1fd3e36a6
52 changed files with 7353 additions and 5551 deletions

View File

@ -419,6 +419,7 @@ START_TEST(test_client)
uint8_t f2_public_key[crypto_box_PUBLICKEYBYTES];
uint8_t f2_secret_key[crypto_box_SECRETKEYBYTES];
crypto_box_keypair(f2_public_key, f2_secret_key);
ip_port_tcp_s.port = htons(ports[rand() % NUM_PORTS]);
TCP_Client_Connection *conn2 = new_TCP_connection(ip_port_tcp_s, self_public_key, f2_public_key, f2_secret_key, 0);
routing_response_handler(conn, response_callback, ((void *)conn) + 2);
routing_status_handler(conn, status_callback, (void *)2);

View File

@ -29,13 +29,13 @@ unsigned char known_key2[crypto_box_BEFORENMBYTES] = {0x7a, 0xfa, 0x95, 0x45, 0x
// same as above, except standard opslimit instead of extra ops limit for test_known_kdf, and hash pw before kdf for compat
/* cause I'm shameless */
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
if (*((uint32_t *)userdata) != 974536)
return;
if (length == 7 && memcmp("Gentoo", data, 7) == 0) {
tox_add_friend_norequest(m, public_key);
tox_friend_add_norequest(m, public_key, 0);
}
}
@ -55,109 +55,116 @@ END_TEST
START_TEST(test_save_friend)
{
Tox *tox1 = tox_new(0);
Tox *tox2 = tox_new(0);
Tox *tox1 = tox_new(0, 0, 0, 0);
Tox *tox2 = tox_new(0, 0, 0, 0);
ck_assert_msg(tox1 || tox2, "Failed to create 2 tox instances");
uint32_t to_compare = 974536;
tox_callback_friend_request(tox2, accept_friend_request, &to_compare);
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(tox2, address);
int test = tox_add_friend(tox1, address, (uint8_t *)"Gentoo", 7);
ck_assert_msg(test == 0, "Failed to add friend error code: %i", test);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(tox2, address);
uint32_t test = tox_friend_add(tox1, address, (uint8_t *)"Gentoo", 7, 0);
ck_assert_msg(test != UINT32_MAX, "Failed to add friend");
uint32_t size = tox_encrypted_size(tox1);
size_t size = tox_get_savedata_size(tox1);
uint8_t data[size];
test = tox_encrypted_save(tox1, data, "correcthorsebatterystaple", 25);
ck_assert_msg(test == 0, "failed to encrypted save");
ck_assert_msg(tox_is_save_encrypted(data) == 1, "magic number missing");
tox_get_savedata(tox1, data);
size_t size2 = size + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t enc_data[size2];
TOX_ERR_ENCRYPTION error1;
bool ret = tox_pass_encrypt(data, size, "correcthorsebatterystaple", 25, enc_data, &error1);
ck_assert_msg(ret, "failed to encrypted save: %u", error1);
ck_assert_msg(tox_is_data_encrypted(enc_data), "magic number missing");
Tox *tox3 = tox_new(0);
test = tox_encrypted_load(tox3, data, size, "correcthorsebatterystaple", 25);
ck_assert_msg(test == 0, "failed to encrypted load");
uint8_t address2[TOX_CLIENT_ID_SIZE];
test = tox_get_client_id(tox3, 0, address2);
ck_assert_msg(test == 0, "no friends!");
ck_assert_msg(memcmp(address, address2, TOX_CLIENT_ID_SIZE) == 0, "addresses don't match!");
TOX_ERR_NEW err2;
Tox *tox3 = tox_new(0, enc_data, size2, &err2);
ck_assert_msg(err2 == TOX_ERR_NEW_LOAD_ENCRYPTED, "wrong error! %u. should fail with %u", err2,
TOX_ERR_NEW_LOAD_ENCRYPTED);
uint8_t dec_data[size];
TOX_ERR_DECRYPTION err3;
ret = tox_pass_decrypt(enc_data, size2, "correcthorsebatterystaple", 25, dec_data, &err3);
ck_assert_msg(ret, "failed to decrypt save: %u", err3);
tox3 = tox_new(0, dec_data, size, &err2);
ck_assert_msg(err2 == TOX_ERR_NEW_OK, "failed to load from decrypted data: %u", err2);
uint8_t address2[TOX_PUBLIC_KEY_SIZE];
ret = tox_friend_get_public_key(tox3, 0, address2, 0);
ck_assert_msg(ret, "no friends!");
ck_assert_msg(memcmp(address, address2, TOX_PUBLIC_KEY_SIZE) == 0, "addresses don't match!");
size = tox_encrypted_size(tox3);
size = tox_get_savedata_size(tox3);
uint8_t data2[size];
uint8_t key[32 + crypto_box_BEFORENMBYTES];
memcpy(key, salt, 32);
memcpy(key + 32, known_key2, crypto_box_BEFORENMBYTES);
test = tox_encrypted_key_save(tox3, data2, key);
ck_assert_msg(test == 0, "failed to encrypted save the second");
ck_assert_msg(tox_is_save_encrypted(data2) == 1, "magic number the second missing");
tox_get_savedata(tox3, data2);
TOX_PASS_KEY key;
memcpy(key.salt, salt, 32);
memcpy(key.key, known_key2, crypto_box_BEFORENMBYTES);
size2 = size + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t encdata2[size2];
ret = tox_pass_key_encrypt(data2, size, &key, encdata2, &error1);
ck_assert_msg(ret, "failed to key encrypt %u", error1);
ck_assert_msg(tox_is_data_encrypted(encdata2), "magic number the second missing");
// first test tox_encrypted_key_load
Tox *tox4 = tox_new(0);
test = tox_encrypted_key_load(tox4, data2, size, key);
ck_assert_msg(test == 0, "failed to encrypted load the second");
uint8_t address4[TOX_CLIENT_ID_SIZE];
test = tox_get_client_id(tox4, 0, address4);
ck_assert_msg(test == 0, "no friends! the second");
ck_assert_msg(memcmp(address, address2, TOX_CLIENT_ID_SIZE) == 0, "addresses don't match! the second");
// now test compaitibilty with tox_encrypted_load, first manually...
uint8_t out1[size], out2[size];
printf("Trying to decrypt from pw:\n");
uint32_t sz1 = tox_pass_decrypt(data2, size, pw, pwlen, out1);
uint32_t sz2 = tox_pass_key_decrypt(data2, size, key, out2);
ck_assert_msg(sz1 == sz2, "differing output sizes");
ck_assert_msg(memcmp(out1, out2, sz1) == 0, "differing output data");
ret = tox_pass_decrypt(encdata2, size2, pw, pwlen, out1, &err3);
ck_assert_msg(ret, "failed to pw decrypt %u", err3);
ret = tox_pass_key_decrypt(encdata2, size2, &key, out2, &err3);
ck_assert_msg(ret, "failed to key decrypt %u", err3);
ck_assert_msg(memcmp(out1, out2, size) == 0, "differing output data");
// and now with the code in use (I only bothered with manually to debug this, and it seems a waste
// to remove the manual check now that it's there)
Tox *tox5 = tox_new(0);
test = tox_encrypted_load(tox5, data2, size, pw, pwlen);
ck_assert_msg(test == 0, "failed to encrypted load the third");
uint8_t address5[TOX_CLIENT_ID_SIZE];
test = tox_get_client_id(tox4, 0, address5);
ck_assert_msg(test == 0, "no friends! the third");
ck_assert_msg(memcmp(address, address2, TOX_CLIENT_ID_SIZE) == 0, "addresses don't match! the third");
Tox *tox4 = tox_new(0, out1, size, &err2);
ck_assert_msg(err2 == TOX_ERR_NEW_OK, "failed to new the third");
uint8_t address5[TOX_PUBLIC_KEY_SIZE];
ret = tox_friend_get_public_key(tox4, 0, address5, 0);
ck_assert_msg(ret, "no friends! the third");
ck_assert_msg(memcmp(address, address2, TOX_PUBLIC_KEY_SIZE) == 0, "addresses don't match! the third");
tox_kill(tox1);
tox_kill(tox2);
tox_kill(tox3);
tox_kill(tox4);
tox_kill(tox5);
}
END_TEST
START_TEST(test_keys)
{
uint8_t key[tox_pass_key_length()];
tox_derive_key_from_pass("123qweasdzxc", 12, key);
TOX_ERR_ENCRYPTION encerr;
TOX_ERR_DECRYPTION decerr;
TOX_ERR_KEY_DERIVATION keyerr;
TOX_PASS_KEY key;
bool ret = tox_derive_key_from_pass("123qweasdzxc", 12, &key, &keyerr);
ck_assert_msg(ret, "generic failure 1: %u", keyerr);
uint8_t *string = "No Patrick, mayonnaise is not an instrument."; // 44
uint8_t encrypted[44 + tox_pass_encryption_extra_length()];
int sz = tox_pass_key_encrypt(string, 44, key, encrypted);
uint8_t encrypted[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
ret = tox_pass_key_encrypt(string, 44, &key, encrypted, &encerr);
ck_assert_msg(ret, "generic failure 2: %u", encerr);
uint8_t encrypted2[44 + tox_pass_encryption_extra_length()];
int sz2 = tox_pass_encrypt(string, 44, "123qweasdzxc", 12, encrypted2);
uint8_t encrypted2[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
ret = tox_pass_encrypt(string, 44, "123qweasdzxc", 12, encrypted2, &encerr);
ck_assert_msg(ret, "generic failure 3: %u", encerr);
ck_assert_msg(sz == sz2, "an encryption failed");
uint8_t out1[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
uint8_t out2[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
uint8_t out1[44 + tox_pass_encryption_extra_length()];
uint8_t out2[44 + tox_pass_encryption_extra_length()];
sz = tox_pass_key_decrypt(encrypted, 44 + tox_pass_encryption_extra_length(), key, out1);
ck_assert_msg(sz == 44, "sz isn't right");
ret = tox_pass_key_decrypt(encrypted, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, &key, out1, &decerr);
ck_assert_msg(ret, "generic failure 4: %u", decerr);
ck_assert_msg(memcmp(out1, string, 44) == 0, "decryption 1 failed");
sz2 = tox_pass_decrypt(encrypted2, 44 + tox_pass_encryption_extra_length(), "123qweasdzxc", 12, out2);
ck_assert_msg(sz2 == 44, "sz2 isn't right");
ret = tox_pass_decrypt(encrypted2, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, "123qweasdzxc", 12, out2, &decerr);
ck_assert_msg(ret, "generic failure 5: %u", decerr);
ck_assert_msg(memcmp(out2, string, 44) == 0, "decryption 2 failed");
// test that pass_decrypt can decrypt things from pass_key_encrypt
sz = tox_pass_decrypt(encrypted, 44 + tox_pass_encryption_extra_length(), "123qweasdzxc", 12, out1);
ck_assert_msg(sz == 44, "sz isn't right");
ret = tox_pass_decrypt(encrypted, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, "123qweasdzxc", 12, out1, &decerr);
ck_assert_msg(ret, "generic failure 6: %u", decerr);
ck_assert_msg(memcmp(out1, string, 44) == 0, "decryption 3 failed");
uint8_t salt[tox_pass_salt_length()];
ck_assert_msg(0 == tox_get_salt(encrypted, salt), "couldn't get salt");
uint8_t key2[tox_pass_key_length()];
tox_derive_key_with_salt("123qweasdzxc", 12, salt, key2);
ck_assert_msg(0 == memcmp(key, key2, tox_pass_key_length()), "salt comparison failed");
uint8_t salt[TOX_PASS_SALT_LENGTH];
ck_assert_msg(tox_get_salt(encrypted, salt), "couldn't get salt");
TOX_PASS_KEY key2;
ret = tox_derive_key_with_salt("123qweasdzxc", 12, salt, &key2, &keyerr);
ck_assert_msg(ret, "generic failure 7: %u", keyerr);
ck_assert_msg(0 == memcmp(&key, &key2, sizeof(TOX_PASS_KEY)), "salt comparison failed");
}
END_TEST
@ -165,7 +172,7 @@ Suite *encryptsave_suite(void)
{
Suite *s = suite_create("encryptsave");
DEFTESTCASE_SLOW(known_kdf, 60); /* is 5-10 seconds on my computer, but is directly dependent on CPU */
DEFTESTCASE_SLOW(known_kdf, 60);
DEFTESTCASE_SLOW(save_friend, 20);
DEFTESTCASE_SLOW(keys, 30);

View File

@ -50,10 +50,10 @@ START_TEST(test_m_sendmesage)
int bad_len = MAX_CRYPTO_PACKET_SIZE;
ck_assert(m_sendmessage(m, -1, (uint8_t *)message, good_len) == 0);
ck_assert(m_sendmessage(m, REALLY_BIG_NUMBER, (uint8_t *)message, good_len) == 0);
ck_assert(m_sendmessage(m, 17, (uint8_t *)message, good_len) == 0);
ck_assert(m_sendmessage(m, friend_id_num, (uint8_t *)message, bad_len) == 0);
ck_assert(m_send_message_generic(m, -1, MESSAGE_NORMAL, (uint8_t *)message, good_len, 0) == -1);
ck_assert(m_send_message_generic(m, REALLY_BIG_NUMBER, MESSAGE_NORMAL, (uint8_t *)message, good_len, 0) == -1);
ck_assert(m_send_message_generic(m, 17, MESSAGE_NORMAL, (uint8_t *)message, good_len, 0) == -1);
ck_assert(m_send_message_generic(m, friend_id_num, MESSAGE_NORMAL, (uint8_t *)message, bad_len, 0) == -2);
}
END_TEST
@ -68,10 +68,10 @@ START_TEST(test_m_get_userstatus_size)
rc = m_get_statusmessage_size(m, friend_id_num);
/* this WILL error if the original m_addfriend_norequest() failed */
ck_assert_msg((rc > 0 && rc <= MAX_STATUSMESSAGE_LENGTH),
"m_get_statusmessage_size is returning out of range values!\n"
ck_assert_msg((rc >= 0 && rc <= MAX_STATUSMESSAGE_LENGTH),
"m_get_statusmessage_size is returning out of range values! (%i)\n"
"(this can be caused by the error of m_addfriend_norequest"
" in the beginning of the suite)\n");
" in the beginning of the suite)\n", rc);
}
END_TEST
@ -337,7 +337,7 @@ int main(int argc, char *argv[])
/* IPv6 status from global define */
Messenger_Options options = {0};
options.ipv6enabled = TOX_ENABLE_IPV6_DEFAULT;
m = new_messenger(&options);
m = new_messenger(&options, 0);
/* setup a default friend and friendnum */
if (m_addfriend_norequest(m, (uint8_t *)friend_id) < 0)

View File

@ -275,7 +275,8 @@ Onions *new_onions(uint16_t port)
DHT *dht = new_DHT(new_networking(ip, port));
on->onion = new_onion(dht);
on->onion_a = new_onion_announce(dht);
on->onion_c = new_onion_client(new_net_crypto(dht, 0));
TCP_Proxy_Info inf = {0};
on->onion_c = new_onion_client(new_net_crypto(dht, &inf));
if (on->onion && on->onion_a && on->onion_c)
return on;
@ -294,15 +295,70 @@ void kill_onions(Onions *on)
{
Networking_Core *net = on->onion->dht->net;
DHT *dht = on->onion->dht;
Net_Crypto *c = on->onion_c->c;
kill_onion_client(on->onion_c);
kill_onion_announce(on->onion_a);
kill_onion(on->onion);
kill_net_crypto(c);
kill_DHT(dht);
kill_networking(net);
free(on);
}
#define NUM_ONIONS 50
#define NUM_FIRST 7
#define NUM_LAST 37
_Bool first_ip, last_ip;
void dht_ip_callback(void *object, int32_t number, IP_Port ip_port)
{
if (NUM_FIRST == number) {
first_ip = 1;
return;
}
if (NUM_LAST == number) {
last_ip = 1;
return;
}
ck_abort_msg("Error.");
}
_Bool first, last;
uint8_t first_dht_pk[crypto_box_PUBLICKEYBYTES];
uint8_t last_dht_pk[crypto_box_PUBLICKEYBYTES];
static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_public_key)
{
Onions *on = object;
uint16_t count = 0;
int ret = DHT_addfriend(on->onion->dht, dht_public_key, &dht_ip_callback, object, number, &count);
ck_assert_msg(count == 1, "Count not 1");
ck_assert_msg(ret == 0, "DHT_addfriend() did not return 0");
if (NUM_FIRST == number && !first) {
first = 1;
if (memcmp(dht_public_key, last_dht_pk, crypto_box_PUBLICKEYBYTES) != 0) {
ck_abort_msg("Error wrong dht key.");
}
return;
}
if (NUM_LAST == number && !last) {
last = 1;
if (memcmp(dht_public_key, first_dht_pk, crypto_box_PUBLICKEYBYTES) != 0) {
ck_abort_msg("Error wrong dht key.");
}
return;
}
ck_abort_msg("Error.");
}
START_TEST(test_announce)
{
@ -340,6 +396,8 @@ START_TEST(test_announce)
c_sleep(50);
}
printf("connected\n");
for (i = 0; i < 25 * 2; ++i) {
for (j = 0; j < NUM_ONIONS; ++j) {
do_onions(onions[j]);
@ -348,37 +406,40 @@ START_TEST(test_announce)
c_sleep(50);
}
onion_addfriend(onions[7]->onion_c, onions[37]->onion_c->c->self_public_key);
int frnum = onion_addfriend(onions[37]->onion_c, onions[7]->onion_c->c->self_public_key);
memcpy(first_dht_pk, onions[NUM_FIRST]->onion->dht->self_public_key, crypto_box_PUBLICKEYBYTES);
memcpy(last_dht_pk, onions[NUM_LAST]->onion->dht->self_public_key, crypto_box_PUBLICKEYBYTES);
printf("adding friend\n");
int frnum_f = onion_addfriend(onions[NUM_FIRST]->onion_c, onions[NUM_LAST]->onion_c->c->self_public_key);
int frnum = onion_addfriend(onions[NUM_LAST]->onion_c, onions[NUM_FIRST]->onion_c->c->self_public_key);
onion_dht_pk_callback(onions[NUM_FIRST]->onion_c, frnum_f, &dht_pk_callback, onions[NUM_FIRST], NUM_FIRST);
onion_dht_pk_callback(onions[NUM_LAST]->onion_c, frnum, &dht_pk_callback, onions[NUM_LAST], NUM_LAST);
int ok = -1;
IP_Port ip_port;
while (ok == -1) {
for (i = 0; i < NUM_ONIONS; ++i) {
networking_poll(onions[i]->onion->net);
do_onion_client(onions[i]->onion_c);
}
ok = onion_getfriendip(onions[37]->onion_c, frnum, &ip_port);
c_sleep(50);
}
printf("id discovered\n");
while (ok != 1) {
while (!first || !last) {
for (i = 0; i < NUM_ONIONS; ++i) {
do_onions(onions[i]);
}
ok = onion_getfriendip(onions[37]->onion_c, frnum, &ip_port);
c_sleep(50);
}
printf("Waiting for ips\n");
while (!first_ip || !last_ip) {
for (i = 0; i < NUM_ONIONS; ++i) {
do_onions(onions[i]);
}
c_sleep(50);
}
ck_assert_msg(ip_port.port == onions[7]->onion->net->port, "Port in returned ip not correct.");
onion_getfriendip(onions[NUM_LAST]->onion_c, frnum, &ip_port);
ck_assert_msg(ip_port.port == onions[NUM_FIRST]->onion->net->port, "Port in returned ip not correct.");
for (i = 0; i < NUM_ONIONS; ++i) {
kill_onions(onions[i]);
@ -391,7 +452,7 @@ Suite *onion_suite(void)
Suite *s = suite_create("Onion");
DEFTESTCASE_SLOW(basic, 5);
//DEFTESTCASE_SLOW(announce, 50); //TODO: fix test.
DEFTESTCASE_SLOW(announce, 70);
return s;
}

View File

@ -21,22 +21,28 @@
#define c_sleep(x) usleep(1000*x)
#endif
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
if (*((uint32_t *)userdata) != 974536)
return;
if (length == 7 && memcmp("Gentoo", data, 7) == 0) {
tox_add_friend_norequest(m, public_key);
tox_friend_add_norequest(m, public_key, 0);
}
}
uint32_t messages_received;
void print_message(Tox *m, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
void print_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length,
void *userdata)
{
if (*((uint32_t *)userdata) != 974536)
return;
if (type != TOX_MESSAGE_TYPE_NORMAL) {
ck_abort_msg("Bad type");
}
uint8_t cmp_msg[TOX_MAX_MESSAGE_LENGTH];
memset(cmp_msg, 'G', sizeof(cmp_msg));
@ -46,7 +52,7 @@ void print_message(Tox *m, int friendnumber, const uint8_t *string, uint16_t len
uint32_t name_changes;
void print_nickchange(Tox *m, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
void print_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
{
if (*((uint32_t *)userdata) != 974536)
return;
@ -55,9 +61,19 @@ void print_nickchange(Tox *m, int friendnumber, const uint8_t *string, uint16_t
++name_changes;
}
uint32_t status_m_changes;
void print_status_m_change(Tox *tox, uint32_t friend_number, const uint8_t *message, size_t length, void *user_data)
{
if (*((uint32_t *)user_data) != 974536)
return;
if (length == sizeof("Installing Gentoo") && memcmp(message, "Installing Gentoo", sizeof("Installing Gentoo")) == 0)
++status_m_changes;
}
uint32_t typing_changes;
void print_typingchange(Tox *m, int friendnumber, uint8_t typing, void *userdata)
void print_typingchange(Tox *m, uint32_t friendnumber, bool typing, void *userdata)
{
if (*((uint32_t *)userdata) != 974536)
return;
@ -70,12 +86,12 @@ void print_typingchange(Tox *m, int friendnumber, uint8_t typing, void *userdata
uint32_t custom_packet;
int handle_custom_packet(Tox *m, int32_t friend_num, const uint8_t *data, uint32_t len, void *object)
void handle_custom_packet(Tox *m, uint32_t friend_num, const uint8_t *data, size_t len, void *object)
{
uint8_t number = *((uint32_t *)object);
if (len != TOX_MAX_CUSTOM_PACKET_SIZE)
return -1;
return;
uint8_t f_data[len];
memset(f_data, number, len);
@ -83,54 +99,168 @@ int handle_custom_packet(Tox *m, int32_t friend_num, const uint8_t *data, uint32
if (memcmp(f_data, data, len) == 0) {
++custom_packet;
} else {
printf("Custom packet fail. %u\n", number );
ck_abort_msg("Custom packet fail. %u", number);
}
return 0;
}
uint8_t filenum;
uint32_t file_accepted;
uint64_t file_size;
void file_request_accept(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, const uint8_t *filename,
uint16_t filename_length, void *userdata)
{
if (*((uint32_t *)userdata) != 974536)
return;
if (filename_length == sizeof("Gentoo.exe") && memcmp(filename, "Gentoo.exe", sizeof("Gentoo.exe")) == 0)
++file_accepted;
file_size = filesize;
tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_ACCEPT, NULL, 0);
}
uint32_t file_sent;
uint32_t sendf_ok;
void file_print_control(Tox *m, int friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type,
const uint8_t *data, uint16_t length, void *userdata)
{
if (*((uint32_t *)userdata) != 974536)
return;
if (receive_send == 0 && control_type == TOX_FILECONTROL_FINISHED)
tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_FINISHED, NULL, 0);
if (receive_send == 1 && control_type == TOX_FILECONTROL_FINISHED)
file_sent = 1;
if (receive_send == 1 && control_type == TOX_FILECONTROL_ACCEPT)
sendf_ok = 1;
return;
}
uint64_t size_recv;
uint8_t num;
void write_file(Tox *m, int friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata)
uint64_t sending_pos;
uint8_t file_cmp_id[TOX_FILE_ID_LENGTH];
uint8_t filenum;
uint32_t file_accepted;
uint64_t file_size;
void tox_file_receive(Tox *tox, uint32_t friend_number, uint32_t file_number, uint32_t kind, uint64_t filesize,
const uint8_t *filename, size_t filename_length, void *userdata)
{
if (*((uint32_t *)userdata) != 974536)
return;
if (kind != TOX_FILE_KIND_DATA) {
ck_abort_msg("Bad kind");
return;
}
if (!(filename_length == sizeof("Gentoo.exe") && memcmp(filename, "Gentoo.exe", sizeof("Gentoo.exe")) == 0)) {
ck_abort_msg("Bad filename");
return;
}
uint8_t file_id[TOX_FILE_ID_LENGTH];
if (!tox_file_get_file_id(tox, friend_number, file_number, file_id, 0)) {
ck_abort_msg("tox_file_get_file_id error");
}
if (memcmp(file_id, file_cmp_id, TOX_FILE_ID_LENGTH) != 0) {
ck_abort_msg("bad file_id");
}
uint8_t empty[TOX_FILE_ID_LENGTH] = {0};
if (memcmp(empty, file_cmp_id, TOX_FILE_ID_LENGTH) == 0) {
ck_abort_msg("empty file_id");
}
file_size = filesize;
if (filesize) {
sending_pos = size_recv = 1337;
TOX_ERR_FILE_SEEK err_s;
if (!tox_file_seek(tox, friend_number, file_number, 1337, &err_s)) {
ck_abort_msg("tox_file_seek error");
}
ck_assert_msg(err_s == TOX_ERR_FILE_SEEK_OK, "tox_file_seek wrong error");
} else {
sending_pos = size_recv = 0;
}
TOX_ERR_FILE_CONTROL error;
if (tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME, &error)) {
++file_accepted;
} else {
ck_abort_msg("tox_file_control failed. %i", error);
}
TOX_ERR_FILE_SEEK err_s;
if (tox_file_seek(tox, friend_number, file_number, 1234, &err_s)) {
ck_abort_msg("tox_file_seek no error");
}
ck_assert_msg(err_s == TOX_ERR_FILE_SEEK_DENIED, "tox_file_seek wrong error");
}
uint32_t sendf_ok;
void file_print_control(Tox *tox, uint32_t friend_number, uint32_t file_number, TOX_FILE_CONTROL control,
void *userdata)
{
if (*((uint32_t *)userdata) != 974536)
return;
/* First send file num is 0.*/
if (file_number == 0 && control == TOX_FILE_CONTROL_RESUME)
sendf_ok = 1;
}
uint64_t max_sending;
_Bool m_send_reached;
uint8_t sending_num;
_Bool file_sending_done;
void tox_file_chunk_request(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position, size_t length,
void *user_data)
{
if (*((uint32_t *)user_data) != 974536)
return;
if (!sendf_ok) {
ck_abort_msg("Didn't get resume control");
}
if (sending_pos != position) {
ck_abort_msg("Bad position %llu", position);
return;
}
if (length == 0) {
if (file_sending_done) {
ck_abort_msg("File sending already done.");
}
file_sending_done = 1;
return;
}
if (position + length > max_sending) {
if (m_send_reached) {
ck_abort_msg("Requested done file tranfer.");
}
length = max_sending - position;
m_send_reached = 1;
}
TOX_ERR_FILE_SEND_CHUNK error;
uint8_t f_data[length];
memset(f_data, sending_num, length);
if (tox_file_send_chunk(tox, friend_number, file_number, position, f_data, length, &error)) {
++sending_num;
sending_pos += length;
} else {
ck_abort_msg("Could not send chunk %i", error);
}
if (error != TOX_ERR_FILE_SEND_CHUNK_OK) {
ck_abort_msg("Wrong error code");
}
}
uint8_t num;
_Bool file_recv;
void write_file(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
size_t length, void *user_data)
{
if (*((uint32_t *)user_data) != 974536)
return;
if (size_recv != position) {
ck_abort_msg("Bad position");
return;
}
if (length == 0) {
file_recv = 1;
return;
}
uint8_t f_data[length];
memset(f_data, num, length);
++num;
@ -138,33 +268,59 @@ void write_file(Tox *m, int friendnumber, uint8_t filenumber, const uint8_t *dat
if (memcmp(f_data, data, length) == 0) {
size_recv += length;
} else {
printf("FILE_CORRUPTED\n");
ck_abort_msg("FILE_CORRUPTED");
}
}
unsigned int connected_t1;
void tox_connection_status(Tox *tox, TOX_CONNECTION connection_status, void *user_data)
{
if (*((uint32_t *)user_data) != 974536)
return;
if (connected_t1 && !connection_status)
ck_abort_msg("Tox went offline");
connected_t1 = connection_status;
}
START_TEST(test_one)
{
Tox *tox1 = tox_new(0);
Tox *tox2 = tox_new(0);
Tox *tox1 = tox_new(0, 0, 0, 0);
Tox *tox2 = tox_new(0, 0, 0, 0);
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(tox1, address);
ck_assert_msg(tox_add_friend(tox1, address, (uint8_t *)"m", 1) == TOX_FAERR_OWNKEY, "Adding own address worked.");
{
TOX_ERR_GET_PORT error;
ck_assert_msg(tox_self_get_udp_port(tox1, &error) == 33445, "First Tox instance did not bind to udp port 33445.\n");
ck_assert_msg(error == TOX_ERR_GET_PORT_OK, "wrong error");
}
tox_get_address(tox2, address);
uint8_t message[TOX_MAX_FRIENDREQUEST_LENGTH + 1];
ck_assert_msg(tox_add_friend(tox1, address, NULL, 0) == TOX_FAERR_NOMESSAGE, "Sending request with no message worked.");
ck_assert_msg(tox_add_friend(tox1, address, message, sizeof(message)) == TOX_FAERR_TOOLONG,
"TOX_MAX_FRIENDREQUEST_LENGTH is too big.");
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(tox1, address);
TOX_ERR_FRIEND_ADD error;
uint32_t ret = tox_friend_add(tox1, address, (uint8_t *)"m", 1, &error);
ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_OWN_KEY, "Adding own address worked.");
tox_self_get_address(tox2, address);
uint8_t message[TOX_MAX_FRIEND_REQUEST_LENGTH + 1];
ret = tox_friend_add(tox1, address, NULL, 0, &error);
ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_NULL, "Sending request with no message worked.");
ret = tox_friend_add(tox1, address, message, 0, &error);
ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_NO_MESSAGE, "Sending request with no message worked.");
ret = tox_friend_add(tox1, address, message, sizeof(message), &error);
ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_TOO_LONG,
"TOX_MAX_FRIEND_REQUEST_LENGTH is too big.");
address[0]++;
ck_assert_msg(tox_add_friend(tox1, address, (uint8_t *)"m", 1) == TOX_FAERR_BADCHECKSUM,
ret = tox_friend_add(tox1, address, (uint8_t *)"m", 1, &error);
ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_BAD_CHECKSUM,
"Adding address with bad checksum worked.");
tox_get_address(tox2, address);
ck_assert_msg(tox_add_friend(tox1, address, message, TOX_MAX_FRIENDREQUEST_LENGTH) == 0, "Failed to add friend.");
ck_assert_msg(tox_add_friend(tox1, address, message, TOX_MAX_FRIENDREQUEST_LENGTH) == TOX_FAERR_ALREADYSENT,
"Adding friend twice worked.");
tox_self_get_address(tox2, address);
ret = tox_friend_add(tox1, address, message, TOX_MAX_FRIEND_REQUEST_LENGTH, &error);
ck_assert_msg(ret == 0 && error == TOX_ERR_FRIEND_ADD_OK, "Failed to add friend.");
ret = tox_friend_add(tox1, address, message, TOX_MAX_FRIEND_REQUEST_LENGTH, &error);
ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_ALREADY_SENT, "Adding friend twice worked.");
uint8_t name[TOX_MAX_NAME_LENGTH];
int i;
@ -173,22 +329,23 @@ START_TEST(test_one)
name[i] = rand();
}
tox_set_name(tox1, name, sizeof(name));
ck_assert_msg(tox_get_self_name_size(tox1) == sizeof(name), "Can't set name of TOX_MAX_NAME_LENGTH");
tox_self_set_name(tox1, name, sizeof(name), 0);
ck_assert_msg(tox_self_get_name_size(tox1) == sizeof(name), "Can't set name of TOX_MAX_NAME_LENGTH");
size_t save_size = tox_size(tox1);
size_t save_size = tox_get_savedata_size(tox1);
uint8_t data[save_size];
tox_save(tox1, data);
tox_get_savedata(tox1, data);
tox_kill(tox2);
tox2 = tox_new(0);
ck_assert_msg(tox_load(tox2, data, save_size) == 0, "Load failed");
TOX_ERR_NEW err_n;
size_t length = tox_get_self_name_size(tox2);
ck_assert_msg(tox_get_self_name_size(tox2) == sizeof name, "Wrong name size.");
tox2 = tox_new(0, data, save_size, &err_n);
ck_assert_msg(err_n == TOX_ERR_NEW_OK, "Load failed");
ck_assert_msg(tox_self_get_name_size(tox2) == sizeof name, "Wrong name size.");
uint8_t new_name[TOX_MAX_NAME_LENGTH] = { 0 };
ck_assert_msg(tox_get_self_name(tox2, new_name) == TOX_MAX_NAME_LENGTH, "Wrong name length");
tox_self_get_name(tox2, new_name);
ck_assert_msg(memcmp(name, new_name, TOX_MAX_NAME_LENGTH) == 0, "Wrong name");
tox_kill(tox1);
@ -199,51 +356,83 @@ END_TEST
START_TEST(test_few_clients)
{
long long unsigned int con_time, cur_time = time(NULL);
Tox *tox1 = tox_new(0);
Tox *tox2 = tox_new(0);
Tox *tox3 = tox_new(0);
TOX_ERR_NEW t_n_error;
Tox *tox1 = tox_new(0, 0, 0, &t_n_error);
ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "wrong error");
Tox *tox2 = tox_new(0, 0, 0, &t_n_error);
ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "wrong error");
Tox *tox3 = tox_new(0, 0, 0, &t_n_error);
ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "wrong error");
ck_assert_msg(tox1 || tox2 || tox3, "Failed to create 3 tox instances");
{
TOX_ERR_GET_PORT error;
ck_assert_msg(tox_self_get_udp_port(tox1, &error) == 33445, "First Tox instance did not bind to udp port 33445.\n");
ck_assert_msg(error == TOX_ERR_GET_PORT_OK, "wrong error");
}
{
TOX_ERR_GET_PORT error;
ck_assert_msg(tox_self_get_udp_port(tox2, &error) == 33446, "Second Tox instance did not bind to udp port 33446.\n");
ck_assert_msg(error == TOX_ERR_GET_PORT_OK, "wrong error");
}
{
TOX_ERR_GET_PORT error;
ck_assert_msg(tox_self_get_udp_port(tox3, &error) == 33447, "Third Tox instance did not bind to udp port 33447.\n");
ck_assert_msg(error == TOX_ERR_GET_PORT_OK, "wrong error");
}
uint32_t to_compare = 974536;
connected_t1 = 0;
tox_callback_self_connection_status(tox1, tox_connection_status, &to_compare);
tox_callback_friend_request(tox2, accept_friend_request, &to_compare);
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(tox2, address);
int test = tox_add_friend(tox3, address, (uint8_t *)"Gentoo", 7);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(tox2, address);
uint32_t test = tox_friend_add(tox3, address, (uint8_t *)"Gentoo", 7, 0);
ck_assert_msg(test == 0, "Failed to add friend error code: %i", test);
uint8_t off = 1;
while (1) {
tox_do(tox1);
tox_do(tox2);
tox_do(tox3);
tox_iterate(tox1);
tox_iterate(tox2);
tox_iterate(tox3);
if (tox_isconnected(tox1) && tox_isconnected(tox2) && tox_isconnected(tox3) && off) {
printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
con_time = time(NULL);
off = 0;
if (tox_self_get_connection_status(tox1) && tox_self_get_connection_status(tox2)
&& tox_self_get_connection_status(tox3)) {
if (off) {
printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
con_time = time(NULL);
off = 0;
}
if (tox_friend_get_connection_status(tox2, 0, 0) == TOX_CONNECTION_UDP
&& tox_friend_get_connection_status(tox3, 0, 0) == TOX_CONNECTION_UDP)
break;
}
if (tox_get_friend_connection_status(tox2, 0) == 1 && tox_get_friend_connection_status(tox3, 0) == 1)
break;
c_sleep(50);
}
ck_assert_msg(connected_t1, "Tox1 isn't connected. %u", connected_t1);
printf("tox clients connected took %llu seconds\n", time(NULL) - con_time);
to_compare = 974536;
tox_callback_friend_message(tox3, print_message, &to_compare);
uint8_t msgs[TOX_MAX_MESSAGE_LENGTH + 1];
memset(msgs, 'G', sizeof(msgs));
ck_assert_msg(tox_send_message(tox2, 0, msgs, TOX_MAX_MESSAGE_LENGTH + 1) == 0,
"TOX_MAX_MESSAGE_LENGTH is too small\n");
ck_assert_msg(tox_send_message(tox2, 0, msgs, TOX_MAX_MESSAGE_LENGTH) != 0, "TOX_MAX_MESSAGE_LENGTH is too big\n");
TOX_ERR_FRIEND_SEND_MESSAGE errm;
tox_friend_send_message(tox2, 0, TOX_MESSAGE_TYPE_NORMAL, msgs, TOX_MAX_MESSAGE_LENGTH + 1, &errm);
ck_assert_msg(errm == TOX_ERR_FRIEND_SEND_MESSAGE_TOO_LONG, "TOX_MAX_MESSAGE_LENGTH is too small\n");
tox_friend_send_message(tox2, 0, TOX_MESSAGE_TYPE_NORMAL, msgs, TOX_MAX_MESSAGE_LENGTH, &errm);
ck_assert_msg(errm == TOX_ERR_FRIEND_SEND_MESSAGE_OK, "TOX_MAX_MESSAGE_LENGTH is too big\n");
while (1) {
messages_received = 0;
tox_do(tox1);
tox_do(tox2);
tox_do(tox3);
tox_iterate(tox1);
tox_iterate(tox2);
tox_iterate(tox3);
if (messages_received)
break;
@ -253,14 +442,16 @@ START_TEST(test_few_clients)
printf("tox clients messaging succeeded\n");
tox_callback_name_change(tox3, print_nickchange, &to_compare);
tox_set_name(tox2, (uint8_t *)"Gentoo", sizeof("Gentoo"));
tox_callback_friend_name(tox3, print_nickchange, &to_compare);
TOX_ERR_SET_INFO err_n;
bool succ = tox_self_set_name(tox2, (uint8_t *)"Gentoo", sizeof("Gentoo"), &err_n);
ck_assert_msg(succ && err_n == TOX_ERR_SET_INFO_OK, "tox_self_set_name failed because %u\n", err_n);
while (1) {
name_changes = 0;
tox_do(tox1);
tox_do(tox2);
tox_do(tox3);
tox_iterate(tox1);
tox_iterate(tox2);
tox_iterate(tox3);
if (name_changes)
break;
@ -268,18 +459,42 @@ START_TEST(test_few_clients)
c_sleep(50);
}
ck_assert_msg(tox_friend_get_name_size(tox3, 0, 0) == sizeof("Gentoo"), "Name length not correct");
uint8_t temp_name[sizeof("Gentoo")];
tox_get_name(tox3, 0, temp_name);
tox_friend_get_name(tox3, 0, temp_name, 0);
ck_assert_msg(memcmp(temp_name, "Gentoo", sizeof("Gentoo")) == 0, "Name not correct");
tox_callback_typing_change(tox2, &print_typingchange, &to_compare);
tox_set_user_is_typing(tox3, 0, 1);
tox_callback_friend_status_message(tox3, print_status_m_change, &to_compare);
succ = tox_self_set_status_message(tox2, (uint8_t *)"Installing Gentoo", sizeof("Installing Gentoo"), &err_n);
ck_assert_msg(succ && err_n == TOX_ERR_SET_INFO_OK, "tox_self_set_status_message failed because %u\n", err_n);
while (1) {
status_m_changes = 0;
tox_iterate(tox1);
tox_iterate(tox2);
tox_iterate(tox3);
if (status_m_changes)
break;
c_sleep(50);
}
ck_assert_msg(tox_friend_get_status_message_size(tox3, 0, 0) == sizeof("Installing Gentoo"),
"status message length not correct");
uint8_t temp_status_m[sizeof("Installing Gentoo")];
tox_friend_get_status_message(tox3, 0, temp_status_m, 0);
ck_assert_msg(memcmp(temp_status_m, "Installing Gentoo", sizeof("Installing Gentoo")) == 0,
"status message not correct");
tox_callback_friend_typing(tox2, &print_typingchange, &to_compare);
tox_self_set_typing(tox3, 0, 1, 0);
while (1) {
typing_changes = 0;
tox_do(tox1);
tox_do(tox2);
tox_do(tox3);
tox_iterate(tox1);
tox_iterate(tox2);
tox_iterate(tox3);
if (typing_changes == 2)
@ -290,14 +505,14 @@ START_TEST(test_few_clients)
c_sleep(50);
}
ck_assert_msg(tox_get_is_typing(tox2, 0) == 1, "Typing fail");
tox_set_user_is_typing(tox3, 0, 0);
ck_assert_msg(tox_friend_get_typing(tox2, 0, 0) == 1, "Typing fail");
tox_self_set_typing(tox3, 0, 0, 0);
while (1) {
typing_changes = 0;
tox_do(tox1);
tox_do(tox2);
tox_do(tox3);
tox_iterate(tox1);
tox_iterate(tox2);
tox_iterate(tox3);
if (typing_changes == 1)
break;
@ -307,23 +522,24 @@ START_TEST(test_few_clients)
c_sleep(50);
}
ck_assert_msg(tox_get_is_typing(tox2, 0) == 0, "Typing fail");
TOX_ERR_FRIEND_QUERY err_t;
ck_assert_msg(tox_friend_get_typing(tox2, 0, &err_t) == 0, "Typing fail");
ck_assert_msg(err_t == TOX_ERR_FRIEND_QUERY_OK, "Typing fail");
uint32_t packet_number = 160;
int ret = tox_lossless_packet_registerhandler(tox3, 0, packet_number, &handle_custom_packet, &packet_number);
ck_assert_msg(ret == 0, "tox_lossless_packet_registerhandler fail %i", ret);
tox_callback_friend_lossless_packet(tox3, &handle_custom_packet, &packet_number);
uint8_t data_c[TOX_MAX_CUSTOM_PACKET_SIZE + 1];
memset(data_c, ((uint8_t)packet_number), sizeof(data_c));
ret = tox_send_lossless_packet(tox2, 0, data_c, sizeof(data_c));
ck_assert_msg(ret == -1, "tox_send_lossless_packet bigger fail %i", ret);
ret = tox_send_lossless_packet(tox2, 0, data_c, TOX_MAX_CUSTOM_PACKET_SIZE);
ck_assert_msg(ret == 0, "tox_send_lossless_packet fail %i", ret);
int ret = tox_friend_send_lossless_packet(tox2, 0, data_c, sizeof(data_c), 0);
ck_assert_msg(ret == 0, "tox_friend_send_lossless_packet bigger fail %i", ret);
ret = tox_friend_send_lossless_packet(tox2, 0, data_c, TOX_MAX_CUSTOM_PACKET_SIZE, 0);
ck_assert_msg(ret == 1, "tox_friend_send_lossless_packet fail %i", ret);
while (1) {
custom_packet = 0;
tox_do(tox1);
tox_do(tox2);
tox_do(tox3);
tox_iterate(tox1);
tox_iterate(tox2);
tox_iterate(tox3);
if (custom_packet == 1)
break;
@ -334,19 +550,18 @@ START_TEST(test_few_clients)
}
packet_number = 200;
ret = tox_lossy_packet_registerhandler(tox3, 0, packet_number, &handle_custom_packet, &packet_number);
ck_assert_msg(ret == 0, "tox_lossy_packet_registerhandler fail %i", ret);
tox_callback_friend_lossy_packet(tox3, &handle_custom_packet, &packet_number);
memset(data_c, ((uint8_t)packet_number), sizeof(data_c));
ret = tox_send_lossy_packet(tox2, 0, data_c, sizeof(data_c));
ck_assert_msg(ret == -1, "tox_send_lossy_packet bigger fail %i", ret);
ret = tox_send_lossy_packet(tox2, 0, data_c, TOX_MAX_CUSTOM_PACKET_SIZE);
ck_assert_msg(ret == 0, "tox_send_lossy_packet fail %i", ret);
ret = tox_friend_send_lossy_packet(tox2, 0, data_c, sizeof(data_c), 0);
ck_assert_msg(ret == 0, "tox_friend_send_lossy_packet bigger fail %i", ret);
ret = tox_friend_send_lossy_packet(tox2, 0, data_c, TOX_MAX_CUSTOM_PACKET_SIZE, 0);
ck_assert_msg(ret == 1, "tox_friend_send_lossy_packet fail %i", ret);
while (1) {
custom_packet = 0;
tox_do(tox1);
tox_do(tox2);
tox_do(tox3);
tox_iterate(tox1);
tox_iterate(tox2);
tox_iterate(tox3);
if (custom_packet == 1)
break;
@ -356,45 +571,48 @@ START_TEST(test_few_clients)
c_sleep(50);
}
filenum = file_accepted = file_size = file_sent = sendf_ok = size_recv = 0;
printf("Starting file transfer test.\n");
file_accepted = file_size = file_recv = sendf_ok = size_recv = 0;
max_sending = UINT64_MAX;
long long unsigned int f_time = time(NULL);
tox_callback_file_data(tox3, write_file, &to_compare);
tox_callback_file_control(tox2, file_print_control, &to_compare);
tox_callback_file_control(tox3, file_print_control, &to_compare);
tox_callback_file_send_request(tox3, file_request_accept, &to_compare);
tox_callback_file_recv_chunk(tox3, write_file, &to_compare);
tox_callback_file_recv_control(tox2, file_print_control, &to_compare);
tox_callback_file_chunk_request(tox2, tox_file_chunk_request, &to_compare);
tox_callback_file_recv_control(tox3, file_print_control, &to_compare);
tox_callback_file_recv(tox3, tox_file_receive, &to_compare);
uint64_t totalf_size = 100 * 1024 * 1024;
int fnum = tox_new_file_sender(tox2, 0, totalf_size, (uint8_t *)"Gentoo.exe", sizeof("Gentoo.exe"));
ck_assert_msg(fnum != -1, "tox_new_file_sender fail");
int fpiece_size = tox_file_data_size(tox2, 0);
uint8_t f_data[fpiece_size];
uint8_t num = 0;
memset(f_data, num, fpiece_size);
uint32_t fnum = tox_file_send(tox2, 0, TOX_FILE_KIND_DATA, totalf_size, 0, (uint8_t *)"Gentoo.exe",
sizeof("Gentoo.exe"), 0);
ck_assert_msg(fnum != UINT32_MAX, "tox_new_file_sender fail");
TOX_ERR_FILE_GET gfierr;
ck_assert_msg(!tox_file_get_file_id(tox2, 1, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_FRIEND_NOT_FOUND, "wrong error");
ck_assert_msg(!tox_file_get_file_id(tox2, 0, fnum + 1, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_NOT_FOUND, "wrong error");
ck_assert_msg(tox_file_get_file_id(tox2, 0, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id failed");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_OK, "wrong error");
while (1) {
file_sent = 0;
tox_do(tox1);
tox_do(tox2);
tox_do(tox3);
tox_iterate(tox1);
tox_iterate(tox2);
tox_iterate(tox3);
if (sendf_ok)
while (tox_file_send_data(tox2, 0, fnum, f_data, fpiece_size < totalf_size ? fpiece_size : totalf_size) == 0) {
if (totalf_size <= fpiece_size) {
sendf_ok = 0;
tox_file_send_control(tox2, 0, 0, fnum, TOX_FILECONTROL_FINISHED, NULL, 0);
}
++num;
memset(f_data, num, fpiece_size);
totalf_size -= fpiece_size;
if (file_sending_done) {
if (sendf_ok && file_recv && totalf_size == file_size && size_recv == file_size && sending_pos == size_recv
&& file_accepted == 1) {
break;
} else {
ck_abort_msg("Something went wrong in file transfer %u %u %u %u %u %u %llu %llu %llu", sendf_ok, file_recv,
totalf_size == file_size, size_recv == file_size, sending_pos == size_recv, file_accepted == 1, totalf_size, size_recv,
sending_pos);
}
}
if (file_sent && size_recv == file_size)
break;
uint32_t tox1_interval = tox_do_interval(tox1);
uint32_t tox2_interval = tox_do_interval(tox2);
uint32_t tox3_interval = tox_do_interval(tox3);
uint32_t tox1_interval = tox_iteration_interval(tox1);
uint32_t tox2_interval = tox_iteration_interval(tox2);
uint32_t tox3_interval = tox_iteration_interval(tox3);
if (tox2_interval > tox3_interval) {
c_sleep(tox3_interval);
@ -405,6 +623,105 @@ START_TEST(test_few_clients)
printf("100MB file sent in %llu seconds\n", time(NULL) - f_time);
printf("Starting file streaming transfer test.\n");
file_sending_done = file_accepted = file_size = file_recv = sendf_ok = size_recv = 0;
f_time = time(NULL);
tox_callback_file_recv_chunk(tox3, write_file, &to_compare);
tox_callback_file_recv_control(tox2, file_print_control, &to_compare);
tox_callback_file_chunk_request(tox2, tox_file_chunk_request, &to_compare);
tox_callback_file_recv_control(tox3, file_print_control, &to_compare);
tox_callback_file_recv(tox3, tox_file_receive, &to_compare);
totalf_size = UINT64_MAX;
fnum = tox_file_send(tox2, 0, TOX_FILE_KIND_DATA, totalf_size, 0, (uint8_t *)"Gentoo.exe", sizeof("Gentoo.exe"), 0);
ck_assert_msg(fnum != UINT32_MAX, "tox_new_file_sender fail");
ck_assert_msg(!tox_file_get_file_id(tox2, 1, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_FRIEND_NOT_FOUND, "wrong error");
ck_assert_msg(!tox_file_get_file_id(tox2, 0, fnum + 1, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_NOT_FOUND, "wrong error");
ck_assert_msg(tox_file_get_file_id(tox2, 0, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id failed");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_OK, "wrong error");
max_sending = 100 * 1024;
m_send_reached = 0;
while (1) {
tox_iterate(tox1);
tox_iterate(tox2);
tox_iterate(tox3);
if (file_sending_done) {
if (sendf_ok && file_recv && m_send_reached && totalf_size == file_size && size_recv == max_sending
&& sending_pos == size_recv && file_accepted == 1) {
break;
} else {
ck_abort_msg("Something went wrong in file transfer %u %u %u %u %u %u %u %llu %llu %llu %llu", sendf_ok, file_recv,
m_send_reached, totalf_size == file_size, size_recv == max_sending, sending_pos == size_recv, file_accepted == 1,
totalf_size, file_size,
size_recv, sending_pos);
}
}
uint32_t tox1_interval = tox_iteration_interval(tox1);
uint32_t tox2_interval = tox_iteration_interval(tox2);
uint32_t tox3_interval = tox_iteration_interval(tox3);
if (tox2_interval > tox3_interval) {
c_sleep(tox3_interval);
} else {
c_sleep(tox2_interval);
}
}
printf("Starting file 0 transfer test.\n");
file_sending_done = file_accepted = file_size = file_recv = sendf_ok = size_recv = 0;
f_time = time(NULL);
tox_callback_file_recv_chunk(tox3, write_file, &to_compare);
tox_callback_file_recv_control(tox2, file_print_control, &to_compare);
tox_callback_file_chunk_request(tox2, tox_file_chunk_request, &to_compare);
tox_callback_file_recv_control(tox3, file_print_control, &to_compare);
tox_callback_file_recv(tox3, tox_file_receive, &to_compare);
totalf_size = 0;
fnum = tox_file_send(tox2, 0, TOX_FILE_KIND_DATA, totalf_size, 0, (uint8_t *)"Gentoo.exe", sizeof("Gentoo.exe"), 0);
ck_assert_msg(fnum != UINT32_MAX, "tox_new_file_sender fail");
ck_assert_msg(!tox_file_get_file_id(tox2, 1, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_FRIEND_NOT_FOUND, "wrong error");
ck_assert_msg(!tox_file_get_file_id(tox2, 0, fnum + 1, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_NOT_FOUND, "wrong error");
ck_assert_msg(tox_file_get_file_id(tox2, 0, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id failed");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_OK, "wrong error");
while (1) {
tox_iterate(tox1);
tox_iterate(tox2);
tox_iterate(tox3);
if (file_sending_done) {
if (sendf_ok && file_recv && totalf_size == file_size && size_recv == file_size && sending_pos == size_recv
&& file_accepted == 1) {
break;
} else {
ck_abort_msg("Something went wrong in file transfer %u %u %u %u %u %u %llu %llu %llu", sendf_ok, file_recv,
totalf_size == file_size, size_recv == file_size, sending_pos == size_recv, file_accepted == 1, totalf_size, size_recv,
sending_pos);
}
}
uint32_t tox1_interval = tox_iteration_interval(tox1);
uint32_t tox2_interval = tox_iteration_interval(tox2);
uint32_t tox3_interval = tox_iteration_interval(tox3);
if (tox2_interval > tox3_interval) {
c_sleep(tox3_interval);
} else {
c_sleep(tox2_interval);
}
}
printf("test_few_clients succeeded, took %llu seconds\n", time(NULL) - cur_time);
tox_kill(tox1);
@ -424,17 +741,23 @@ START_TEST(test_many_clients)
uint32_t to_comp = 974536;
for (i = 0; i < NUM_TOXES; ++i) {
toxes[i] = tox_new(0);
toxes[i] = tox_new(0, 0, 0, 0);
ck_assert_msg(toxes[i] != 0, "Failed to create tox instances %u", i);
tox_callback_friend_request(toxes[i], accept_friend_request, &to_comp);
}
{
TOX_ERR_GET_PORT error;
ck_assert_msg(tox_self_get_udp_port(toxes[0], &error) == 33445, "First Tox instance did not bind to udp port 33445.\n");
ck_assert_msg(error == TOX_ERR_GET_PORT_OK, "wrong error");
}
struct {
uint16_t tox1;
uint16_t tox2;
} pairs[NUM_FRIENDS];
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
uint8_t address[TOX_ADDRESS_SIZE];
for (i = 0; i < NUM_FRIENDS; ++i) {
loop_top:
@ -446,22 +769,24 @@ loop_top:
goto loop_top;
}
tox_get_address(toxes[pairs[i].tox1], address);
int test = tox_add_friend(toxes[pairs[i].tox2], address, (uint8_t *)"Gentoo", 7);
tox_self_get_address(toxes[pairs[i].tox1], address);
if (test == TOX_FAERR_ALREADYSENT) {
TOX_ERR_FRIEND_ADD test;
uint32_t num = tox_friend_add(toxes[pairs[i].tox2], address, (uint8_t *)"Gentoo", 7, &test);
if (test == TOX_ERR_FRIEND_ADD_ALREADY_SENT) {
goto loop_top;
}
ck_assert_msg(test >= 0, "Failed to add friend error code: %i", test);
ck_assert_msg(num != UINT32_MAX && test == TOX_ERR_FRIEND_ADD_OK, "Failed to add friend error code: %i", test);
}
while (1) {
uint16_t counter = 0;
for (i = 0; i < NUM_TOXES; ++i) {
for (j = 0; j < tox_count_friendlist(toxes[i]); ++j)
if (tox_get_friend_connection_status(toxes[i], j) == 1)
for (j = 0; j < tox_self_get_friend_list_size(toxes[i]); ++j)
if (tox_friend_get_connection_status(toxes[i], j, 0) == TOX_CONNECTION_UDP)
++counter;
}
@ -470,7 +795,7 @@ loop_top:
}
for (i = 0; i < NUM_TOXES; ++i) {
tox_do(toxes[i]);
tox_iterate(toxes[i]);
}
c_sleep(50);
@ -486,13 +811,13 @@ END_TEST
#define NUM_GROUP_TOX 32
void g_accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
void g_accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
if (*((uint32_t *)userdata) != 234212)
return;
if (length == 7 && memcmp("Gentoo", data, 7) == 0) {
tox_add_friend_norequest(m, public_key);
tox_friend_add_norequest(m, public_key, 0);
}
}
@ -543,24 +868,30 @@ START_TEST(test_many_group)
uint32_t to_comp = 234212;
for (i = 0; i < NUM_GROUP_TOX; ++i) {
toxes[i] = tox_new(0);
toxes[i] = tox_new(0, 0, 0, 0);
ck_assert_msg(toxes[i] != 0, "Failed to create tox instances %u", i);
tox_callback_friend_request(toxes[i], &g_accept_friend_request, &to_comp);
tox_callback_group_invite(toxes[i], &print_group_invite_callback, &to_comp);
}
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(toxes[NUM_GROUP_TOX - 1], address);
{
TOX_ERR_GET_PORT error;
ck_assert_msg(tox_self_get_udp_port(toxes[0], &error) == 33445, "First Tox instance did not bind to udp port 33445.\n");
ck_assert_msg(error == TOX_ERR_GET_PORT_OK, "wrong error");
}
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(toxes[NUM_GROUP_TOX - 1], address);
for (i = 0; i < NUM_GROUP_TOX; ++i) {
ck_assert_msg(tox_add_friend(toxes[i], address, (uint8_t *)"Gentoo", 7) == 0, "Failed to add friend");
ck_assert_msg(tox_friend_add(toxes[i], address, (uint8_t *)"Gentoo", 7, 0) == 0, "Failed to add friend");
tox_get_address(toxes[i], address);
tox_self_get_address(toxes[i], address);
}
while (1) {
for (i = 0; i < NUM_GROUP_TOX; ++i) {
if (tox_get_friend_connection_status(toxes[i], 0) != 1) {
if (tox_friend_get_connection_status(toxes[i], 0, 0) != TOX_CONNECTION_UDP) {
break;
}
}
@ -569,7 +900,7 @@ START_TEST(test_many_group)
break;
for (i = 0; i < NUM_GROUP_TOX; ++i) {
tox_do(toxes[i]);
tox_iterate(toxes[i]);
}
c_sleep(50);
@ -587,7 +918,7 @@ START_TEST(test_many_group)
while (1) {
for (i = 0; i < NUM_GROUP_TOX; ++i) {
tox_do(toxes[i]);
tox_iterate(toxes[i]);
}
if (!invite_counter) {
@ -625,7 +956,7 @@ START_TEST(test_many_group)
for (j = 0; j < 20; ++j) {
for (i = 0; i < NUM_GROUP_TOX; ++i) {
tox_do(toxes[i]);
tox_iterate(toxes[i]);
}
c_sleep(50);
@ -638,7 +969,7 @@ START_TEST(test_many_group)
for (j = 0; j < 10; ++j) {
for (i = 0; i < NUM_GROUP_TOX; ++i) {
tox_do(toxes[i]);
tox_iterate(toxes[i]);
}
c_sleep(50);

View File

@ -55,10 +55,10 @@ typedef struct _Status {
/* My default settings */
static ToxAvCSettings muhcaps;
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
if (length == 7 && memcmp("gentoo", data, 7) == 0) {
tox_add_friend_norequest(m, public_key);
tox_friend_add_norequest(m, public_key, 0);
}
}
@ -232,7 +232,7 @@ void register_callbacks(ToxAv *av, void *data)
*/
#define CALL_AND_START_LOOP(AliceCallType, BobCallType) \
{ int step = 0, running = 1; while (running) {\
tox_do(bootstrap_node); tox_do(Alice); tox_do(Bob); \
tox_iterate(bootstrap_node); tox_iterate(Alice); tox_iterate(Bob); \
toxav_do(status_control.Bob.av); toxav_do(status_control.Alice.av); \
switch ( step ) {\
case 0: /* Alice */ printf("Alice is calling...\n");\
@ -250,33 +250,35 @@ if (status_control.Alice.status == Ended && status_control.Bob.status == Ended)
START_TEST(test_AV_flows)
{
long long unsigned int cur_time = time(NULL);
Tox *bootstrap_node = tox_new(0);
Tox *Alice = tox_new(0);
Tox *Bob = tox_new(0);
Tox *bootstrap_node = tox_new(0, 0, 0, 0);
Tox *Alice = tox_new(0, 0, 0, 0);
Tox *Bob = tox_new(0, 0, 0, 0);
ck_assert_msg(bootstrap_node || Alice || Bob, "Failed to create 3 tox instances");
uint32_t to_compare = 974536;
tox_callback_friend_request(Alice, accept_friend_request, &to_compare);
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(Alice, address);
int test = tox_add_friend(Bob, address, (uint8_t *)"gentoo", 7);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(Alice, address);
uint32_t test = tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, 0);
ck_assert_msg(test == 0, "Failed to add friend error code: %i", test);
uint8_t off = 1;
while (1) {
tox_do(bootstrap_node);
tox_do(Alice);
tox_do(Bob);
tox_iterate(bootstrap_node);
tox_iterate(Alice);
tox_iterate(Bob);
if (tox_isconnected(bootstrap_node) && tox_isconnected(Alice) && tox_isconnected(Bob) && off) {
if (tox_self_get_connection_status(bootstrap_node) && tox_self_get_connection_status(Alice)
&& tox_self_get_connection_status(Bob)
&& off) {
printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
off = 0;
}
if (tox_get_friend_connection_status(Alice, 0) == 1 && tox_get_friend_connection_status(Bob, 0) == 1)
if (tox_friend_get_connection_status(Alice, 0, 0) && tox_friend_get_connection_status(Bob, 0, 0))
break;
c_sleep(20);
@ -483,9 +485,9 @@ START_TEST(test_AV_flows)
int running = 1;
while (running) {
tox_do(bootstrap_node);
tox_do(Alice);
tox_do(Bob);
tox_iterate(bootstrap_node);
tox_iterate(Alice);
tox_iterate(Bob);
switch ( step ) {
case 0: /* Alice */
@ -524,9 +526,9 @@ START_TEST(test_AV_flows)
int running = 1;
while (running) {
tox_do(bootstrap_node);
tox_do(Alice);
tox_do(Bob);
tox_iterate(bootstrap_node);
tox_iterate(Alice);
tox_iterate(Bob);
toxav_do(status_control.Alice.av);
toxav_do(status_control.Bob.av);
@ -569,9 +571,9 @@ START_TEST(test_AV_flows)
int running = 1;
while (running) {
tox_do(bootstrap_node);
tox_do(Alice);
tox_do(Bob);
tox_iterate(bootstrap_node);
tox_iterate(Alice);
tox_iterate(Bob);
toxav_do(status_control.Alice.av);
toxav_do(status_control.Bob.av);

View File

@ -58,10 +58,10 @@ typedef struct _Status {
Status status_control;
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
if (length == 7 && memcmp("gentoo", data, 7) == 0) {
tox_add_friend_norequest(m, public_key);
tox_friend_add_norequest(m, public_key, 0);
}
}
@ -264,12 +264,12 @@ START_TEST(test_AV_three_calls)
// void test_AV_three_calls()
{
long long unsigned int cur_time = time(NULL);
Tox *bootstrap_node = tox_new(0);
Tox *caller = tox_new(0);
Tox *bootstrap_node = tox_new(0, 0, 0, 0);
Tox *caller = tox_new(0, 0, 0, 0);
Tox *callees[3] = {
tox_new(0),
tox_new(0),
tox_new(0),
tox_new(0, 0, 0, 0),
tox_new(0, 0, 0, 0),
tox_new(0, 0, 0, 0),
};
@ -284,37 +284,37 @@ START_TEST(test_AV_three_calls)
for ( i = 0; i < 3; i ++ ) {
uint32_t to_compare = 974536;
tox_callback_friend_request(callees[i], accept_friend_request, &to_compare);
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(callees[i], address);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(callees[i], address);
int test = tox_add_friend(caller, address, (uint8_t *)"gentoo", 7);
uint32_t test = tox_friend_add(caller, address, (uint8_t *)"gentoo", 7, 0);
ck_assert_msg( test == i, "Failed to add friend error code: %i", test);
}
uint8_t off = 1;
while (1) {
tox_do(bootstrap_node);
tox_do(caller);
tox_iterate(bootstrap_node);
tox_iterate(caller);
for (i = 0; i < 3; i ++) {
tox_do(callees[i]);
tox_iterate(callees[i]);
}
if (tox_isconnected(bootstrap_node) &&
tox_isconnected(caller) &&
tox_isconnected(callees[0]) &&
tox_isconnected(callees[1]) &&
tox_isconnected(callees[2]) && off) {
if (tox_self_get_connection_status(bootstrap_node) &&
tox_self_get_connection_status(caller) &&
tox_self_get_connection_status(callees[0]) &&
tox_self_get_connection_status(callees[1]) &&
tox_self_get_connection_status(callees[2]) && off) {
printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
off = 0;
}
if (tox_get_friend_connection_status(caller, 0) == 1 &&
tox_get_friend_connection_status(caller, 1) == 1 &&
tox_get_friend_connection_status(caller, 2) == 1 )
if (tox_friend_get_connection_status(caller, 0, 0) &&
tox_friend_get_connection_status(caller, 1, 0) &&
tox_friend_get_connection_status(caller, 2, 0) )
break;
c_sleep(20);
@ -351,11 +351,11 @@ START_TEST(test_AV_three_calls)
while (call_running[0] || call_running[1] || call_running[2]) {
pthread_mutex_lock(&muhmutex);
tox_do(bootstrap_node);
tox_do(caller);
tox_do(callees[0]);
tox_do(callees[1]);
tox_do(callees[2]);
tox_iterate(bootstrap_node);
tox_iterate(caller);
tox_iterate(callees[0]);
tox_iterate(callees[1]);
tox_iterate(callees[2]);
for ( i = 0; i < 3; i++ )
toxav_do(status_control.calls[0].Caller.av);

View File

@ -472,7 +472,7 @@ AC_C_BIGENDIAN
# Checks for library functions.
AC_FUNC_FORK
AC_CHECK_FUNCS([gettimeofday memset socket strchr malloc])
if (test "x$WIN32" != "xyes") && (test "x$MACH" != "xyes") && (test "x$DISABLE_RT" != "xyes"); then
if (test "x$WIN32" != "xyes") && (test "x$MACH" != "xyes") && (test "x${host_os#*openbsd}" == "x$host_os") && (test "x$DISABLE_RT" != "xyes"); then
AC_CHECK_LIB(rt, clock_gettime,
[
RT_LIBS="-lrt"

View File

@ -1,631 +0,0 @@
# User avatars in Tox
## Introduction and rationale
User avatars are small icons or images used to identify users in the friend
list; they exist in virtually all VoIP and IM protocols and provide an easy
way for one user to identify another in the friend list.
This document describes the implementation of avatars in the Tox protocol,
according to the following design considerations:
- Avatars are handled as private information, i.e., they are only exchanged
over Tox encrypted channels among previously authenticated friends.
- The library treats all images as blobs and does not interpret or
understand image formats. It only ensures that the avatar data sent by
a user is correctly received by the other. The client application is
responsible for validating, decoding, resizing, and presenting the
image to the user.
- There is a strict limit of 16 KiB to the avatar raw data size -- this
seems suitable for practical use as, for example, the raw data of an
uncompressed 64 x 64 pixels 24 bpp RGB bitmap is 12288 bytes long; the
data limit provides enough space for larger bitmaps if the usual
compressed formats are used.
**Notice:** As designed, this limit can be changed in the future without
breaking the protocol compatibility, but clients using the original
limit will reject larger avatars.
- The protocol MUST provide means to allow caching and avoid unnecessary
data transfers.
- Avatars are transferred between clients in a background operation.
- Avatars are served on a "best effort" basis, without breaking clients
which do not support them.
- The protocol MUST resist to malicious users.
- The protocol MUST work with both UDP and TCP networks.
The Single Tox Standard Draft v.0.1.0 recommends implementing avatars as
a purely client-side feature through a procedure that can be summarized as
sending a specially named file as a file transfer request and accepting
it silently. This procedure can be improved to provide the previously stated
design considerations, but this requires a higher integration with the core
protocol. Moving this feature to the core protocol also:
- provides a simpler and cleaner interface for client applications;
- hides protocol complexities from the client;
- avoids code duplication and ad-hoc protocols in the clients;
- avoids incompatibility between client implementations;
- allows important optimizations, such as lightweight notification of
removed and updated avatars;
- plays well with cache schemes;
- makes avatar transfer essentially a background operation.
## High level description
This is a very high level description. The usage patterns expected from
client applications are described in the section "Using Avatars in Client
Applications", and a low level protocol description is available in the
section "Internal Protocol Description").
The avatar exchange is implemented with the following new elements in the
Tox protocol:
- **Avatar Information Notifications** are events which may be sent by
a user to another anytime, but are usually sent after one of them
connects to the network, changes his avatar, or in reply to an **avatar
information request**. They are delivered by a very lightweight message
but with enough information to allow a user to validate or discard an
avatar from the local cache and to decide if it is interesting to request
the avatar data from the peer.
This event contains two data fields: (1) the image format, and (2) the
cryptographic hash of the current image data. The image format may be
NONE (for users who have no avatar or removed their avatars) or PNG. The
cryptographic hash is intended to be compared with the hash of the
currently cached avatar (if any) in order to check if it is still up to
date.
- **Avatar Information Requests** are very lightweight messages sent by a
user asking for an **avatar information notification**. They may be sent
as part of the login process or when the client thinks the currently
cached avatar is outdated. The receiver may or may not answer to this
request. This message contains no data fields.
- An **Avatar Data Request** is sent by a user asking another for his
complete avatar data. It is sent only when the requesting user decides
the avatar does not exist in the local cache or is outdated. The receiver
may or may not answer to this request. This message contains no data
fields.
- An **Avatar Data Notification** is an event signaling the client that
the complete avatar image data of another user is available. The actual
data transfer is implemented using several data and control messages,
but the details are hidden from the client applications. This event can
only arrive in reply to an **avatar data request**.
This event contains three data fields: (1) the image format, (2) the
cryptographic hash of the image data, and (3) the raw image data. If the
image format is NONE (i.e. no avatar), the hash is zeroed and the image
data is empty. The raw image data is locally validated and ensured to
match the hash (the event is **not** triggered otherwise).
## API
To implement this feature, the following public symbols were added. The
complete API documentation is available in `tox.h`.
```
#define TOX_AVATAR_MAX_DATA_LENGTH 16384
#define TOX_HASH_LENGTH 32
/* Data formats for user avatar images */
typedef enum {
TOX_AVATAR_FORMAT_NONE,
TOX_AVATAR_FORMAT_PNG
}
TOX_AVATAR_FORMAT;
/* Set the user avatar image data. */
int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t length);
/* Remove the user avatar image data. */
int tox_unset_avatar(Tox *tox);
/* Get avatar data from the current user. */
int tox_get_self_avatar(const Tox *tox, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, uint8_t *hash);
/* Generate a cryptographic hash of the given data (usually a cached avatar). */
int tox_hash(uint8_t *hash, const uint8_t *data, const uint32_t datalen);
/* Request avatar information from a friend. */
int tox_request_avatar_info(const Tox *tox, const int32_t friendnumber);
/* Send an unrequested avatar information to a friend. */
int tox_send_avatar_info(Tox *tox, const int32_t friendnumber);
/* Request the avatar data from a friend. */
int tox_request_avatar_data(const Tox *tox, const int32_t friendnumber);
/* Set the callback function for avatar data. */
void tox_callback_avatar_info(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t*, void *), void *userdata);
/* Set the callback function for avatar data. */
void tox_callback_avatar_data(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t*, uint8_t*, uint32_t, void *), void *userdata);
```
## Using Avatars in Client Applications
### General recommendations
- Clients MUST NOT imply the availability of avatars in other users.
Avatars are an optional feature and not all users and clients may
support them.
- Clients MUST NOT block waiting for avatar information and avatar data
packets.
- Clients MUST treat avatar data as insecure and potentially malicious.
For example, users may accidentally use corrupted images as avatars,
a malicious user may send a specially crafted image to exploit a known
vulnerability in an image decoding library, etc. It is recommended to
handle the avatar image data in the same way as an image downloaded
from an unknown Internet source.
- The peers MUST NOT assume any coupling between the operations of
receiving an avatar information packet, sending unrequested avatar
information packets, requesting avatar data, or receiving avatar data.
For example, the following situations are valid:
* A text-mode client may send avatars to other users, but never
request them.
* A client may not understand a particular image format and ignore
avatars using it, but request and handle other formats.
* A client on a slow mobile network may ask for avatar information to
ensure its cached avatars are still valid, but not request avatar
data. The same client may start asking for avatar data once it
connects through a fast network.
- Clients SHOULD implement a local cache of avatars and not request
avatar data from other peers unless necessary.
- When avatar information is received, the client should delete the
avatar if the new avatar format is NONE or compare the hash received
from the peer with the hash of the currently cached avatar. If they
differ, send an avatar data request.
- If the cached avatar is older than a given threshold, the client may
also send an avatar info request to that friend once he is online and
mark the avatar as updated *before* any avatar information is received
(to not spam the peer with such requests).
- When an avatar data notification is received, the client must update
the cached avatar with the new one.
- Clients should resize or crop the image such that it better adapts
to the client's user interface.
- If the user already has an avatar defined in the client configuration,
it must be set before connecting to the network to avoid spurious avatar
change notifications and unnecessary data transfers.
- If no avatar data is available for a given friend, the client should
show a placeholder image.
### Interoperability and sharing avatars among different clients
**This section is a tentative recommendation of how clients should store
avatars to ensure local interoperability, and should be revised if this
code is accepted into Tox core.**
It is desirable that the user avatar and the cached friends' avatars could be
shared among different Tox clients in the same system, in the spirit of the
proposed Single Tox Standard. This not only makes switching from one client
to another easier, but also minimizes the need of data transfers, as avatars
already downloaded by other clients can be reused.
Given the Tox data directory described in STS Draft v0.1.0:
- Avatars are stored in a directory called "avatars" and named
as "xxxxx.png", where "xxxxx" is the complete public key (but not friend
address!) encoded as an uppercase hexadecimal string and "png" is the
extension for the PNG avatar. As new image formats may be used in the
future, clients should ensure no other file "xxxxx.*" exists. No file
should be kept for a user who has no avatar.
- The client's own avatar is not special and is stored like any other. This
is partially for simplicity, and partially in anticipation of profiles.
- The avatar should be stored as it was received, before any modifications by
the client for display purposes.
- The hash, as calculated by toxcore and passed into the data callback,
should be saved in "avatars/xxxxx.hash" where "xxxxx" means the
same thing as for avatars. (The filename is longer than the file :) )
**To be discussed:** User keys are usually presented in Tox clients as
upper case strings, but lower case file names are more usual.
Example for Linux and other Unix systems, assuming a user called "gildor":
Tox data directory: /home/gildor/.config/tox/
Tox data file: /home/gildor/.config/tox/data
Avatar data dir: /home/gildor/.config/tox/avatars/
Gildor's avatar: /home/gildor/.config/tox/avatars/446F4E6F744D6564646C65496E546865416666616972734F6657697A61726473.png
Elrond's avatar: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.png
Elrond's hash: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.hash
Elladan's avatar: /home/gildor/.config/tox/avatars/49486174655768656E48756D616E735468696E6B49416D4D7942726F74686572.png
Elladan's hash: /home/gildor/.config/tox/avatars/49486174655768656E48756D616E735468696E6B49416D4D7942726F74686572.hash
Elrohir's avatar /home/gildor/.config/tox/avatars/726568746F7242794D6D41496B6E696854736E616D75486E6568576574614849.png
Elrohir's hash: /home/gildor/.config/tox/avatars/726568746F7242794D6D41496B6E696854736E616D75486E6568576574614849.hash
This recommendation is partially implemented by "testing/test_avatars.c".
### Common operations
These are minimal examples of how to perform common operations with avatar
functions. For a complete working example, see `testing/test_avatars.c`.
#### Setting an avatar for the current user
In this example, `load_data_file` is just a hypothetical function that loads
data from a file into the buffer and sets the length accordingly.
uint8_t buf[TOX_AVATAR_MAX_DATA_LENGTH];
uint32_t len;
if (load_data_file("avatar.png", buf, &len) == 0)
if (tox_set_avatar(tox, TOX_AVATAR_FORMAT_PNG, buf, len) != 0)
fprintf(stderr, "Failed to set avatar.\n");
If the user is connected, this function will also notify all connected
friends about the avatar change.
If the user already has an avatar defined in the client configuration, it
must be set before connecting to the network to avoid spurious avatar change
notifications and unnecessary data transfers.
#### Removing the avatar from the current user
To remove the current avatar, an application must call
tox_unset_avatar(tox);
the effect is the same as setting the avatar format to `TOX_AVATAR_FORMAT_NONE`
with no data:
tox_set_avatar(tox, TOX_AVATAR_FORMAT_NONE, NULL, 0);
If the user is connected, this function will also notify all connected
friends about the avatar change.
#### Receiving avatar information from friends
All avatar information is passed to a callback function with the prototype:
void function(Tox *tox, int32_t friendnumber, uint8_t format,
uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata)
As in this example:
static void avatar_info_cb(Tox *tox, int32_t friendnumber, uint8_t format,
uint8_t *hash, void *userdata)
{
printf("Receiving avatar information from friend %d. Format = %d\n",
friendnumber, format);
printf("Data hash: ");
hex_printf(hash, TOX_HASH_LENGTH); /* Hypothetical function */
printf("\n");
}
And, somewhere in the Tox initialization calls, set it as the callback to be
triggered when an avatar information event arrives:
tox_callback_avatar_info(tox, avatar_info_cb, NULL);
A typical client will test the currently cached avatar against the hash given
in the avatar information event and, if needed, request the avatar data.
#### Receiving avatar data from friends
Avatar data events are only delivered in reply to avatar data requests, which
**should** only be sent after getting the user avatar information (format
and hash) from an avatar information event and checking it against a local
cache.
For this, an application must define an avatar information callback which
checks the local avatar cache and emits an avatar data request if necessary:
static void avatar_info_cb(Tox *tox, int32_t friendnumber, uint8_t format,
uint8_t *hash, void *userdata)
{
printf("Receiving avatar information from friend %d. Format = %d\n",
friendnumber, format);
if (format = TOX_AVATAR_FORMAT_NONE) {
/* User has no avatar or removed the avatar */
delete_avatar_from_cache(tox, friendnumber);
} else {
/* Use the received hash to check if the cached avatar is
still up to date. */
if (!is_user_cached_avatar_updated(tox, friendnumber, hash)) {
/* User avatar is outdated, send data request */
tox_request_avatar_data(tox, friendnumber);
}
}
}
Then define an avatar data callback to store the received data in the local
cache:
static void avatar_data_cb(Tox *tox, int32_t friendnumber, uint8_t format,
uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata)
{
if (format = TOX_AVATAR_FORMAT_NONE) {
/* User has no avatar or removed the avatar */
delete_avatar_from_cache(tox, friendnumber);
} else {
save_avatar_data_to_cache(tox, friendnumber, format, hash,
data, datalen);
}
}
And, finally, register both callbacks somewhere in the Tox initialization
calls:
tox_callback_avatar_info(tox, avatar_info_cb, NULL);
tox_callback_avatar_data(tox, avatar_data_cb, NULL);
In the previous examples, implementation of the functions to check, store,
and retrieve data from the cache were omitted for brevity. These functions
will also need to get the friend public key (client id) from the friend
number and, usually, convert it from a byte string to a hexadecimal
string. A complete, yet more complex, example is available in the file
`testing/test_avatars.c`.
## Internal Protocol Description
### New packet types
The avatar transfer protocol adds the following new packet types and ids:
PACKET_ID_AVATAR_INFO_REQ = 52
PACKET_ID_AVATAR_INFO = 53
PACKET_ID_AVATAR_DATA_CONTROL = 54
PACKET_ID_AVATAR_DATA_START = 55
PACKET_ID_AVATAR_DATA_PUSH = 56
### Requesting avatar information
To request avatar information, a user must send a packet of type
`PACKET_ID_AVATAR_INFO_REQ`. This packet has no data fields. Upon
receiving this packet, a client which supports avatars should answer with
a `PACKET_ID_AVATAR_INFO`. The sender must accept that the friend may
not answer at all.
### Receiving avatar information
Avatar information arrives in a packet of type `PACKET_ID_AVATAR_INFO` with
the following structure:
PACKET_ID_AVATAR_INFO (53)
Packet data size: 33 bytes
[1: uint8_t format][32: uint8_t hash]
where 'format' is the image data format, one of the following:
0 = AVATAR_FORMAT_NONE (no avatar set)
1 = AVATAR_FORMAT_PNG
and 'hash' is the SHA-256 message digest of the avatar data.
This packet may be sent at any time and no previous request is required.
Clients should send this packet upon connection or when a friend
connects, in the same way Tox sends name, status and action information.
### Requesting avatar data
Transmission of avatar data is a multi-step procedure using three new packet
types.
- Packet `PACKET_ID_AVATAR_DATA_CONTROL` has the format:
PACKET_ID_AVATAR_DATA_CONTROL (54)
Packet data size: 1 byte
[1: uint8_t op]
where 'op' is a code signaling either an operation request or a status
return, the semantics of which are explained below. The following values are
defined:
0 = AVATAR_DATACONTROL_REQ
1 = AVATAR_DATACONTROL_ERROR
- Packet `PACKET_ID_AVATAR_DATA_START` has the following format:
PACKET_ID_AVATAR_DATA_START (55)
Packet data size: 37 bytes
[1: uint8_t format][32: uint8_t hash][1: uint32_t data_length]
where 'format' is the image format, with the same values accepted for
the field 'format' in packet type `PACKET_ID_AVATAR_INFO`, 'hash' is
the SHA-256 cryptographic hash of the avatar raw data, and 'data_length'
is the total number of bytes the raw avatar data.
- Packet `PACKET_ID_AVATAR_DATA_PUSH` has no format structure, just up
to `AVATAR_DATA_MAX_CHUNK_SIZE` bytes of raw avatar image data; this
value is defined according to the maximum amount of data a Tox encrypted
packet can hold.
The following procedure assumes that a client "A" is requesting avatar data
from a client "B":
- "A" must initialize its control structures and mark its data transfer
as not yet started. Then it requests avatar data from "B" by sending a
packet `PACKET_ID_AVATAR_DATA_CONTROL` with 'op' set to
`AVATAR_DATACONTROL_REQ`.
- If "B" accepts this transfer, it answers by sending a
`PACKET_ID_AVATAR_DATA_START` with the fields 'format', 'hash', and
'data_length' set to the respective values of the current avatar.
If "B" has no avatar set, 'format' must be `AVATAR_FORMAT_NONE`, 'hash'
must be zeroed and 'data_length' must be zero.
If "B" does not accept sending the avatar, it may send a packet
`PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to
`AVATAR_DATACONTROL_ERROR` or simply ignore this request. "A" must cope
with this.
If "B" has an avatar, it sends a variable number of
`PACKET_ID_AVATAR_DATA_PUSH` packets with the avatar data in a single
shot.
- Upon receiving a `PACKET_ID_AVATAR_DATA_START`, "A" checks if it
has sent a data request to "B". If not, it simply ignores the packet.
If "A" really requested avatar data and the format is `AVATAR_FORMAT_NONE`,
it triggers the avatar data callback, and clears all the temporary data,
finishing the process. For other formats, "A" just waits for packets
of type `PACKET_ID_AVATAR_DATA_PUSH`.
- Upon receiving a `PACKET_ID_AVATAR_DATA_PUSH`, "A" checks if it really
sent an avatar data request and if the `PACKET_ID_AVATAR_DATA_START` was
already received. If these conditions were met, it checks if the total
length of the data already stored in the receiving buffer plus the data
present in the push packet is still less or equal than
`TOX_AVATAR_MAX_DATA_LENGTH`. If that is not the case, it replies with a
`PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to
`AVATAR_DATACONTROL_ERROR`.
If valid, "A" updates the 'bytes_received' counter and concatenates the
newly arrived data to the buffer.
Then "A" checks if all the data has already been received, by comparing the
counter 'bytes_received' with the field 'total_length'. If they are
equal, "A" takes a SHA-256 hash of the data and compares it with the
hash stored in the field 'hash' received with the first
`PACKET_ID_AVATAR_DATA_START`.
If the hashes match, the avatar data was correctly received, and "A"
triggers the avatar data callback and clears all the temporary data,
finishing the process.
If not all data was received, "A" simply waits for more data.
Client "A" is always responsible for controlling the transfer and
validating the data received. "B" doesn't need to keep any state for the
protocol, have full control over the data sent and should implement
some transfer limit for the data it sends.
- Any peer receiving a `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op'
set to `AVATAR_DATACONTROL_ERROR` clears any existing control state and
aborts sending or receiving data.
## Security considerations
The major security implication of background data transfers of large objects,
like avatars, is the possibility of exhausting the network resources of a
client. This problem is exacerbated when there is the possibility of an
amplification attack, as happens, for example, when sending a very small
avatar request message will force the user to reply with a larger avatar
data message.
The present proposal mitigates this situation by:
- only transferring data between previously authenticated friends,
- enforcing strict limits on the avatar data size,
- providing an alternate, smaller message for cooperative users to refresh
avatar information when nothing has changed (`PACKET_ID_AVATAR_INFO`),
- having a per-friend data transfer limit. As the current protocol still
allows a user to request avatar data again and again, the implementation
limits the amount of data a particular user can request for some time. The
exact values are defined in constants `AVATAR_DATA_TRANSFER_LIMIT` and
`AVATAR_DATA_TRANSFER_TIMEOUT` in file `Messenger.c`.
- making the requester responsible for storing partial data and state
information
Another problem present in avatars is the possibility of a friend sending
a maliciously crafted image intended to exploit vulnerabilities in image
decoders. Without an intermediate server to recompress, validate, and
convert the images to neutral formats, the client applications must handle
this situation by themselves using stable and secure image libraries and
imposing limits on the maximum amount of system resources the decoding
process can take. Images coming from Tox friends must be treated in the same
way as images coming from random Internet sources.

View File

@ -1,48 +0,0 @@
TODO list.
[IN PROGRESS] Add what is left to do to the TODO list.
Networking:
[NEEDS TESTING] UPnP port forwarding. (https://github.com/irungentoo/toxcore/pull/969)
[NOT STARTED] NAT-PMP port forwarding.
DHT:
[ALMOST DONE] Metadata collection prevention. (docs/Prevent_Tracking.txt)
[IN PROGRESS] Hardening against attacks.
[IN PROGRESS] Optimizing the code.
[DONE] Friend only group chats
[DONE] Networking base.
[MOSTLY DONE] Syncing chat state between clients (nicknames, list of who is in chat, etc...)
[NOT STARTED] Private messages. (and adding friends from group chats using those private messages.)
[NOT STARTED] File transfers.
[IN PROGRESS] Audio/Video
[DONE] encoding/streaming/decoding
[DONE] Call initiation
[DONE] Encryption
[IN PROGRESS] Auditing.
[NEEDS TESTING] Video packet splitting.
[IN PROGRESS] Prevent audio skew (seems to be easily solvable client side.)
[IN PROGRESS] Group chats, audio done.
Friend_requests.c:
[NOT STARTED] What happens when a friend request is received needs to be changed.
[NOT STARTED] Add multiple nospam functionality.
[DONE] File transfers
[NOT STARTED] Offline messaging
[IN PROGRESS] Friends list syncing
[DONE] IPV6 support
[IN PROGRESS] Make toxcore thread safe.
[NOT STARTED] Make the core save/datafile portable across client versions/different processor architectures.
[MOSTLY DONE] A way for people to connect to people on Tox if they are behind a bad NAT that
blocks UDP (or is just unpunchable) (docs/TCP_Network.txt) (Current way doesn't scale very well.)
[DONE] Encrypted Saves. (see: toxencryptsave)
[NOT STARTED] Security audit from professionals

46
docs/TODO.md Normal file
View File

@ -0,0 +1,46 @@
# Toxcore todo list.
Welcome to the Toxcore todo list, this is very likely out of date, but either way it's a good jumping off point if
you're looking to see where core is going, or where it could use a little love.
There are 3 sections; In Progress, TODO, and Done. These tasks are somewhat sorted by priority, but that shouldn't be
taken to mean that this is the order tasks will (or should) be completed in.
## In Progress
- [ ] [IN PROGRESS] Audio/Video
- [X] [DONE] encoding/streaming/decoding
- [X] [DONE] Call initiation
- [X] [DONE] Encryption
- [ ] [NEEDS TESTING] Video packet splitting.
- [ ] [IN PROGRESS] Auditing.
- [ ] [IN PROGRESS] Prevent audio skew (seems to be easily solvable client side.)
- [ ] [IN PROGRESS] Group chats, audio done.
- [ ] Networking:
- [ ] [NEEDS TESTING] UPnP port forwarding. ([#969](https://github.com/irungentoo/toxcore/pull/969))
- [ ] [TODO] NAT-PMP port forwarding.
- [ ] DHT:
- [ ] [ALMOST DONE] Metadata collection prevention. (docs/Prevent_Tracking.txt)
- [ ] [IN PROGRESS] Hardening against attacks.
- [ ] [IN PROGRESS] Optimizing the code.
- [ ] [DONE] Friend only group chats
- [X] [DONE] Networking base.
- [X] [MOSTLY DONE] Syncing chat state between clients (nicknames, list of who is in chat, etc...)
- [ ] [TODO] Group private messages. (and adding friends from group chats using those private messages.)
- [ ] [TODO] Group file transfers.
- [ ] [IN PROGRESS] Friends list syncing
- [ ] [IN PROGRESS] Make toxcore thread safe.
- [ ] [MOSTLY DONE] A way for people to connect to people on Tox if they are behind a bad NAT that blocks UDP (or is
just unpunchable) ([docs/TCP_Network.txt](TCP_Network.txt)) (Current way doesn't scale very well.)
## TODO
- [ ] [TODO] Make the core save/datafile portable across client versions/different processor architectures.
- [ ] [TODO] Friend_requests.c:
- [ ] [TODO] What happens when a friend request is received needs to be changed.
- [ ] [DONE?] Add multiple nospam functionality. ([#1317](https://github.com/irungentoo/toxcore/pull/1317))
- [ ] [TODO] Offline messaging
- [ ] [TODO] Security audit from professionals
## Done
- [X] [DONE] File transfers
- [X] [DONE] IPV6 support
- [X] [DONE] Encrypted Saves. (see: toxencryptsave)

View File

@ -106,21 +106,6 @@ tox_shell_LDADD = $(LIBSODIUM_LDFLAGS) \
-lutil
noinst_PROGRAMS += test_avatars
test_avatars_SOURCES = ../testing/test_avatars.c
test_avatars_CFLAGS = $(LIBSODIUM_CFLAGS) \
$(NACL_CFLAGS)
test_avatars_LDADD = $(LIBSODIUM_LDFLAGS) \
$(NACL_LDFLAGS) \
libtoxcore.la \
$(LIBSODIUM_LIBS) \
$(NACL_OBJECTS) \
$(NACL_LIBS)
noinst_PROGRAMS += irc_syncbot
irc_syncbot_SOURCES = ../testing/irc_syncbot.c

View File

@ -56,17 +56,18 @@
#endif
void print_message(Messenger *m, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
void print_message(Messenger *m, uint32_t friendnumber, unsigned int type, const uint8_t *string, size_t length,
void *userdata)
{
printf("Message with length %u received from %u: %s \n", length, friendnumber, string);
m_sendmessage(m, friendnumber, (uint8_t *)"Test1", 6);
printf("Message with length %lu received from %u: %s \n", length, friendnumber, string);
m_send_message_generic(m, friendnumber, type, (uint8_t *)"Test1", 6, 0);
}
/* FIXME needed as print_request has to match the interface expected by
* networking_requesthandler and so cannot take a Messenger * */
static Messenger *m;
void print_request(Messenger *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
void print_request(Messenger *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
printf("Friend request received from: \n");
printf("ClientID: ");
@ -79,7 +80,7 @@ void print_request(Messenger *m, const uint8_t *public_key, const uint8_t *data,
printf("%hhX", public_key[j]);
}
printf("\nOf length: %u with data: %s \n", length, data);
printf("\nOf length: %lu with data: %s \n", length, data);
if (length != sizeof("Install Gentoo")) {
return;
@ -112,7 +113,7 @@ int main(int argc, char *argv[])
Messenger_Options options = {0};
options.ipv6enabled = ipv6enabled;
m = new_messenger(&options);
m = new_messenger(&options, 0);
if ( !m ) {
fputs("Failed to allocate messenger datastructure\n", stderr);
@ -184,7 +185,7 @@ int main(int argc, char *argv[])
getname(m, num, name);
printf("%s\n", name);
m_sendmessage(m, num, (uint8_t *)"Test", 5);
m_send_message_generic(m, num, MESSAGE_NORMAL, (uint8_t *)"Test", 5, 0);
do_messenger(m);
c_sleep(30);
FILE *file = fopen("Save.bak", "wb");

View File

@ -98,7 +98,7 @@ int main(int argc, char *argv[])
for (i = r_len - 1; i != 0 && buffer[i] != '='; --i);
uint8_t tox_id[TOX_FRIEND_ADDRESS_SIZE];
uint8_t tox_id[TOX_ADDRESS_SIZE];
if (tox_decrypt_dns3_TXT(d, tox_id, buffer + i + 1, r_len - (i + 1), request_id) != 0)
return -1;
@ -106,7 +106,7 @@ int main(int argc, char *argv[])
printf("The Tox id for username %s is:\n", argv[3]);
//unsigned int i;
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
printf("%02hhX", tox_id[i]);
}

View File

@ -89,7 +89,8 @@ static void callback_group_invite(Tox *tox, int fid, uint8_t type, const uint8_t
current_group = tox_join_groupchat(tox, fid, data, length);
}
void callback_friend_message(Tox *tox, int fid, const uint8_t *message, uint16_t length, void *userdata)
void callback_friend_message(Tox *tox, uint32_t fid, TOX_MESSAGE_TYPE type, const uint8_t *message, size_t length,
void *userdata)
{
if (length == 1 && *message == 'c') {
if (tox_del_groupchat(tox, current_group) == 0)
@ -203,7 +204,7 @@ void send_irc_group(Tox *tox, uint8_t *msg, uint16_t len)
Tox *init_tox(int argc, char *argv[])
{
uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */
uint8_t ipv6enabled = 1; /* x */
int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled);
if (argvoffset < 0)
@ -215,12 +216,12 @@ Tox *init_tox(int argc, char *argv[])
exit(0);
}
Tox *tox = tox_new(0);
Tox *tox = tox_new(0, 0, 0, 0);
if (!tox)
exit(1);
tox_set_name(tox, (uint8_t *)IRC_NAME, sizeof(IRC_NAME) - 1);
tox_self_set_name(tox, (uint8_t *)IRC_NAME, sizeof(IRC_NAME) - 1, 0);
tox_callback_friend_message(tox, &callback_friend_message, 0);
tox_callback_group_invite(tox, &callback_group_invite, 0);
tox_callback_group_message(tox, &copy_groupmessage, 0);
@ -228,7 +229,7 @@ Tox *init_tox(int argc, char *argv[])
uint16_t port = atoi(argv[argvoffset + 2]);
unsigned char *binary_string = hex_string_to_bin(argv[argvoffset + 3]);
int res = tox_bootstrap_from_address(tox, argv[argvoffset + 1], port, binary_string);
tox_bootstrap(tox, argv[argvoffset + 1], port, binary_string, 0);
free(binary_string);
char temp_id[128];
@ -239,10 +240,10 @@ Tox *init_tox(int argc, char *argv[])
}
uint8_t *bin_id = hex_string_to_bin(temp_id);
int num = tox_add_friend(tox, bin_id, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo") - 1);
uint32_t num = tox_friend_add(tox, bin_id, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo") - 1, 0);
free(bin_id);
if (num < 0) {
if (num == UINT32_MAX) {
printf("\nSomething went wrong when adding friend.\n");
exit(1);
}
@ -342,7 +343,7 @@ int main(int argc, char *argv[])
}
}
tox_do(tox);
tox_iterate(tox);
usleep(1000 * 50);
}

View File

@ -105,55 +105,52 @@ int x, y;
int conversation_default = 0;
typedef struct {
uint8_t id[TOX_CLIENT_ID_SIZE];
uint8_t id[TOX_PUBLIC_KEY_SIZE];
uint8_t accepted;
} Friend_request;
Friend_request pending_requests[256];
uint8_t num_requests = 0;
#define NUM_FILE_SENDERS 256
#define NUM_FILE_SENDERS 64
typedef struct {
FILE *file;
uint16_t friendnum;
uint8_t filenumber;
uint8_t nextpiece[1024];
uint16_t piecelength;
uint32_t friendnum;
uint32_t filenumber;
} File_Sender;
File_Sender file_senders[NUM_FILE_SENDERS];
uint8_t numfilesenders;
void send_filesenders(Tox *m)
void tox_file_chunk_request(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position, size_t length,
void *user_data)
{
uint32_t i;
unsigned int i;
for (i = 0; i < NUM_FILE_SENDERS; ++i) {
if (file_senders[i].file == 0)
continue;
while (1) {
if (tox_file_send_data(m, file_senders[i].friendnum, file_senders[i].filenumber, file_senders[i].nextpiece,
file_senders[i].piecelength) == -1)
break;
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) {
/* This is slow */
if (file_senders[i].file && file_senders[i].friendnum == friend_number && file_senders[i].filenumber == file_number) {
if (length == 0) {
fclose(file_senders[i].file);
file_senders[i].file = 0;
tox_file_send_control(m, file_senders[i].friendnum, 0, file_senders[i].filenumber, 3, 0, 0);
char msg[512];
sprintf(msg, "[t] %u file transfer: %u completed", file_senders[i].friendnum, file_senders[i].filenumber);
new_lines(msg);
break;
}
fseek(file_senders[i].file, position, SEEK_SET);
uint8_t data[length];
int len = fread(data, 1, length, file_senders[i].file);
tox_file_send_chunk(tox, friend_number, file_number, position, data, len, 0);
break;
}
}
}
int add_filesender(Tox *m, uint16_t friendnum, char *filename)
uint32_t add_filesender(Tox *m, uint16_t friendnum, char *filename)
{
FILE *tempfile = fopen(filename, "r");
FILE *tempfile = fopen(filename, "rb");
if (tempfile == 0)
return -1;
@ -161,15 +158,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_file_sender(m, friendnum, filesize, (uint8_t *)filename, strlen(filename) + 1);
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_DATA, filesize, 0, (uint8_t *)filename,
strlen(filename), 0);
if (filenum == -1)
return -1;
file_senders[numfilesenders].file = tempfile;
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;
file_senders[numfilesenders].filenumber = filenum;
++numfilesenders;
@ -179,19 +174,19 @@ int add_filesender(Tox *m, uint16_t friendnum, char *filename)
#define FRADDR_TOSTR_CHUNK_LEN 8
#define FRADDR_TOSTR_BUFSIZE (TOX_FRIEND_ADDRESS_SIZE * 2 + TOX_FRIEND_ADDRESS_SIZE / FRADDR_TOSTR_CHUNK_LEN + 1)
#define FRADDR_TOSTR_BUFSIZE (TOX_ADDRESS_SIZE * 2 + TOX_ADDRESS_SIZE / FRADDR_TOSTR_CHUNK_LEN + 1)
static void fraddr_to_str(uint8_t *id_bin, char *id_str)
{
uint32_t i, delta = 0, pos_extra, sum_extra = 0;
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
for (i = 0; i < TOX_ADDRESS_SIZE; i++) {
sprintf(&id_str[2 * i + delta], "%02hhX", id_bin[i]);
if ((i + 1) == TOX_CLIENT_ID_SIZE)
if ((i + 1) == TOX_PUBLIC_KEY_SIZE)
pos_extra = 2 * (i + 1) + delta;
if (i >= TOX_CLIENT_ID_SIZE)
if (i >= TOX_PUBLIC_KEY_SIZE)
sum_extra |= id_bin[i];
if (!((i + 1) % FRADDR_TOSTR_CHUNK_LEN)) {
@ -210,14 +205,15 @@ void get_id(Tox *m, char *data)
{
sprintf(data, "[i] ID: ");
int offset = strlen(data);
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, address);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(m, address);
fraddr_to_str(address, data + offset);
}
int getfriendname_terminated(Tox *m, int friendnum, char *namebuf)
{
int res = tox_get_name(m, friendnum, (uint8_t *)namebuf);
tox_friend_get_name(m, friendnum, (uint8_t *)namebuf, NULL);
int res = tox_friend_get_name_size(m, friendnum, NULL);
if (res >= 0)
namebuf[res] = 0;
@ -249,13 +245,13 @@ void new_lines(char *line)
const char ptrn_friend[] = "[i] Friend %i: %s\n+ id: %s";
const int id_str_len = TOX_FRIEND_ADDRESS_SIZE * 2 + 3;
const int id_str_len = TOX_ADDRESS_SIZE * 2 + 3;
void print_friendlist(Tox *m)
{
new_lines("[i] Friend List:");
char name[TOX_MAX_NAME_LENGTH + 1];
uint8_t fraddr_bin[TOX_FRIEND_ADDRESS_SIZE];
uint8_t fraddr_bin[TOX_ADDRESS_SIZE];
char fraddr_str[FRADDR_TOSTR_BUFSIZE];
/* account for the longest name and the longest "base" string and number (int) and id_str */
@ -264,7 +260,7 @@ void print_friendlist(Tox *m)
uint32_t i = 0;
while (getfriendname_terminated(m, i, name) != -1) {
if (!tox_get_client_id(m, i, fraddr_bin))
if (tox_friend_get_public_key(m, i, fraddr_bin, NULL))
fraddr_to_str(fraddr_bin, fraddr_str);
else
sprintf(fraddr_str, "???");
@ -347,50 +343,59 @@ void line_eval(Tox *m, char *line)
}
unsigned char *bin_string = hex_string_to_bin(temp_id);
int num = tox_add_friend(m, bin_string, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"));
TOX_ERR_FRIEND_ADD error;
uint32_t num = tox_friend_add(m, bin_string, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"), &error);
free(bin_string);
char numstring[100];
switch (num) {
case TOX_FAERR_TOOLONG:
switch (error) {
case TOX_ERR_FRIEND_ADD_TOO_LONG:
sprintf(numstring, "[i] Message is too long.");
break;
case TOX_FAERR_NOMESSAGE:
case TOX_ERR_FRIEND_ADD_NO_MESSAGE:
sprintf(numstring, "[i] Please add a message to your request.");
break;
case TOX_FAERR_OWNKEY:
case TOX_ERR_FRIEND_ADD_OWN_KEY:
sprintf(numstring, "[i] That appears to be your own ID.");
break;
case TOX_FAERR_ALREADYSENT:
case TOX_ERR_FRIEND_ADD_ALREADY_SENT:
sprintf(numstring, "[i] Friend request already sent.");
break;
case TOX_FAERR_UNKNOWN:
sprintf(numstring, "[i] Undefined error when adding friend.");
case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM:
sprintf(numstring, "[i] Address has a bad checksum.");
break;
default:
if (num >= 0) {
sprintf(numstring, "[i] Added friend as %d.", num);
save_data(m);
} else
sprintf(numstring, "[i] Unknown error %i.", num);
case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM:
sprintf(numstring, "[i] New nospam set.");
break;
case TOX_ERR_FRIEND_ADD_MALLOC:
sprintf(numstring, "[i] malloc error.");
break;
case TOX_ERR_FRIEND_ADD_NULL:
sprintf(numstring, "[i] message was NULL.");
break;
case TOX_ERR_FRIEND_ADD_OK:
sprintf(numstring, "[i] Added friend as %d.", num);
save_data(m);
break;
}
new_lines(numstring);
} else if (inpt_command == 'd') {
tox_do(m);
tox_iterate(m);
} else if (inpt_command == 'm') { //message command: /m friendnumber messsage
char *posi[1];
int num = strtoul(line + prompt_offset, posi, 0);
if (**posi != 0) {
if (tox_send_message(m, num, (uint8_t *) *posi + 1, strlen(*posi + 1)) < 1) {
if (tox_friend_send_message(m, num, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *) *posi + 1, strlen(*posi + 1), NULL) < 1) {
char sss[256];
sprintf(sss, "[i] could not send message to friend num %u", num);
new_lines(sss);
@ -410,14 +415,14 @@ void line_eval(Tox *m, char *line)
}
name[i - 3] = 0;
tox_set_name(m, name, i - 2);
tox_self_set_name(m, name, i - 2, NULL);
char numstring[100];
sprintf(numstring, "[i] changed nick to %s", (char *)name);
new_lines(numstring);
} else if (inpt_command == 'l') {
print_friendlist(m);
} else if (inpt_command == 's') {
uint8_t status[TOX_MAX_STATUSMESSAGE_LENGTH];
uint8_t status[TOX_MAX_STATUS_MESSAGE_LENGTH];
size_t i, len = strlen(line);
for (i = 3; i < len; i++) {
@ -427,7 +432,7 @@ void line_eval(Tox *m, char *line)
}
status[i - 3] = 0;
tox_set_status_message(m, status, strlen((char *)status));
tox_self_set_status_message(m, status, strlen((char *)status), NULL);
char numstring[100];
sprintf(numstring, "[i] changed status to %s", (char *)status);
new_lines(numstring);
@ -439,9 +444,9 @@ void line_eval(Tox *m, char *line)
sprintf(numchar, "[i] you either didn't receive that request or you already accepted it");
new_lines(numchar);
} else {
int num = tox_add_friend_norequest(m, pending_requests[numf].id);
uint32_t num = tox_friend_add_norequest(m, pending_requests[numf].id, NULL);
if (num != -1) {
if (num != UINT32_MAX) {
pending_requests[numf].accepted = 1;
sprintf(numchar, "[i] friend request %u accepted as friend no. %d", numf, num);
new_lines(numchar);
@ -475,9 +480,9 @@ void line_eval(Tox *m, char *line)
} while ((c != 'y') && (c != 'n') && (c != EOF));
if (c == 'y') {
int res = tox_del_friend(m, numf);
int res = tox_friend_delete(m, numf, NULL);
if (res == 0)
if (res)
sprintf(msg, "[i] [%i: %s] is no longer your friend", numf, fname);
else
sprintf(msg, "[i] failed to remove friend");
@ -602,7 +607,7 @@ void line_eval(Tox *m, char *line)
if (conversation_default != 0) {
if (conversation_default > 0) {
int friendnumber = conversation_default - 1;
uint32_t res = tox_send_message(m, friendnumber, (uint8_t *)line, strlen(line));
uint32_t res = tox_friend_send_message(m, friendnumber, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *)line, strlen(line), NULL);
if (res == 0) {
char sss[128];
@ -863,20 +868,21 @@ void do_refresh()
refresh();
}
void print_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
void print_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
new_lines("[i] received friend request with message:");
new_lines((char *)data);
char numchar[100];
sprintf(numchar, "[i] accept request with /a %u", num_requests);
new_lines(numchar);
memcpy(pending_requests[num_requests].id, public_key, TOX_CLIENT_ID_SIZE);
memcpy(pending_requests[num_requests].id, public_key, TOX_PUBLIC_KEY_SIZE);
pending_requests[num_requests].accepted = 0;
++num_requests;
do_refresh();
}
void print_message(Tox *m, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
void print_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length,
void *userdata)
{
/* ensure null termination */
uint8_t null_string[length + 1];
@ -885,7 +891,7 @@ void print_message(Tox *m, int friendnumber, const uint8_t *string, uint16_t len
print_formatted_message(m, (char *)null_string, friendnumber, 0);
}
void print_nickchange(Tox *m, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
void print_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
{
char name[TOX_MAX_NAME_LENGTH + 1];
@ -901,7 +907,7 @@ void print_nickchange(Tox *m, int friendnumber, const uint8_t *string, uint16_t
}
}
void print_statuschange(Tox *m, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
void print_statuschange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
{
char name[TOX_MAX_NAME_LENGTH + 1];
@ -919,7 +925,7 @@ void print_statuschange(Tox *m, int friendnumber, const uint8_t *string, uint16_
static char *data_file_name = NULL;
static int load_data(Tox *m)
static Tox *load_data()
{
FILE *data_file = fopen(data_file_name, "r");
@ -936,7 +942,7 @@ static int load_data(Tox *m)
return 0;
}
tox_load(m, data, size);
Tox *m = tox_new(0, data, size, NULL);
if (fclose(data_file) < 0) {
perror("[!] fclose failed");
@ -944,10 +950,10 @@ static int load_data(Tox *m)
/* return 0; */
}
return 1;
return m;
}
return 0;
return tox_new(0, 0, 0, NULL);
}
static int save_data(Tox *m)
@ -960,9 +966,9 @@ static int save_data(Tox *m)
}
int res = 1;
size_t size = tox_size(m);
size_t size = tox_get_savedata_size(m);
uint8_t data[size];
tox_save(m, data);
tox_get_savedata(m, data);
if (fwrite(data, sizeof(uint8_t), size, data_file) != size) {
fputs("[!] could not write data file (1)!", stderr);
@ -977,13 +983,10 @@ static int save_data(Tox *m)
return res;
}
static int load_data_or_init(Tox *m, char *path)
static int save_data_file(Tox *m, char *path)
{
data_file_name = path;
if (load_data(m))
return 1;
if (save_data(m))
return 1;
@ -1125,58 +1128,94 @@ void print_groupnamelistchange(Tox *m, int groupnumber, int peernumber, uint8_t
print_groupchatpeers(m, groupnumber);
}
}
void file_request_accept(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, const uint8_t *filename,
uint16_t filename_length, void *userdata)
void file_request_accept(Tox *tox, uint32_t friend_number, uint32_t file_number, uint32_t type, uint64_t file_size,
const uint8_t *filename, size_t filename_length, void *user_data)
{
if (type != TOX_FILE_KIND_DATA) {
new_lines("Refused invalid file type.");
tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, 0);
return;
}
char msg[512];
sprintf(msg, "[t] %u is sending us: %s of size %llu", friendnumber, filename, (long long unsigned int)filesize);
sprintf(msg, "[t] %u is sending us: %s of size %llu", friend_number, filename, (long long unsigned int)file_size);
new_lines(msg);
if (tox_file_send_control(m, friendnumber, 1, filenumber, 0, 0, 0) == 0) {
sprintf(msg, "Accepted file transfer. (saving file as: %u.%u.bin)", friendnumber, filenumber);
if (tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME, 0)) {
sprintf(msg, "Accepted file transfer. (saving file as: %u.%u.bin)", friend_number, file_number);
new_lines(msg);
} else
new_lines("Could not accept file transfer.");
}
void file_print_control(Tox *m, int friendnumber, uint8_t send_recieve, uint8_t filenumber, uint8_t control_type,
const uint8_t *data, uint16_t length, void *userdata)
void file_print_control(Tox *tox, uint32_t friend_number, uint32_t file_number, TOX_FILE_CONTROL control,
void *user_data)
{
char msg[512] = {0};
if (control_type == 0)
sprintf(msg, "[t] %u accepted file transfer: %u", friendnumber, filenumber);
else if (control_type == 3)
sprintf(msg, "[t] %u file transfer: %u completed", friendnumber, filenumber);
else
sprintf(msg, "[t] control %u received", control_type);
sprintf(msg, "[t] control %u received", control);
new_lines(msg);
if (control == TOX_FILE_CONTROL_CANCEL) {
unsigned int i;
for (i = 0; i < NUM_FILE_SENDERS; ++i) {
/* This is slow */
if (file_senders[i].file && file_senders[i].friendnum == friend_number && file_senders[i].filenumber == file_number) {
fclose(file_senders[i].file);
file_senders[i].file = 0;
char msg[512];
sprintf(msg, "[t] %u file transfer: %u cancelled", file_senders[i].friendnum, file_senders[i].filenumber);
new_lines(msg);
}
}
}
}
void write_file(Tox *m, int friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata)
void write_file(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
size_t length, void *user_data)
{
char filename[256];
sprintf(filename, "%u.%u.bin", friendnumber, filenumber);
FILE *pFile = fopen(filename, "a");
if (tox_file_data_remaining(m, friendnumber, filenumber, 1) == 0) {
//file_control(m, friendnumber, 1, filenumber, 3, 0, 0);
if (length == 0) {
char msg[512];
sprintf(msg, "[t] %u file transfer: %u completed", friendnumber, filenumber);
new_lines(msg);
return;
}
char filename[256];
sprintf(filename, "%u.%u.bin", friendnumber, filenumber);
FILE *pFile = fopen(filename, "r+b");
if (pFile == NULL)
pFile = fopen(filename, "wb");
fseek(pFile, position, SEEK_SET);
if (fwrite(data, length, 1, pFile) != 1)
new_lines("Error writing to file");
fclose(pFile);
}
void print_online(Tox *tox, uint32_t friendnumber, TOX_CONNECTION status, void *userdata)
{
if (status)
printf("\nOther went online.\n");
else {
printf("\nOther went offline.\n");
unsigned int i;
for (i = 0; i < NUM_FILE_SENDERS; ++i)
if (file_senders[i].file != 0 && file_senders[i].friendnum == friendnumber) {
fclose(file_senders[i].file);
file_senders[i].file = 0;
}
}
}
char timeout_getch(Tox *m)
{
char c;
int slpval = tox_do_interval(m);
int slpval = tox_iteration_interval(m);
fd_set fds;
FD_ZERO(&fds);
@ -1214,7 +1253,7 @@ int main(int argc, char *argv[])
}
/* let user override default by cmdline */
uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */
uint8_t ipv6enabled = 1; /* x */
int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled);
if (argvoffset < 0)
@ -1231,25 +1270,28 @@ int main(int argc, char *argv[])
if (!strcmp(argv[argc - 2], "-f"))
filename = argv[argc - 1];
m = tox_new(0);
data_file_name = filename;
m = load_data();
if ( !m ) {
fputs("Failed to allocate Messenger datastructure", stderr);
exit(0);
}
load_data_or_init(m, filename);
save_data_file(m, filename);
tox_callback_friend_request(m, print_request, NULL);
tox_callback_friend_message(m, print_message, NULL);
tox_callback_name_change(m, print_nickchange, NULL);
tox_callback_status_message(m, print_statuschange, NULL);
tox_callback_friend_name(m, print_nickchange, NULL);
tox_callback_friend_status_message(m, print_statuschange, NULL);
tox_callback_group_invite(m, print_invite, NULL);
tox_callback_group_message(m, print_groupmessage, NULL);
tox_callback_file_data(m, write_file, NULL);
tox_callback_file_control(m, file_print_control, NULL);
tox_callback_file_send_request(m, file_request_accept, NULL);
tox_callback_file_recv_chunk(m, write_file, NULL);
tox_callback_file_recv_control(m, file_print_control, NULL);
tox_callback_file_recv(m, file_request_accept, NULL);
tox_callback_file_chunk_request(m, tox_file_chunk_request, NULL);
tox_callback_group_namelist_change(m, print_groupnamelistchange, NULL);
tox_callback_friend_connection_status(m, print_online, NULL);
initscr();
noecho();
@ -1263,7 +1305,7 @@ int main(int argc, char *argv[])
uint16_t port = atoi(argv[argvoffset + 2]);
unsigned char *binary_string = hex_string_to_bin(argv[argvoffset + 3]);
int res = tox_bootstrap_from_address(m, argv[argvoffset + 1], port, binary_string);
int res = tox_bootstrap(m, argv[argvoffset + 1], port, binary_string, NULL);
if (!res) {
printf("Failed to convert \"%s\" into an IP address. Exiting...\n", argv[argvoffset + 1]);
@ -1275,7 +1317,8 @@ int main(int argc, char *argv[])
new_lines("[i] change username with /n");
uint8_t name[TOX_MAX_NAME_LENGTH + 1];
uint16_t namelen = tox_get_self_name(m, name);
tox_self_get_name(m, name);
uint16_t namelen = tox_self_get_name_size(m);
name[namelen] = 0;
if (namelen > 0) {
@ -1288,7 +1331,7 @@ int main(int argc, char *argv[])
while (1) {
if (on == 0) {
if (tox_isconnected(m)) {
if (tox_self_get_connection_status(m)) {
new_lines("[i] connected to DHT");
on = 1;
} else {
@ -1296,15 +1339,12 @@ int main(int argc, char *argv[])
if (timestamp0 + 10 < timestamp1) {
timestamp0 = timestamp1;
tox_bootstrap_from_address(m, argv[argvoffset + 1], port, binary_string);
tox_bootstrap(m, argv[argvoffset + 1], port, binary_string, NULL);
}
}
}
send_filesenders(m);
tox_do(m);
tox_iterate(m);
do_refresh();
int c = timeout_getch(m);
@ -1320,11 +1360,12 @@ int main(int argc, char *argv[])
} else if (c == 8 || c == 127) {
input_line[strlen(input_line) - 1] = '\0';
} else if (isalnum(c) || ispunct(c) || c == ' ') {
strcpy(input_line, appender(input_line, (char) c));
appender(input_line, (char) c);
}
}
free(binary_string);
save_data_file(m, filename);
tox_kill(m);
endwin();
return 0;

View File

@ -1,755 +0,0 @@
/*
* A bot to test Tox avatars
*
* Usage: ./test_avatars <data dir>
*
* Connects to the Tox network, publishes our avatar, requests our friends
* avatars and, if available, saves them to a local cache.
* This bot automatically accepts any friend request.
*
*
* Data dir MUST have:
*
* - A file named "data" (named accordingly to STS Draft v0.1.0) with
* user id, friends, bootstrap data, etc. from a previously configured
* Tox session; use a client (eg. toxic) to configure it, add friends,
* etc.
*
* Data dir MAY have:
*
* - A directory named "avatars" with the user's avatar and cached avatars.
* The user avatar must be named in the format: "<uppercase pub key>.png"
*
*
* The bot will answer to these commands:
*
* !debug-on - Enable extended debug messages
* !debug-off - Disenable extended debug messages
* !set-avatar - Set our avatar from "avatars/<USERID>.png"
* !remove-avatar - Remove our avatar
*
*/
#define DATA_FILE_NAME "data"
#define AVATAR_DIR_NAME "avatars"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "../toxcore/tox.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <stdbool.h>
#include <limits.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <stdarg.h>
/* Basic debug utils */
#define DEBUG(format, ...) debug_printf("DEBUG: %s:%d %s: " format "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
static bool print_debug_msgs = true;
static void debug_printf(const char *fmt, ...)
{
if (print_debug_msgs == true) {
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
}
/* ------------ Avatar cache managenment functions ------------ */
typedef struct {
uint8_t format;
char *suffix;
} avatar_format_data_t;
static const avatar_format_data_t avatar_formats[] = {
/* In order of preference */
{ TOX_AVATAR_FORMAT_PNG, "png" },
{ TOX_AVATAR_FORMAT_NONE, NULL }, /* Must be the last one */
};
static void set_avatar(Tox *tox, const char *base_dir);
static char *get_avatar_suffix_from_format(uint8_t format)
{
int i;
for (i = 0; avatar_formats[i].format != TOX_AVATAR_FORMAT_NONE; i++)
if (avatar_formats[i].format == format)
return avatar_formats[i].suffix;
return NULL;
}
/* Load avatar data from a file into a memory buffer 'buf'.
* buf must have at least TOX_MAX_AVATAR_DATA_LENGTH bytes
* Returns the length of the data sucess or < 0 on error
*/
static int load_avatar_data(char *fname, uint8_t *buf)
{
FILE *fp = fopen(fname, "rb");
if (fp == NULL)
return -1; /* Error */
size_t n = fread(buf, 1, TOX_AVATAR_MAX_DATA_LENGTH, fp);
int ret;
if (ferror(fp) != 0 || n == 0)
ret = -1; /* Error */
else
ret = n;
fclose(fp);
return ret;
}
/* Save avatar data to a file */
static int save_avatar_data(char *fname, uint8_t *data, uint32_t len)
{
FILE *fp = fopen(fname, "wb");
if (fp == NULL)
return -1; /* Error */
int ret = 0; /* Ok */
if (fwrite(data, 1, len, fp) != len)
ret = -1; /* Error */
if (fclose(fp) != 0)
ret = -1; /* Error */
return ret;
}
static void byte_to_hex_str(const uint8_t *buf, const size_t buflen, char *dst)
{
const char *hex_chars = "0123456789ABCDEF";
size_t i = 0;
size_t j = 0;
while (i < buflen) {
dst[j++] = hex_chars[(buf[i] >> 4) & 0xf];
dst[j++] = hex_chars[buf[i] & 0xf];
i++;
}
dst[j++] = '\0';
}
/* Make the cache file name for an avatar of the given format for the given
* public key.
*/
static int make_avatar_file_name(char *dst, size_t dst_len, const char *base_dir,
const uint8_t format, uint8_t *public_key)
{
char public_key_str[2 * TOX_PUBLIC_KEY_SIZE + 1];
byte_to_hex_str(public_key, TOX_PUBLIC_KEY_SIZE, public_key_str);
const char *suffix = get_avatar_suffix_from_format(format);
if (suffix == NULL)
return -1; /* Error */
int n = snprintf(dst, dst_len, "%s/%s/%s.%s", base_dir, AVATAR_DIR_NAME,
public_key_str, suffix);
dst[dst_len - 1] = '\0';
if (n >= dst_len)
return -1; /* Error: Output truncated */
return 0; /* Ok */
}
/* Load a cached avatar into the buffer 'data' (which must be at least
* TOX_MAX_AVATAR_DATA_LENGTH bytes long). Gets the file name from client
* id and the given data format.
* Returns 0 on success, or -1 on error.
*/
static int load_user_avatar(Tox *tox, char *base_dir, int friendnum,
uint8_t format, uint8_t *hash, uint8_t *data, uint32_t *datalen)
{
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
if (tox_get_client_id(tox, friendnum, addr) != 0) {
DEBUG("Bad client id, friendnumber=%d", friendnum);
return -1;
}
char path[PATH_MAX];
int ret = make_avatar_file_name(path, sizeof(path), base_dir, format, addr);
if (ret != 0) {
DEBUG("Can't create an file name for this user/avatar.");
return -1;
}
ret = load_avatar_data(path, data);
if (ret < 0) {
DEBUG("Failed to load avatar data.");
return -1;
}
*datalen = ret;
tox_hash(hash, data, *datalen);
return 0;
}
/* Save a user avatar into the cache. Gets the file name from the public key
* and the given data format.
* Returns 0 on success, or -1 on error.
*/
static int save_user_avatar(Tox *tox, char *base_dir, int friendnum,
uint8_t format, uint8_t *data, uint32_t datalen)
{
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
if (tox_get_client_id(tox, friendnum, addr) != 0) {
DEBUG("Bad client id, friendnumber=%d", friendnum);
return -1;
}
char path[PATH_MAX];
int ret = make_avatar_file_name(path, sizeof(path), base_dir, format, addr);
if (ret != 0) {
DEBUG("Can't create a file name for this user/avatar");
return -1;
}
return save_avatar_data(path, data, datalen);
}
/* Delete all cached avatars for a given user */
static int delete_user_avatar(Tox *tox, char *base_dir, int friendnum)
{
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
if (tox_get_client_id(tox, friendnum, addr) != 0) {
DEBUG("Bad client id, friendnumber=%d", friendnum);
return -1;
}
char path[PATH_MAX];
/* This iteration is dumb and inefficient */
int i;
for (i = 0; avatar_formats[i].format != TOX_AVATAR_FORMAT_NONE; i++) {
int ret = make_avatar_file_name(path, sizeof(path), base_dir,
avatar_formats[i].format, addr);
if (ret != 0) {
DEBUG("Failed to create avatar path for friend #%d, format %d\n",
friendnum, avatar_formats[i].format);
continue;
}
if (unlink(path) == 0)
printf("Avatar file %s deleted.\n", path);
}
return 0;
}
/* ------------ Protocol callbacks ------------ */
static void friend_status_cb(Tox *tox, int n, uint8_t status, void *ud)
{
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1];
if (tox_get_client_id(tox, n, addr) == 0) {
byte_to_hex_str(addr, TOX_PUBLIC_KEY_SIZE, addr_str);
printf("Receiving status from %s: %u\n", addr_str, status);
}
}
static void friend_avatar_info_cb(Tox *tox, int32_t n, uint8_t format, uint8_t *hash, void *ud)
{
char *base_dir = (char *) ud;
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1];
char hash_str[2 * TOX_HASH_LENGTH + 1];
if (tox_get_client_id(tox, n, addr) == 0) {
byte_to_hex_str(addr, TOX_PUBLIC_KEY_SIZE, addr_str);
printf("Receiving avatar information from %s.\n", addr_str);
} else {
DEBUG("tox_get_client_id failed");
printf("Receiving avatar information from friend number %u.\n", n);
}
byte_to_hex_str(hash, TOX_HASH_LENGTH, hash_str);
DEBUG("format=%u, hash=%s", format, hash_str);
if (format == TOX_AVATAR_FORMAT_NONE) {
printf(" -> User do not have an avatar.\n");
/* User have no avatar anymore, delete it from our cache */
delete_user_avatar(tox, base_dir, n);
} else {
/* Check the hash of the currently cached user avatar
* WARNING: THIS IS ONLY AN EXAMPLE!
*
* Real clients should keep the hashes in memory (eg. in the object
* used to represent a friend in the friend list) and do not access
* the file system or do anything resource intensive in reply of
* these events.
*/
uint32_t cur_av_len;
uint8_t cur_av_data[TOX_AVATAR_MAX_DATA_LENGTH];
uint8_t cur_av_hash[TOX_HASH_LENGTH];
int ret;
ret = load_user_avatar(tox, base_dir, n, format, cur_av_hash, cur_av_data, &cur_av_len);
if (ret != 0
&& memcpy(cur_av_hash, hash, TOX_HASH_LENGTH) != 0) {
printf(" -> Cached avatar is outdated. Requesting avatar data.\n");
tox_request_avatar_data(tox, n);
} else {
printf(" -> Cached avatar is still updated.\n");
}
}
}
static void friend_avatar_data_cb(Tox *tox, int32_t n, uint8_t format,
uint8_t *hash, uint8_t *data, uint32_t datalen, void *ud)
{
char *base_dir = (char *) ud;
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1];
char hash_str[2 * TOX_HASH_LENGTH + 1];
if (tox_get_client_id(tox, n, addr) == 0) {
byte_to_hex_str(addr, TOX_PUBLIC_KEY_SIZE, addr_str);
printf("Receiving avatar data from %s.\n", addr_str);
} else {
DEBUG("tox_get_client_id failed");
printf("Receiving avatar data from friend number %u.\n", n);
}
byte_to_hex_str(hash, TOX_HASH_LENGTH, hash_str);
DEBUG("format=%u, datalen=%d, hash=%s\n", format, datalen, hash_str);
delete_user_avatar(tox, base_dir, n);
if (format != TOX_AVATAR_FORMAT_NONE) {
int ret = save_user_avatar(tox, base_dir, n, format, data, datalen);
if (ret == 0)
printf(" -> Avatar updated in the cache.\n");
else
printf(" -> Failed to save user avatar.\n");
}
}
static void friend_msg_cb(Tox *tox, int n, const uint8_t *msg, uint16_t len, void *ud)
{
const char *base_dir = (char *) ud;
const char *msg_str = (char *) msg;
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1];
if (tox_get_client_id(tox, n, addr) == 0) {
byte_to_hex_str(addr, TOX_FRIEND_ADDRESS_SIZE, addr_str);
printf("Receiving message from %s:\n %s\n", addr_str, msg);
}
/* Handle bot commands for the tests */
char *reply_ptr = NULL;
if (strstr(msg_str, "!debug-on") != NULL) {
print_debug_msgs = true;
reply_ptr = "Debug enabled.";
} else if (strstr(msg_str, "!debug-off") != NULL) {
print_debug_msgs = false;
reply_ptr = "Debug disabled.";
} else if (strstr(msg_str, "!set-avatar") != NULL) {
set_avatar(tox, base_dir);
reply_ptr = "Setting image avatar";
} else if (strstr(msg_str, "!remove-avatar") != NULL) {
int r = tox_set_avatar(tox, TOX_AVATAR_FORMAT_NONE, NULL, 0);
DEBUG("tox_set_avatar returned %d", r);
reply_ptr = "Removing avatar";
}
/* Add more useful commands here: add friend, etc. */
char reply[TOX_MAX_MESSAGE_LENGTH];
int reply_len;
if (reply_ptr)
reply_len = snprintf(reply, sizeof(reply), "%s", reply_ptr);
else
reply_len = snprintf(reply, sizeof(reply),
"No command found in message: %s", msg);
reply[sizeof(reply) - 1] = '\0';
printf(" -> Reply: %s\n", reply);
tox_send_message(tox, n, (uint8_t *) reply, reply_len);
}
static void friend_request_cb(Tox *tox, const uint8_t *public_key,
const uint8_t *data, uint16_t length, void *ud)
{
char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1];
byte_to_hex_str(public_key, TOX_PUBLIC_KEY_SIZE, addr_str);
printf("Accepting friend request from %s.\n %s\n", addr_str, data);
tox_add_friend_norequest(tox, public_key);
}
static void set_avatar(Tox *tox, const char *base_dir)
{
uint8_t addr[TOX_FRIEND_ADDRESS_SIZE];
char path[PATH_MAX];
uint8_t buf[2 * TOX_AVATAR_MAX_DATA_LENGTH];
tox_get_address(tox, addr);
int i;
for (i = 0; ; i++) {
if (avatar_formats[i].format == TOX_AVATAR_FORMAT_NONE) {
tox_set_avatar(tox, TOX_AVATAR_FORMAT_NONE, NULL, 0);
printf("No avatar file found, setting to NONE.\n");
break;
} else {
int ret = make_avatar_file_name(path, sizeof(path), base_dir,
avatar_formats[i].format, addr);
if (ret < 0) {
printf("Failed to generate avatar file name.\n");
return;
}
int len = load_avatar_data(path, buf);
if (len < 0) {
printf("Failed to load avatar data from file: %s\n", path);
continue;
}
if (len > TOX_AVATAR_MAX_DATA_LENGTH) {
printf("Avatar file %s is too big (more than %d bytes)",
path, TOX_AVATAR_MAX_DATA_LENGTH);
return;
}
ret = tox_set_avatar(tox, avatar_formats[i].format, buf, len);
DEBUG("tox_set_avatar returned=%d", ret);
if (ret == 0)
printf("Setting avatar from %s (%d bytes).\n", path, len);
else
printf("Error setting avatar from %s.\n", path);
return;
}
}
}
static void print_avatar_info(Tox *tox)
{
uint8_t format;
uint8_t data[TOX_AVATAR_MAX_DATA_LENGTH];
uint8_t hash[TOX_HASH_LENGTH];
uint32_t data_length;
char hash_str[2 * TOX_HASH_LENGTH + 1];
int ret = tox_get_self_avatar(tox, &format, data, &data_length, sizeof(data), hash);
DEBUG("tox_get_self_avatar returned %d", ret);
DEBUG("format: %d, data_length: %d", format, data_length);
byte_to_hex_str(hash, TOX_HASH_LENGTH, hash_str);
DEBUG("hash: %s", hash_str);
}
/* ------------ Initialization functions ------------ */
/* Create directory to store tha avatars. Returns 0 if it was sucessfuly
* created or already existed. Returns -1 on error.
*/
static int create_avatar_diretory(const char *base_dir)
{
char path[PATH_MAX];
int n = snprintf(path, sizeof(path), "%s/%s", base_dir, AVATAR_DIR_NAME);
path[sizeof(path) - 1] = '\0';
if (n >= sizeof(path))
return -1;
if (mkdir(path, 0755) == 0) {
return 0; /* Done */
} else if (errno == EEXIST) {
/* Check if the existing path is a directory */
struct stat st;
if (stat(path, &st) != 0) {
perror("stat()ing avatar directory");
return -1;
}
if (S_ISDIR(st.st_mode))
return 0;
}
return -1; /* Error */
}
static void *load_bootstrap_data(const char *base_dir, uint32_t *len)
{
char path[PATH_MAX];
int n = snprintf(path, sizeof(path), "%s/%s", base_dir, DATA_FILE_NAME);
path[sizeof(path) - 1] = '\0';
if (n >= sizeof(path)) {
printf("Load error: path %s too long\n", path);
return NULL;
}
/* We should be using POSIX functions here, but let's try to be
* compatible with Windows.
*/
FILE *fp = fopen(path, "rb");
if (fp == NULL) {
printf("fatal error: file %s not found.\n", path);
return NULL;
}
if (fseek(fp, 0, SEEK_END) != 0) {
printf("seek fail\n");
fclose(fp);
return NULL;
}
int32_t flen = ftell(fp);
if (flen < 8 || flen > 2e6) {
printf("Fatal error: file %s have %u bytes. Out of acceptable range.\n", path, flen);
fclose(fp);
return NULL;
}
if (fseek(fp, 0, SEEK_SET) != 0) {
printf("seek fail\n");
fclose(fp);
return NULL;
}
void *buf = malloc(flen);
if (buf == NULL) {
printf("malloc failed, %u bytes", flen);
fclose(fp);
return NULL;
}
*len = fread(buf, 1, flen, fp);
fclose(fp);
if (*len != flen) {
printf("fatal: %s have %u bytes, read only %u\n", path, flen, *len);
free(buf);
return NULL;
}
printf("bootstrap data loaded from %s (%u bytes)\n", path, flen);
return buf;
}
static int save_bootstrap_data(Tox *tox, const char *base_dir)
{
char path[PATH_MAX];
int n = snprintf(path, sizeof(path), "%s/%s", base_dir, DATA_FILE_NAME);
path[sizeof(path) - 1] = '\0';
if (n >= sizeof(path)) {
printf("Save error: path %s too long\n", path);
return -1;
}
char path_tmp[PATH_MAX];
n = snprintf(path_tmp, sizeof(path_tmp), "%s.tmp", path);
path_tmp[sizeof(path_tmp) - 1] = '\0';
if (n >= sizeof(path_tmp)) {
printf("error: path %s too long\n", path);
return -1;
}
uint32_t len = tox_size(tox);
if (len < 8 || len > 2e6) {
printf("save data length == %u, out of acceptable range\n", len);
return -1;
}
void *buf = malloc(len);
if (buf == NULL) {
printf("save data: malloc failed\n");
return -1;
}
tox_save(tox, buf);
FILE *fp = fopen(path_tmp, "wb");
if (fp == NULL) {
printf("Error saving data: can't open %s\n", path_tmp);
free(buf);
return -1;
}
if (fwrite(buf, 1, len, fp) != len) {
printf("Error writing data to %s\n", path_tmp);
free(buf);
fclose(fp);
return -1;
}
free(buf);
if (fclose(fp) != 0) {
printf("Error writing data to %s\n", path_tmp);
return -1;
}
if (rename(path_tmp, path) != 0) {
printf("Error renaming %s to %s\n", path_tmp, path);
return -1;
}
printf("Bootstrap data saved to %s\n", path);
return 0; /* Done */
}
int main(int argc, char *argv[])
{
int ret;
if (argc != 2) {
printf("usage: %s <data dir>\n", argv[0]);
return 1;
}
char *base_dir = argv[1];
if (create_avatar_diretory(base_dir) != 0)
printf("Error creating avatar directory.\n");
Tox *tox = tox_new(NULL);
uint32_t len;
void *data = load_bootstrap_data(base_dir, &len);
if (data == NULL)
return 1;
ret = tox_load(tox, data, len);
free(data);
if (ret == 0) {
printf("Tox initialized\n");
} else {
printf("Fatal: tox_load returned %d\n", ret);
return 1;
}
tox_callback_connection_status(tox, friend_status_cb, NULL);
tox_callback_friend_message(tox, friend_msg_cb, base_dir);
tox_callback_friend_request(tox, friend_request_cb, NULL);
tox_callback_avatar_info(tox, friend_avatar_info_cb, base_dir);
tox_callback_avatar_data(tox, friend_avatar_data_cb, base_dir);
uint8_t addr[TOX_FRIEND_ADDRESS_SIZE];
char addr_str[2 * TOX_FRIEND_ADDRESS_SIZE + 1];
tox_get_address(tox, addr);
byte_to_hex_str(addr, TOX_FRIEND_ADDRESS_SIZE, addr_str);
printf("Using local tox address: %s\n", addr_str);
#ifdef TEST_SET_RESET_AVATAR
printf("Printing default avatar information:\n");
print_avatar_info(tox);
printf("Setting a new avatar:\n");
set_avatar(tox, base_dir);
print_avatar_info(tox);
printf("Removing the avatar we just set:\n");
tox_avatar(tox, TOX_AVATARFORMAT_NONE, NULL, 0);
print_avatar_info(tox);
printf("Setting that avatar again:\n");
#endif /* TEST_SET_RESET_AVATAR */
set_avatar(tox, base_dir);
print_avatar_info(tox);
bool waiting = true;
time_t last_save = time(0);
while (1) {
if (tox_isconnected(tox) && waiting) {
printf("DHT connected.\n");
waiting = false;
}
tox_do(tox);
time_t now = time(0);
if (now - last_save > 120) {
save_bootstrap_data(tox, base_dir);
last_save = now;
}
usleep(500000);
}
return 0;
}

View File

@ -45,15 +45,16 @@
#define c_sleep(x) usleep(1000*x)
void print_online(Tox *tox, int friendnumber, uint8_t status, void *userdata)
void print_online(Tox *tox, uint32_t friendnumber, TOX_CONNECTION status, void *userdata)
{
if (status == 1)
if (status)
printf("\nOther went online.\n");
else
printf("\nOther went offline.\n");
}
void print_message(Tox *tox, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
void print_message(Tox *tox, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length,
void *userdata)
{
int master = *((int *)userdata);
write(master, string, length);
@ -62,7 +63,7 @@ void print_message(Tox *tox, int friendnumber, const uint8_t *string, uint16_t l
int main(int argc, char *argv[])
{
uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */
uint8_t ipv6enabled = 1; /* x */
int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled);
if (argvoffset < 0)
@ -94,14 +95,14 @@ int main(int argc, char *argv[])
printf("error setting flags\n");
}
Tox *tox = tox_new(0);
tox_callback_connection_status(tox, print_online, NULL);
Tox *tox = tox_new(0, 0, 0, 0);
tox_callback_friend_connection_status(tox, print_online, NULL);
tox_callback_friend_message(tox, print_message, master);
uint16_t port = atoi(argv[argvoffset + 2]);
unsigned char *binary_string = hex_string_to_bin(argv[argvoffset + 3]);
int res = tox_bootstrap_from_address(tox, argv[argvoffset + 1], port, binary_string);
int res = tox_bootstrap(tox, argv[argvoffset + 1], port, binary_string, 0);
free(binary_string);
if (!res) {
@ -109,11 +110,11 @@ int main(int argc, char *argv[])
exit(1);
}
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(tox, address);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(tox, address);
uint32_t i;
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
for (i = 0; i < TOX_ADDRESS_SIZE; i++) {
printf("%02X", address[i]);
}
@ -125,10 +126,10 @@ int main(int argc, char *argv[])
}
uint8_t *bin_id = hex_string_to_bin(temp_id);
int num = tox_add_friend(tox, bin_id, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"));
uint32_t num = tox_friend_add(tox, bin_id, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"), 0);
free(bin_id);
if (num < 0) {
if (num == UINT32_MAX) {
printf("\nSomething went wrong when adding friend.\n");
return 1;
}
@ -136,22 +137,22 @@ int main(int argc, char *argv[])
uint8_t notconnected = 1;
while (1) {
if (tox_isconnected(tox) && notconnected) {
if (tox_self_get_connection_status(tox) && notconnected) {
printf("\nDHT connected.\n");
notconnected = 0;
}
while (tox_get_friend_connection_status(tox, num) == 1) {
while (tox_friend_get_connection_status(tox, num, 0)) {
uint8_t buf[TOX_MAX_MESSAGE_LENGTH];
ret = read(*master, buf, sizeof(buf));
if (ret <= 0)
break;
tox_send_message(tox, num, buf, ret);
tox_friend_send_message(tox, num, TOX_MESSAGE_TYPE_NORMAL, buf, ret, 0);
}
tox_do(tox);
tox_iterate(tox);
c_sleep(1);
}

View File

@ -46,43 +46,39 @@
#define NUM_FILE_SENDERS 256
typedef struct {
FILE *file;
uint16_t friendnum;
uint8_t filenumber;
uint8_t nextpiece[1400];
uint16_t piecelength;
uint32_t friendnum;
uint32_t filenumber;
} File_t;
File_t file_senders[NUM_FILE_SENDERS];
File_t file_recv[NUM_FILE_SENDERS];
uint8_t numfilesenders;
void send_filesenders(Tox *m)
void tox_file_chunk_request(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position, size_t length,
void *user_data)
{
uint32_t i;
unsigned int i;
for (i = 0; i < NUM_FILE_SENDERS; ++i) {
if (file_senders[i].file == 0)
continue;
while (1) {
if (tox_file_send_data(m, file_senders[i].friendnum, file_senders[i].filenumber, file_senders[i].nextpiece,
file_senders[i].piecelength) != 0)
break;
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) {
/* This is slow */
if (file_senders[i].file && file_senders[i].friendnum == friend_number && file_senders[i].filenumber == file_number) {
if (length == 0) {
fclose(file_senders[i].file);
file_senders[i].file = 0;
printf("[t] %u file transfer: %u completed %i\n", file_senders[i].friendnum, file_senders[i].filenumber,
tox_file_send_control(m, file_senders[i].friendnum, 0, file_senders[i].filenumber, TOX_FILECONTROL_FINISHED, 0, 0));
printf("[t] %u file transfer: %u completed\n", file_senders[i].friendnum, file_senders[i].filenumber);
break;
}
fseek(file_senders[i].file, position, SEEK_SET);
uint8_t data[length];
int len = fread(data, 1, length, file_senders[i].file);
tox_file_send_chunk(tox, friend_number, file_number, position, data, len, 0);
break;
}
}
}
int add_filesender(Tox *m, uint16_t friendnum, char *filename)
uint32_t add_filesender(Tox *m, uint16_t friendnum, char *filename)
{
FILE *tempfile = fopen(filename, "rb");
@ -92,22 +88,20 @@ 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_file_sender(m, friendnum, filesize, (uint8_t *)filename, strlen(filename) + 1);
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_DATA, filesize, 0, (uint8_t *)filename,
strlen(filename), 0);
if (filenum == -1)
return -1;
file_senders[numfilesenders].file = tempfile;
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;
file_senders[numfilesenders].filenumber = filenum;
++numfilesenders;
return filenum;
}
void kill_filesender(Tox *m, uint8_t filenum)
void kill_filesender(Tox *m, uint32_t filenum)
{
uint32_t i;
@ -130,9 +124,15 @@ int not_sending()
static char path[1024];
void file_request_accept(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, const uint8_t *filename,
uint16_t filename_length, void *userdata)
void file_request_accept(Tox *tox, uint32_t friend_number, uint32_t file_number, uint32_t type, uint64_t file_size,
const uint8_t *filename, size_t filename_length, void *user_data)
{
if (type != TOX_FILE_KIND_DATA) {
printf("Refused invalid file type.");
tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, 0);
return;
}
char fullpath[1024];
uint32_t i;
uint16_t rm = 0;
@ -151,58 +151,87 @@ void file_request_accept(Tox *m, int friendnumber, uint8_t filenumber, uint64_t
if (tempfile != 0) {
fclose(tempfile);
tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_KILL, 0, 0);
tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, 0);
return;
}
file_recv[filenumber].file = fopen(fullpath, "wb");
uint8_t file_index = (file_number >> 16) - 1;
file_recv[file_index].file = fopen(fullpath, "wb");
if (file_recv[filenumber].file == 0) {
tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_KILL, 0, 0);
if (file_recv[file_index].file == 0) {
tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, 0);
return;
}
if (tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) {
if (tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME, 0)) {
printf("Accepted file transfer. (file: %s)\n", fullpath);
}
}
void file_print_control(Tox *m, int friendnumber, uint8_t recieve_send, uint8_t filenumber, uint8_t control_type,
const uint8_t *data,
uint16_t length, void *userdata)
void file_print_control(Tox *tox, uint32_t friend_number, uint32_t file_number, TOX_FILE_CONTROL control,
void *user_data)
{
if (recieve_send == 1 && (control_type == TOX_FILECONTROL_KILL || control_type == TOX_FILECONTROL_FINISHED)) {
kill_filesender(m, filenumber);
if (file_number < (1 << 15) && (control == TOX_FILE_CONTROL_CANCEL)) {
kill_filesender(tox, file_number);
return;
}
if (recieve_send == 0 && (control_type == TOX_FILECONTROL_KILL || control_type == TOX_FILECONTROL_FINISHED)) {
fclose(file_recv[filenumber].file);
if (file_number > (1 << 15) && (control == TOX_FILE_CONTROL_CANCEL)) {
uint8_t file_index = (file_number >> 16) - 1;
fclose(file_recv[file_index].file);
printf("File closed\n");
file_recv[filenumber].file = 0;
file_recv[file_index].file = 0;
return;
}
}
void write_file(Tox *m, int friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata)
void write_file(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
size_t length, void *user_data)
{
if (file_recv[filenumber].file != 0)
if (fwrite(data, length, 1, file_recv[filenumber].file) != 1)
uint8_t file_index = (filenumber >> 16) - 1;
if (length == 0) {
fclose(file_recv[file_index].file);
printf("File closed\n");
file_recv[file_index].file = 0;
printf("%u file transfer: %u completed\n", friendnumber, filenumber);
return;
}
if (file_recv[file_index].file != 0) {
fseek(file_recv[file_index].file, position, SEEK_SET);
if (fwrite(data, length, 1, file_recv[file_index].file) != 1)
printf("Error writing data\n");
}
}
void print_online(Tox *tox, int friendnumber, uint8_t status, void *userdata)
void print_online(Tox *tox, uint32_t friendnumber, TOX_CONNECTION status, void *userdata)
{
if (status == 1)
if (status)
printf("\nOther went online.\n");
else
else {
printf("\nOther went offline.\n");
unsigned int i;
for (i = 0; i < NUM_FILE_SENDERS; ++i) {
if (file_senders[i].file != 0) {
fclose(file_senders[i].file);
file_senders[i].file = 0;
}
if (file_recv[i].file != 0) {
fclose(file_recv[i].file);
file_recv[i].file = 0;
}
}
}
}
int main(int argc, char *argv[])
{
uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */
uint8_t ipv6enabled = 1; /* x */
int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled);
if (argvoffset < 0)
@ -214,15 +243,16 @@ int main(int argc, char *argv[])
exit(0);
}
Tox *tox = tox_new(0);
tox_callback_file_data(tox, write_file, NULL);
tox_callback_file_control(tox, file_print_control, NULL);
tox_callback_file_send_request(tox, file_request_accept, NULL);
tox_callback_connection_status(tox, print_online, NULL);
Tox *tox = tox_new(0, 0, 0, 0);
tox_callback_file_recv_chunk(tox, write_file, NULL);
tox_callback_file_recv_control(tox, file_print_control, NULL);
tox_callback_file_recv(tox, file_request_accept, NULL);
tox_callback_file_chunk_request(tox, tox_file_chunk_request, NULL);
tox_callback_friend_connection_status(tox, print_online, NULL);
uint16_t port = atoi(argv[argvoffset + 2]);
unsigned char *binary_string = hex_string_to_bin(argv[argvoffset + 3]);
int res = tox_bootstrap_from_address(tox, argv[argvoffset + 1], port, binary_string);
int res = tox_bootstrap(tox, argv[argvoffset + 1], port, binary_string, 0);
free(binary_string);
if (!res) {
@ -230,11 +260,11 @@ int main(int argc, char *argv[])
exit(1);
}
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(tox, address);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(tox, address);
uint32_t i;
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
for (i = 0; i < TOX_ADDRESS_SIZE; i++) {
printf("%02X", address[i]);
}
@ -246,10 +276,10 @@ int main(int argc, char *argv[])
}
uint8_t *bin_id = hex_string_to_bin(temp_id);
int num = tox_add_friend(tox, bin_id, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"));
uint32_t num = tox_friend_add(tox, bin_id, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"), 0);
free(bin_id);
if (num < 0) {
if (num == UINT32_MAX) {
printf("\nSomething went wrong when adding friend.\n");
return 1;
}
@ -260,12 +290,12 @@ int main(int argc, char *argv[])
uint8_t notconnected = 1;
while (1) {
if (tox_isconnected(tox) && notconnected) {
if (tox_self_get_connection_status(tox) && notconnected) {
printf("\nDHT connected.\n");
notconnected = 0;
}
if (not_sending() && tox_get_friend_connection_status(tox, num)) {
if (not_sending() && tox_friend_get_connection_status(tox, num, 0)) {
d = opendir(path);
if (d) {
@ -290,8 +320,7 @@ int main(int argc, char *argv[])
}
}
send_filesenders(tox);
tox_do(tox);
tox_iterate(tox);
c_sleep(1);
}

View File

@ -211,21 +211,21 @@ void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
uint32_t sampling_rate,
void *user_data)
{
CallControl* cc = user_data;
frame* f = malloc(sizeof(frame) + sample_count * sizeof(int16_t));
memcpy(f->data, pcm, sample_count);
f->size = sample_count/channels;
// CallControl* cc = user_data;
// frame* f = malloc(sizeof(frame) + sample_count * sizeof(int16_t));
// memcpy(f->data, pcm, sample_count);
// f->size = sample_count/channels;
//
// pthread_mutex_lock(cc->arb_mutex);
// free(rb_write(cc->arb, f));
// pthread_mutex_unlock(cc->arb_mutex);
pthread_mutex_lock(cc->arb_mutex);
free(rb_write(cc->arb, f));
pthread_mutex_unlock(cc->arb_mutex);
// Pa_WriteStream(adout, pcm, sample_count/channels);
Pa_WriteStream(adout, pcm, sample_count/channels);
}
void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
if (length == 7 && memcmp("gentoo", data, 7) == 0) {
tox_add_friend_norequest(m, public_key);
assert(tox_friend_add_norequest(m, public_key, NULL) != ~0);
}
}
@ -237,11 +237,17 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA
Tox* Alice;
Tox* Bob;
*bootstrap = tox_new(0);
Alice = tox_new(0);
Bob = tox_new(0);
assert(bootstrap && Alice && Bob);
{
TOX_ERR_NEW error;
*bootstrap = tox_new(NULL, NULL, 0, &error);
assert(error == TOX_ERR_NEW_OK);
Alice = tox_new(NULL, NULL, 0, &error);
assert(error == TOX_ERR_NEW_OK);
Bob = tox_new(NULL, NULL, 0, &error);
assert(error == TOX_ERR_NEW_OK);
}
printf("Created 3 instances of Tox\n");
@ -249,26 +255,30 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA
long long unsigned int cur_time = time(NULL);
uint32_t to_compare = 974536;
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
uint8_t address[TOX_ADDRESS_SIZE];
tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare);
tox_get_address(Alice, address);
tox_self_get_address(Alice, address);
assert(tox_add_friend(Bob, address, (uint8_t *)"gentoo", 7) >= 0);
assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != ~0);
uint8_t off = 1;
while (1) {
tox_do(*bootstrap);
tox_do(Alice);
tox_do(Bob);
tox_iterate(*bootstrap);
tox_iterate(Alice);
tox_iterate(Bob);
if (tox_isconnected(*bootstrap) && tox_isconnected(Alice) && tox_isconnected(Bob) && off) {
if (tox_self_get_connection_status(*bootstrap) &&
tox_self_get_connection_status(Alice) &&
tox_self_get_connection_status(Bob) && off) {
printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
off = 0;
}
if (tox_get_friend_connection_status(Alice, 0) == 1 && tox_get_friend_connection_status(Bob, 0) == 1)
if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP &&
tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP)
break;
c_sleep(20);
@ -300,11 +310,11 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA
}
int iterate_tox(Tox* bootstrap, ToxAV* AliceAV, ToxAV* BobAV)
{
tox_do(bootstrap);
tox_do(toxav_get_tox(AliceAV));
tox_do(toxav_get_tox(BobAV));
tox_iterate(bootstrap);
tox_iterate(toxav_get_tox(AliceAV));
tox_iterate(toxav_get_tox(BobAV));
return MIN(tox_do_interval(toxav_get_tox(AliceAV)), tox_do_interval(toxav_get_tox(BobAV)));
return MIN(tox_iteration_interval(toxav_get_tox(AliceAV)), tox_iteration_interval(toxav_get_tox(BobAV)));
}
void* iterate_toxav (void * data)
{

View File

@ -23,9 +23,11 @@
#endif
#include "group.h"
#include "../toxcore/util.h"
#include "../toxcore/logger.h"
#define GROUP_JBUF_SIZE 6
#define GROUP_JBUF_DEAD_SECONDS 4
typedef struct {
uint16_t sequnum;
@ -39,6 +41,7 @@ typedef struct {
uint32_t capacity;
uint16_t bottom;
uint16_t top;
uint64_t last_queued_time;
} Group_JitterBuffer;
static Group_JitterBuffer *create_queue(unsigned int capacity)
@ -90,11 +93,19 @@ static int queue(Group_JitterBuffer *q, Group_Audio_Packet *pk)
unsigned int num = sequnum % q->size;
if (!is_timeout(q->last_queued_time, GROUP_JBUF_DEAD_SECONDS)) {
if ((uint32_t)(sequnum - q->bottom) > (1 << 15)) {
/* Drop old packet. */
return -1;
}
}
if ((uint32_t)(sequnum - q->bottom) > q->size) {
clear_queue(q);
q->bottom = sequnum - q->capacity;
q->queue[num] = pk;
q->top = sequnum + 1;
q->last_queued_time = unix_time();
return 0;
}
@ -106,6 +117,7 @@ static int queue(Group_JitterBuffer *q, Group_Audio_Packet *pk)
if ((sequnum - q->bottom) >= (q->top - q->bottom))
q->top = sequnum + 1;
q->last_queued_time = unix_time();
return 0;
}

View File

@ -88,10 +88,10 @@ static int invoke_callback(MSICall* call, MSICallbackID cb);
static MSICall *get_call ( MSISession *session, uint32_t friend_id );
MSICall *new_call ( MSISession *session, uint32_t friend_id );
void kill_call ( MSICall *call );
void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data);
void on_peer_status(Messenger *m, uint32_t friend_id, uint8_t status, void *data);
void handle_push ( MSICall *call, const MSIMessage *msg );
void handle_pop ( MSICall *call, const MSIMessage *msg );
void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object );
void handle_msi_packet ( Messenger *m, uint32_t friend_id, const uint8_t *data, uint16_t length, void *object );
/**
@ -572,7 +572,7 @@ CLEAR_CONTAINER:
free(call);
session->calls = NULL;
}
void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data)
void on_peer_status(Messenger* m, uint32_t friend_id, uint8_t status, void* data)
{
(void)m;
MSISession *session = data;
@ -744,7 +744,7 @@ void handle_pop ( MSICall *call, const MSIMessage *msg )
kill_call ( call );
}
void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object )
void handle_msi_packet ( Messenger* m, uint32_t friend_id, const uint8_t* data, uint16_t length, void* object )
{
LOGGER_DEBUG("Got msi message");

View File

@ -25,6 +25,7 @@
#include "../toxcore/logger.h"
#include "../toxcore/util.h"
#include "../toxcore/Messenger.h"
#include "rtp.h"
#include <stdlib.h>
@ -77,11 +78,9 @@ RTPMessage *msg_parse ( const uint8_t *data, int length );
uint8_t *parse_header_out ( const RTPHeader* header, uint8_t* payload );
uint8_t *parse_ext_header_out ( const RTPExtHeader* header, uint8_t* payload );
void build_header ( RTPSession* session, RTPHeader* header );
void send_rtcp_report ( RTCPSession* session, Messenger* m, int32_t friendnumber );
int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object );
int handle_rtcp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object );
void send_rtcp_report ( RTCPSession* session, Messenger* m, uint32_t friendnumber );
int handle_rtp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object );
int handle_rtcp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object );
RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
@ -121,7 +120,7 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
return NULL;
}
retu->rtcp_session->prefix = 222 + payload_type % 192;
retu->rtcp_session->prefix = payload_type + 2;
retu->rtcp_session->pl_stats = rb_new(4);
retu->rtcp_session->rtp_session = retu;
@ -201,15 +200,15 @@ int rtp_start_receiving(RTPSession* session)
if (session == NULL)
return -1;
if (custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix,
if (m_callback_rtp_packet(session->m, session->dest, session->prefix,
handle_rtp_packet, session) == -1) {
LOGGER_WARNING("Failed to register rtp receive handler");
return -1;
}
if (custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp_session->prefix,
if (m_callback_rtp_packet(session->m, session->dest, session->rtcp_session->prefix,
handle_rtcp_packet, session->rtcp_session) == -1) {
LOGGER_WARNING("Failed to register rtcp receive handler");
custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, NULL, NULL);
m_callback_rtp_packet(session->m, session->dest, session->prefix, NULL, NULL);
return -1;
}
@ -220,8 +219,8 @@ int rtp_stop_receiving(RTPSession* session)
if (session == NULL)
return -1;
custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, NULL, NULL);
custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp_session->prefix, NULL, NULL); /* RTCP */
m_callback_rtp_packet(session->m, session->dest, session->prefix, NULL, NULL);
m_callback_rtp_packet(session->m, session->dest, session->rtcp_session->prefix, NULL, NULL); /* RTCP */
return 0;
}
@ -251,6 +250,7 @@ int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length )
memcpy ( it, data, length );
if ( -1 == send_custom_lossy_packet(session->m, session->dest, parsed, parsed_len) ) {
LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno));
return -1;
@ -514,7 +514,7 @@ void build_header ( RTPSession *session, RTPHeader *header )
header->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
}
void send_rtcp_report(RTCPSession* session, Messenger* m, int32_t friendnumber)
void send_rtcp_report(RTCPSession* session, Messenger* m, uint32_t friendnumber)
{
if (session->last_expected_packets == 0)
return;
@ -538,7 +538,7 @@ void send_rtcp_report(RTCPSession* session, Messenger* m, int32_t friendnumber)
session->last_sent_report_ts = current_time_monotonic();
}
}
int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object )
int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object )
{
RTPSession *session = object;
RTPMessage *msg;
@ -574,7 +574,7 @@ int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data,
queue_message(session, msg);
return 0;
}
int handle_rtcp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object )
int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object )
{
if (length < 9)
return -1;

View File

@ -904,7 +904,7 @@ ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error)
goto END;
}
if (m_get_friend_connectionstatus(av->m, friend_number) != 1) {
if (m_get_friend_connectionstatus(av->m, friend_number) < 1) {
rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
goto END;
}

View File

@ -78,8 +78,8 @@ int id_closest(const uint8_t *id, const uint8_t *id1, const uint8_t *id2)
for (i = 0; i < CLIENT_ID_SIZE; ++i) {
distance1 = abs(((int8_t *)id)[i] ^ ((int8_t *)id1)[i]);
distance2 = abs(((int8_t *)id)[i] ^ ((int8_t *)id2)[i]);
distance1 = id[i] ^ id1[i];
distance2 = id[i] ^ id2[i];
if (distance1 < distance2)
return 1;

View File

@ -1,7 +1,8 @@
lib_LTLIBRARIES += libtoxcore.la
libtoxcore_la_include_HEADERS = \
../toxcore/tox.h
../toxcore/tox.h \
../toxcore/tox_old.h
libtoxcore_la_includedir = $(includedir)/tox
@ -45,6 +46,8 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \
../toxcore/TCP_client.c \
../toxcore/TCP_server.h \
../toxcore/TCP_server.c \
../toxcore/TCP_connection.h \
../toxcore/TCP_connection.c \
../toxcore/list.c \
../toxcore/list.h \
../toxcore/misc_tools.h

File diff suppressed because it is too large Load Diff

View File

@ -33,28 +33,25 @@
#define MAX_NAME_LENGTH 128
/* TODO: this must depend on other variable. */
#define MAX_STATUSMESSAGE_LENGTH 1007
#define AVATAR_MAX_DATA_LENGTH 16384
#define AVATAR_HASH_LENGTH crypto_hash_sha256_BYTES
#define FRIEND_ADDRESS_SIZE (crypto_box_PUBLICKEYBYTES + sizeof(uint32_t) + sizeof(uint16_t))
/* NOTE: Packet ids below 17 must never be used. */
#define PACKET_ID_SHARE_RELAYS 17
enum {
MESSAGE_NORMAL,
MESSAGE_ACTION
};
/* NOTE: Packet ids below 20 must never be used. */
#define PACKET_ID_SHARE_RELAYS 23
#define PACKET_ID_ONLINE 24
#define PACKET_ID_OFFLINE 25
#define PACKET_ID_NICKNAME 48
#define PACKET_ID_STATUSMESSAGE 49
#define PACKET_ID_USERSTATUS 50
#define PACKET_ID_TYPING 51
#define PACKET_ID_AVATAR_INFO_REQ 52
#define PACKET_ID_AVATAR_INFO 53
#define PACKET_ID_AVATAR_DATA_CONTROL 54
#define PACKET_ID_AVATAR_DATA_START 55
#define PACKET_ID_AVATAR_DATA_PUSH 56
#define PACKET_ID_RECEIPT 63
#define PACKET_ID_MESSAGE 64
#define PACKET_ID_ACTION 65
#define PACKET_ID_ACTION (PACKET_ID_MESSAGE + MESSAGE_ACTION) /* 65 */
#define PACKET_ID_MSI 69
#define PACKET_ID_FILE_SENDREQUEST 80
#define PACKET_ID_FILE_CONTROL 81
@ -71,13 +68,22 @@
/* All packets starting with a byte in this range can be used for anything. */
#define PACKET_ID_LOSSLESS_RANGE_START 160
#define PACKET_ID_LOSSLESS_RANGE_SIZE 32
#define PACKET_LOSSY_AV_RESERVED 8 /* Number of lossy packet types at start of range reserved for A/V. */
typedef struct {
uint8_t ipv6enabled;
uint8_t udp_disabled;
TCP_Proxy_Info proxy_info;
uint16_t port_range[2];
} Messenger_Options;
struct Receipts {
uint32_t packet_num;
uint32_t msg_id;
struct Receipts *next;
};
/* Status definitions. */
enum {
NOFRIEND,
@ -95,25 +101,23 @@ enum {
FAERR_NOMESSAGE = -2,
FAERR_OWNKEY = -3,
FAERR_ALREADYSENT = -4,
FAERR_UNKNOWN = -5,
FAERR_BADCHECKSUM = -6,
FAERR_SETNEWNOSPAM = -7,
FAERR_NOMEM = -8
};
/* Default start timeout in seconds between friend requests. */
#define FRIENDREQUEST_TIMEOUT 5;
/* Interval between the sending of tcp relay information */
#define FRIEND_SHARE_RELAYS_INTERVAL (5 * 60)
/* Must be < MAX_CRYPTO_DATA_SIZE */
#define AVATAR_DATA_MAX_CHUNK_SIZE (MAX_CRYPTO_DATA_SIZE-1)
/* Per-friend data limit for avatar data requests */
#define AVATAR_DATA_TRANSFER_LIMIT (10*AVATAR_MAX_DATA_LENGTH)
#define AVATAR_DATA_TRANSFER_TIMEOUT (60) /* 164kB every 60 seconds is not a lot */
enum {
CONNECTION_NONE,
CONNECTION_TCP,
CONNECTION_UDP
};
/* USERSTATUS -
* Represents userstatuses someone can have.
@ -127,56 +131,33 @@ typedef enum {
}
USERSTATUS;
/* AVATAR_FORMAT -
* Data formats for user avatar images
*/
typedef enum {
AVATAR_FORMAT_NONE = 0,
AVATAR_FORMAT_PNG
}
AVATAR_FORMAT;
/* AVATAR_DATACONTROL
* To control avatar data requests (PACKET_ID_AVATAR_DATA_CONTROL)
*/
typedef enum {
AVATAR_DATACONTROL_REQ,
AVATAR_DATACONTROL_ERROR
}
AVATAR_DATACONTROL;
typedef struct {
uint8_t started;
AVATAR_FORMAT format;
uint8_t hash[AVATAR_HASH_LENGTH];
uint32_t total_length;
uint32_t bytes_received;
uint8_t data[AVATAR_MAX_DATA_LENGTH];
}
AVATAR_RECEIVEDATA;
typedef struct {
/* Fields only used to limit the network usage from a given friend */
uint32_t bytes_sent; /* Total bytes send to this user */
uint64_t last_reset; /* Time the data counter was last reset */
}
AVATAR_SENDDATA;
#define FILE_ID_LENGTH 32
struct File_Transfers {
uint64_t size;
uint64_t transferred;
uint8_t status; /* 0 == no transfer, 1 = not accepted, 2 = paused by the other, 3 = transferring, 4 = broken, 5 = paused by us */
uint8_t status; /* 0 == no transfer, 1 = not accepted, 3 = transferring, 4 = broken, 5 = finished */
uint8_t paused; /* 0: not paused, 1 = paused by us, 2 = paused by other, 3 = paused by both. */
uint32_t last_packet_number; /* number of the last packet sent. */
uint64_t requested; /* total data requested by the request chunk callback */
unsigned int slots_allocated; /* number of slots allocated to this transfer. */
uint8_t id[FILE_ID_LENGTH];
};
enum {
FILESTATUS_NONE,
FILESTATUS_NOT_ACCEPTED,
FILESTATUS_PAUSED_BY_OTHER,
FILESTATUS_TRANSFERRING,
FILESTATUS_BROKEN,
FILESTATUS_PAUSED_BY_US,
FILESTATUS_TEMPORARY
//FILESTATUS_BROKEN,
FILESTATUS_FINISHED
};
enum {
FILE_PAUSE_NOT,
FILE_PAUSE_US,
FILE_PAUSE_OTHER,
FILE_PAUSE_BOTH
};
/* This cannot be bigger than 256 */
#define MAX_CONCURRENT_FILE_PIPES 256
@ -184,10 +165,15 @@ enum {
FILECONTROL_ACCEPT,
FILECONTROL_PAUSE,
FILECONTROL_KILL,
FILECONTROL_FINISHED,
FILECONTROL_RESUME_BROKEN
FILECONTROL_SEEK
};
enum {
FILEKIND_DATA,
FILEKIND_AVATAR
};
typedef struct Messenger Messenger;
typedef struct {
@ -201,36 +187,31 @@ typedef struct {
uint8_t name[MAX_NAME_LENGTH];
uint16_t name_length;
uint8_t name_sent; // 0 if we didn't send our name to this friend 1 if we have.
uint8_t *statusmessage;
uint8_t statusmessage[MAX_STATUSMESSAGE_LENGTH];
uint16_t statusmessage_length;
uint8_t statusmessage_sent;
USERSTATUS userstatus;
uint8_t userstatus_sent;
uint8_t avatar_info_sent;
uint8_t user_istyping;
uint8_t user_istyping_sent;
uint8_t is_typing;
uint16_t info_size; // Length of the info.
uint32_t message_id; // a semi-unique id used in read receipts.
uint8_t receives_read_receipts; // shall we send read receipts to this person?
uint32_t friendrequest_nospam; // The nospam number used in the friend request.
uint64_t ping_lastrecv;//TODO remove
uint64_t last_seen_time;
uint64_t share_relays_lastsent;
uint8_t last_connection_udp_tcp;
struct File_Transfers file_sending[MAX_CONCURRENT_FILE_PIPES];
unsigned int num_sending_files;
struct File_Transfers file_receiving[MAX_CONCURRENT_FILE_PIPES];
AVATAR_SENDDATA avatar_send_data;
AVATAR_RECEIVEDATA *avatar_recv_data; // We are receiving avatar data from this friend.
struct {
int (*function)(Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t len, void *object);
int (*function)(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t len, void *object);
void *object;
} lossy_packethandlers[PACKET_ID_LOSSY_RANGE_SIZE];
} lossy_rtp_packethandlers[PACKET_LOSSY_AV_RESERVED];
struct {
int (*function)(Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t len, void *object);
void *object;
} lossless_packethandlers[PACKET_ID_LOSSLESS_RANGE_SIZE];
struct Receipts *receipts_start;
struct Receipts *receipts_end;
} Friend;
@ -255,11 +236,6 @@ struct Messenger {
USERSTATUS userstatus;
AVATAR_FORMAT avatar_format;
uint8_t *avatar_data;
uint32_t avatar_data_length;
uint8_t avatar_hash[AVATAR_HASH_LENGTH];
Friend *friendlist;
uint32_t numfriends;
@ -271,45 +247,49 @@ struct Messenger {
uint8_t has_added_relays; // If the first connection has occurred in do_messenger
Node_format loaded_relays[NUM_SAVED_TCP_RELAYS]; // Relays loaded from config
void (*friend_message)(struct Messenger *m, int32_t, const uint8_t *, uint16_t, void *);
void (*friend_message)(struct Messenger *m, uint32_t, unsigned int, const uint8_t *, size_t, void *);
void *friend_message_userdata;
void (*friend_action)(struct Messenger *m, int32_t, const uint8_t *, uint16_t, void *);
void *friend_action_userdata;
void (*friend_namechange)(struct Messenger *m, int32_t, const uint8_t *, uint16_t, void *);
void (*friend_namechange)(struct Messenger *m, uint32_t, const uint8_t *, size_t, void *);
void *friend_namechange_userdata;
void (*friend_statusmessagechange)(struct Messenger *m, int32_t, const uint8_t *, uint16_t, void *);
void (*friend_statusmessagechange)(struct Messenger *m, uint32_t, const uint8_t *, size_t, void *);
void *friend_statusmessagechange_userdata;
void (*friend_userstatuschange)(struct Messenger *m, int32_t, uint8_t, void *);
void (*friend_userstatuschange)(struct Messenger *m, uint32_t, unsigned int, void *);
void *friend_userstatuschange_userdata;
void (*friend_typingchange)(struct Messenger *m, int32_t, uint8_t, void *);
void (*friend_typingchange)(struct Messenger *m, uint32_t, _Bool, void *);
void *friend_typingchange_userdata;
void (*read_receipt)(struct Messenger *m, int32_t, uint32_t, void *);
void (*read_receipt)(struct Messenger *m, uint32_t, uint32_t, void *);
void *read_receipt_userdata;
void (*friend_statuschange)(struct Messenger *m, int32_t, uint8_t, void *);
void *friend_statuschange_userdata;
void (*friend_connectionstatuschange)(struct Messenger *m, int32_t, uint8_t, void *);
void (*friend_connectionstatuschange)(struct Messenger *m, uint32_t, unsigned int, void *);
void *friend_connectionstatuschange_userdata;
void (*friend_connectionstatuschange_internal)(struct Messenger *m, int32_t, uint8_t, void *);
void (*friend_connectionstatuschange_internal)(struct Messenger *m, uint32_t, uint8_t, void *);
void *friend_connectionstatuschange_internal_userdata;
void *avatar_info_recv_userdata;
void (*avatar_info_recv)(struct Messenger *m, int32_t, uint8_t, uint8_t *, void *);
void *avatar_data_recv_userdata;
void (*avatar_data_recv)(struct Messenger *m, int32_t, uint8_t, uint8_t *, uint8_t *, uint32_t, void *);
void *group_chat_object; /* Set by new_groupchats()*/
void (*group_invite)(struct Messenger *m, int32_t, const uint8_t *, uint16_t);
void (*group_message)(struct Messenger *m, int32_t, const uint8_t *, uint16_t);
void (*group_invite)(struct Messenger *m, uint32_t, const uint8_t *, uint16_t);
void (*group_message)(struct Messenger *m, uint32_t, const uint8_t *, uint16_t);
void (*file_sendrequest)(struct Messenger *m, int32_t, uint8_t, uint64_t, const uint8_t *, uint16_t, void *);
void (*file_sendrequest)(struct Messenger *m, uint32_t, uint32_t, uint32_t, uint64_t, const uint8_t *, size_t,
void *);
void *file_sendrequest_userdata;
void (*file_filecontrol)(struct Messenger *m, int32_t, uint8_t, uint8_t, uint8_t, const uint8_t *, uint16_t, void *);
void (*file_filecontrol)(struct Messenger *m, uint32_t, uint32_t, unsigned int, void *);
void *file_filecontrol_userdata;
void (*file_filedata)(struct Messenger *m, int32_t, uint8_t, const uint8_t *, uint16_t length, void *);
void (*file_filedata)(struct Messenger *m, uint32_t, uint32_t, uint64_t, const uint8_t *, size_t, void *);
void *file_filedata_userdata;
void (*file_reqchunk)(struct Messenger *m, uint32_t, uint32_t, uint64_t, size_t, void *);
void *file_reqchunk_userdata;
void (*msi_packet)(struct Messenger *m, int32_t, const uint8_t *, uint16_t, void *);
void (*msi_packet)(struct Messenger *m, uint32_t, const uint8_t *, uint16_t, void *);
void *msi_packet_userdata;
void (*lossy_packethandler)(struct Messenger *m, uint32_t, const uint8_t *, size_t, void *);
void *lossy_packethandler_userdata;
void (*lossless_packethandler)(struct Messenger *m, uint32_t, const uint8_t *, size_t, void *);
void *lossless_packethandler_userdata;
void (*core_connection_change)(struct Messenger *m, unsigned int, void *);
void *core_connection_change_userdata;
unsigned int last_connection_status;
Messenger_Options options;
};
@ -329,7 +309,6 @@ void getaddress(const Messenger *m, uint8_t *address);
* return -2 if no message (message length must be >= 1 byte).
* return -3 if user's own key.
* return -4 if friend request already sent or already a friend.
* return -5 for unknown error.
* return -6 if bad checksum in address.
* return -7 if the friend was already there but the nospam was different.
* (the nospam for that friend was set to the new one).
@ -340,7 +319,10 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u
/* Add a friend without sending a friendrequest.
* return the friend number if success.
* return -1 if failure.
* return -3 if user's own key.
* return -4 if friend request already sent or already a friend.
* return -6 if bad checksum in address.
* return -8 if increasing the friend list size fails.
*/
int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk);
@ -371,8 +353,9 @@ int m_delfriend(Messenger *m, int32_t friendnumber);
/* Checks friend's connecting status.
*
* return 1 if friend is connected to us (Online).
* return 0 if friend is not connected to us (Offline).
* return CONNECTION_UDP (2) if friend is directly connected to us (Online UDP).
* return CONNECTION_TCP (1) if friend is connected to us (Online TCP).
* return CONNECTION_NONE (0) if friend is not connected to us (Offline).
* return -1 on failure.
*/
int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber);
@ -384,33 +367,20 @@ int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber);
*/
int m_friend_exists(const Messenger *m, int32_t friendnumber);
/* Send a text chat message to an online friend.
/* Send a message of type to an online friend.
*
* return the message id if packet was successfully put into the send queue.
* return 0 if it was not.
* return -1 if friend not valid.
* return -2 if too large.
* return -3 if friend not online.
* return -4 if send failed (because queue is full).
* return -5 if bad type.
* return 0 if success.
*
* You will want to retain the return value, it will be passed to your read_receipt callback
* if one is received.
* 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.
* the value in message_id will be passed to your read_receipt callback when the other receives the message.
*/
uint32_t m_sendmessage(Messenger *m, int32_t friendnumber, const uint8_t *message, uint32_t length);
uint32_t m_sendmessage_withid(Messenger *m, int32_t friendnumber, uint32_t theid, const uint8_t *message,
uint32_t length);
int m_send_message_generic(Messenger *m, int32_t friendnumber, uint8_t type, const uint8_t *message, uint32_t length,
uint32_t *message_id);
/* Send an action to an online friend.
*
* return the message id if packet was successfully put into the send queue.
* return 0 if it was not.
*
* You will want to retain the return value, it will be passed to your read_receipt callback
* if one is received.
* 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 m_sendaction(Messenger *m, int32_t friendnumber, const uint8_t *action, uint32_t length);
uint32_t m_sendaction_withid(const Messenger *m, int32_t friendnumber, uint32_t theid, const uint8_t *action,
uint32_t length);
/* Set the name and name_length of a friend.
* name must be a string of maximum MAX_NAME_LENGTH length.
@ -479,7 +449,7 @@ int m_get_self_statusmessage_size(const Messenger *m);
* retruns -1 on failure.
*/
int m_copy_statusmessage(const Messenger *m, int32_t friendnumber, uint8_t *buf, uint32_t maxlen);
int m_copy_self_statusmessage(const Messenger *m, uint8_t *buf, uint32_t maxlen);
int m_copy_self_statusmessage(const Messenger *m, uint8_t *buf);
/* return one of USERSTATUS values.
* Values unknown to your application should be represented as USERSTATUS_NONE.
@ -490,115 +460,8 @@ uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber);
uint8_t m_get_self_userstatus(const Messenger *m);
/* Set the user avatar image data.
* This should be made before connecting, so we will not announce that the user have no avatar
* before setting and announcing a new one, forcing the peers to re-download it.
*
* Notice that the library treats the image as raw data and does not interpret it by any way.
*
* Arguments:
* format - Avatar image format or NONE for user with no avatar (see AVATAR_FORMAT);
* data - pointer to the avatar data (may be NULL it the format is NONE);
* length - length of image data. Must be <= MAX_AVATAR_DATA_LENGTH.
*
* returns 0 on success
* returns -1 on failure.
*/
int m_set_avatar(Messenger *m, uint8_t format, const uint8_t *data, uint32_t length);
/* Unsets the user avatar.
returns 0 on success (currently always returns 0) */
int m_unset_avatar(Messenger *m);
/* Get avatar data from the current user.
* Copies the current user avatar data to the destination buffer and sets the image format
* accordingly.
*
* If the avatar format is NONE, the buffer 'buf' isleft uninitialized, 'hash' is zeroed, and
* 'length' is set to zero.
*
* If any of the pointers format, buf, length, and hash are NULL, that particular field will be ignored.
*
* Arguments:
* format - destination pointer to the avatar image format (see AVATAR_FORMAT);
* buf - destination buffer to the image data. Must have at least 'maxlen' bytes;
* length - destination pointer to the image data length;
* maxlen - length of the destination buffer 'buf';
* hash - destination pointer to the avatar hash (it must be exactly AVATAR_HASH_LENGTH bytes long).
*
* returns 0 on success;
* returns -1 on failure.
*
*/
int m_get_self_avatar(const Messenger *m, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen,
uint8_t *hash);
/* Generates a cryptographic hash of the given data.
* This function may be used by clients for any purpose, but is provided primarily for
* validating cached avatars.
* This function is a wrapper to internal message-digest functions.
*
* Arguments:
* hash - destination buffer for the hash data, it must be exactly crypto_hash_sha256_BYTES bytes long.
* data - data to be hashed;
* datalen - length of the data;
*
* returns 0 on success
* returns -1 on failure.
*/
int m_hash(uint8_t *hash, const uint8_t *data, const uint32_t datalen);
/* Generates a cryptographic hash of the given avatar data.
* This function is a wrapper to m_hash and specifically provided
* to generate hashes from user avatars that may be memcmp()ed with the values returned by the
* other avatar functions. It is specially important to validate cached avatars.
*
* Arguments:
* hash - destination buffer for the hash data, it must be exactly AVATAR_HASH_LENGTH bytes long.
* data - avatar image data;
* datalen - length of the avatar image data; it must be <= MAX_AVATAR_DATA_LENGTH.
*
* returns 0 on success
* returns -1 on failure.
*/
int m_avatar_hash(uint8_t *hash, const uint8_t *data, const uint32_t datalen);
/* Request avatar information from a friend.
* Asks a friend to provide their avatar information (image format and hash). The friend may
* or may not answer this request and, if answered, the information will be provided through
* the callback 'avatar_info'.
*
* returns 0 on success
* returns -1 on failure.
*/
int m_request_avatar_info(const Messenger *m, const int32_t friendnumber);
/* Send an unrequested avatar information to a friend.
* Sends our avatar format and hash to a friend; he/she can use this information to validate
* an avatar from the cache and may (or not) reply with an avatar data request.
*
* Notice: it is NOT necessary to send these notification after changing the avatar or
* connecting. The library already does this.
*
* returns 0 on success
* returns -1 on failure.
*/
int m_send_avatar_info(const Messenger *m, const int32_t friendnumber);
/* Request the avatar data from a friend.
* Ask a friend to send their avatar data. The friend may or may not answer this request and,
* if answered, the information will be provided in callback 'avatar_data'.
*
* returns 0 on sucess
* returns -1 on failure.
*/
int m_request_avatar_data(const Messenger *m, const int32_t friendnumber);
/* returns timestamp of last time friendnumber was seen online, or 0 if never seen.
* returns -1 on error.
/* returns timestamp of last time friendnumber was seen online or 0 if never seen.
* if friendnumber is invalid this function will return UINT64_MAX.
*/
uint64_t m_get_last_online(const Messenger *m, int32_t friendnumber);
@ -615,58 +478,48 @@ int m_set_usertyping(Messenger *m, int32_t friendnumber, uint8_t is_typing);
* returns 0 if friend is not typing.
* returns 1 if friend is typing.
*/
uint8_t m_get_istyping(const Messenger *m, int32_t friendnumber);
/* Sets whether we send read receipts for friendnumber.
* This function is not lazy, and it will fail if yesno is not (0 or 1).
*/
void m_set_sends_receipts(Messenger *m, int32_t friendnumber, int yesno);
int m_get_istyping(const Messenger *m, int32_t friendnumber);
/* 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)
* Function format is function(uint8_t * public_key, uint8_t * data, size_t length)
*/
void m_callback_friendrequest(Messenger *m, void (*function)(Messenger *m, const uint8_t *, const uint8_t *, uint16_t,
void m_callback_friendrequest(Messenger *m, void (*function)(Messenger *m, const uint8_t *, const uint8_t *, size_t,
void *), void *userdata);
/* Set the function that will be executed when a message from a friend is received.
* Function format is: function(int32_t friendnumber, uint8_t * message, uint32_t length)
* Function format is: function(uint32_t friendnumber, unsigned int type, uint8_t * message, uint32_t length)
*/
void m_callback_friendmessage(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t, void *),
void *userdata);
/* Set the function that will be executed when an action from a friend is received.
* Function format is: function(int32_t friendnumber, uint8_t * action, uint32_t length)
*/
void m_callback_action(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t, void *),
void *userdata);
void m_callback_friendmessage(Messenger *m, void (*function)(Messenger *m, uint32_t, unsigned int, const uint8_t *,
size_t, void *), void *userdata);
/* Set the callback for name changes.
* Function(int32_t friendnumber, uint8_t *newname, uint16_t length)
* Function(uint32_t friendnumber, uint8_t *newname, size_t length)
* You are not responsible for freeing newname.
*/
void m_callback_namechange(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t, void *),
void m_callback_namechange(Messenger *m, void (*function)(Messenger *m, uint32_t, const uint8_t *, size_t, void *),
void *userdata);
/* Set the callback for status message changes.
* Function(int32_t friendnumber, uint8_t *newstatus, uint16_t length)
* Function(uint32_t friendnumber, uint8_t *newstatus, size_t length)
*
* You are not responsible for freeing newstatus
*/
void m_callback_statusmessage(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t, void *),
void m_callback_statusmessage(Messenger *m, void (*function)(Messenger *m, uint32_t, const uint8_t *, size_t, void *),
void *userdata);
/* Set the callback for status type changes.
* Function(int32_t friendnumber, USERSTATUS kind)
* Function(uint32_t friendnumber, USERSTATUS kind)
*/
void m_callback_userstatus(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, void *), void *userdata);
void m_callback_userstatus(Messenger *m, void (*function)(Messenger *m, uint32_t, unsigned int, void *),
void *userdata);
/* Set the callback for typing changes.
* Function(int32_t friendnumber, uint8_t is_typing)
* Function(uint32_t friendnumber, uint8_t is_typing)
*/
void m_callback_typingchange(Messenger *m, void(*function)(Messenger *m, int32_t, uint8_t, void *), void *userdata);
void m_callback_typingchange(Messenger *m, void(*function)(Messenger *m, uint32_t, _Bool, void *), void *userdata);
/* Set the callback for read receipts.
* Function(int32_t friendnumber, uint32_t receipt)
* Function(uint32_t friendnumber, uint32_t receipt)
*
* If you are keeping a record of returns from m_sendmessage,
* receipt might be one of those values, meaning the message
@ -674,10 +527,10 @@ void m_callback_typingchange(Messenger *m, void(*function)(Messenger *m, int32_t
* Since core doesn't track ids for you, receipt may not correspond to any message.
* In that case, you should discard it.
*/
void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, int32_t, uint32_t, void *), void *userdata);
void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, void *), void *userdata);
/* Set the callback for connection status changes.
* function(int32_t friendnumber, uint8_t status)
* function(uint32_t friendnumber, uint8_t status)
*
* Status:
* 0 -- friend went offline after being previously online.
@ -687,62 +540,25 @@ void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, int32_
* being previously online" part.
* It's assumed that when adding friends, their connection status is offline.
*/
void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, void *),
void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, uint32_t, unsigned int, void *),
void *userdata);
/* Same as previous but for internal A/V core usage only */
void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, void *),
void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Messenger *m, uint32_t, uint8_t, void *),
void *userdata);
/* Set the callback function for avatar information.
* This callback will be called when avatar information are received from friends. These events
* can arrive at anytime, but are usually received uppon connection and in reply of avatar
* information requests.
*
* Function format is:
* function(Tox *tox, int32_t friendnumber, uint8_t format, uint8_t *hash, void *userdata)
*
* where 'format' is the avatar image format (see AVATAR_FORMAT) and 'hash' is the hash of
* the avatar data for caching purposes and it is exactly AVATAR_HASH_LENGTH long. If the
* image format is NONE, the hash is zeroed.
*
/* Set the callback for typing changes.
* Function(unsigned int connection_status (0 = not connected, 1 = TCP only, 2 = UDP + TCP))
*/
void m_callback_avatar_info(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t *, void *),
void *userdata);
/* Set the callback function for avatar data.
* This callback will be called when the complete avatar data was correctly received from a
* friend. This only happens in reply of a avatar data request (see tox_request_avatar_data);
*
* Function format is:
* function(Tox *tox, int32_t friendnumber, uint8_t format, uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata)
*
* where 'format' is the avatar image format (see AVATAR_FORMAT); 'hash' is the
* locally-calculated cryptographic hash of the avatar data and it is exactly
* AVATAR_HASH_LENGTH long; 'data' is the avatar image data and 'datalen' is the length
* of such data.
*
* If format is NONE, 'data' is NULL, 'datalen' is zero, and the hash is zeroed. The hash is
* always validated locally with the function tox_avatar_hash and ensured to match the image
* data, so this value can be safely used to compare with cached avatars.
*
* WARNING: users MUST treat all avatar image data received from another peer as untrusted and
* potentially malicious. The library only ensures that the data which arrived is the same the
* other user sent, and does not interpret or validate any image data.
*/
void m_callback_avatar_data(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t *, uint8_t *,
uint32_t, void *), void *userdata);
void m_callback_core_connection(Messenger *m, void (*function)(Messenger *m, unsigned int, void *), void *userdata);
/**********GROUP CHATS************/
/* Set the callback for group invites.
*
* Function(Messenger *m, int32_t friendnumber, uint8_t *data, uint16_t length)
* Function(Messenger *m, uint32_t friendnumber, uint8_t *data, uint16_t length)
*/
void m_callback_group_invite(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t));
void m_callback_group_invite(Messenger *m, void (*function)(Messenger *m, uint32_t, const uint8_t *, uint16_t));
/* Send a group invite packet.
*
@ -756,58 +572,97 @@ int send_group_invite_packet(const Messenger *m, int32_t friendnumber, const uin
/* Set the callback for file send requests.
*
* Function(Tox *tox, int32_t friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length, void *userdata)
* Function(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint32_t filetype, uint64_t filesize, uint8_t *filename, size_t filename_length, void *userdata)
*/
void callback_file_sendrequest(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint64_t, const uint8_t *,
uint16_t, void *), void *userdata);
void callback_file_sendrequest(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, uint32_t, uint64_t,
const uint8_t *, size_t, void *), void *userdata);
/* Set the callback for file control requests.
*
* Function(Tox *tox, int32_t friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t control_type, uint8_t *data, uint16_t length, void *userdata)
* Function(Tox *tox, uint32_t friendnumber, uint32_t filenumber, unsigned int control_type, void *userdata)
*
*/
void callback_file_control(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t, uint8_t,
const uint8_t *, uint16_t, void *), void *userdata);
void callback_file_control(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, unsigned int, void *),
void *userdata);
/* Set the callback for file data.
*
* Function(Tox *tox, int32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata)
* Function(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, uint8_t *data, size_t length, void *userdata)
*
*/
void callback_file_data(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, const uint8_t *, uint16_t length,
void *), void *userdata);
void callback_file_data(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, uint64_t, const uint8_t *,
size_t, void *), void *userdata);
/* Send a file send request.
* Maximum filename length is 255 bytes.
* return 1 on success
* return 0 on failure
/* Set the callback for file request chunk.
*
* Function(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, void *userdata)
*
*/
int file_sendrequest(const Messenger *m, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
const uint8_t *filename, uint16_t filename_length);
void callback_file_reqchunk(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, uint64_t, size_t, void *),
void *userdata);
/* Copy the file transfer file id to file_id
*
* return 0 on success.
* return -1 if friend not valid.
* return -2 if filenumber not valid
*/
int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint8_t *file_id);
/* Send a file send request.
* Maximum filename length is 255 bytes.
* return file number on success
* return -1 on failure
* return -1 if friend not found.
* return -2 if filename length invalid.
* return -3 if no more file sending slots left.
* return -4 if could not send packet (friend offline).
*
*/
int new_filesender(const Messenger *m, int32_t friendnumber, uint64_t filesize, const uint8_t *filename,
uint16_t filename_length);
long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_type, uint64_t filesize,
const uint8_t *file_id, const 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 sending file, 1 if it targets a receiving file.
*
* return 1 on success
* return 0 on failure
* return 0 on success
* return -1 if friend not valid.
* return -2 if friend not online.
* return -3 if file number invalid.
* return -4 if file control is bad.
* return -5 if file already paused.
* return -6 if resume file failed because it was only paused by the other.
* return -7 if resume file failed because it wasn't paused.
* return -8 if packet failed to send.
*/
int file_control(const Messenger *m, int32_t friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t message_id,
const uint8_t *data, uint16_t length);
int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, unsigned int control);
/* Send a seek file control request.
*
* return 0 on success
* return -1 if friend not valid.
* return -2 if friend not online.
* return -3 if file number invalid.
* return -4 if not receiving file.
* return -5 if file status wrong.
* return -6 if position bad.
* return -8 if packet failed to send.
*/
int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position);
/* Send file data.
*
* return 1 on success
* return 0 on failure
* return 0 on success
* return -1 if friend not valid.
* return -2 if friend not online.
* return -3 if filenumber invalid.
* return -4 if file transfer not transferring.
* return -5 if bad data size.
* return -6 if packet queue full.
* return -7 if wrong position.
*/
int file_data(const Messenger *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length);
int file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
uint16_t length);
/* Give the number of bytes left to be sent/received.
*
@ -822,9 +677,9 @@ uint64_t file_dataremaining(const Messenger *m, int32_t friendnumber, uint8_t fi
/* Set the callback for msi packets.
*
* Function(Messenger *m, int32_t friendnumber, uint8_t *data, uint16_t length, void *userdata)
* Function(Messenger *m, uint32_t friendnumber, uint8_t *data, uint16_t length, void *userdata)
*/
void m_callback_msi_packet(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t, void *),
void m_callback_msi_packet(Messenger *m, void (*function)(Messenger *m, uint32_t, const uint8_t *, uint16_t, void *),
void *userdata);
/* Send an msi packet.
@ -834,20 +689,29 @@ void m_callback_msi_packet(Messenger *m, void (*function)(Messenger *m, int32_t,
*/
int m_msi_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length);
/**********************************************/
/* Set handlers for custom lossy packets (RTP packets for example.)
/* Set handlers for lossy rtp packets.
*
* return -1 on failure.
* return 0 on success.
*/
int custom_lossy_packet_registerhandler(Messenger *m, int32_t friendnumber, uint8_t byte,
int (*packet_handler_callback)(Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t len, void *object),
void *object);
int m_callback_rtp_packet(Messenger *m, int32_t friendnumber, uint8_t byte, int (*packet_handler_callback)(Messenger *m,
uint32_t friendnumber, const uint8_t *data, uint16_t len, void *object), void *object);
/**********************************************/
/* Set handlers for custom lossy packets.
*
*/
void custom_lossy_packet_registerhandler(Messenger *m, void (*packet_handler_callback)(Messenger *m,
uint32_t friendnumber, const uint8_t *data, size_t len, void *object), void *object);
/* High level function to send custom lossy packets.
*
* return -1 on failure.
* return -1 if friend invalid.
* return -2 if length wrong.
* return -3 if first byte invalid.
* return -4 if friend offline.
* return -5 if packet failed to send because of other error.
* return 0 on success.
*/
int send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length);
@ -855,28 +719,36 @@ int send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const uin
/* Set handlers for custom lossless packets.
*
* byte must be in PACKET_ID_LOSSLESS_RANGE_START PACKET_ID_LOSSLESS_RANGE_SIZE range.
*
* return -1 on failure.
* return 0 on success.
*/
int custom_lossless_packet_registerhandler(Messenger *m, int32_t friendnumber, uint8_t byte,
int (*packet_handler_callback)(Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t len, void *object),
void *object);
void custom_lossless_packet_registerhandler(Messenger *m, void (*packet_handler_callback)(Messenger *m,
uint32_t friendnumber, const uint8_t *data, size_t len, void *object), void *object);
/* High level function to send custom lossless packets.
*
* return -1 on failure.
* return -1 if friend invalid.
* return -2 if length wrong.
* return -3 if first byte invalid.
* return -4 if friend offline.
* return -5 if packet failed to send because of other error.
* return 0 on success.
*/
int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length);
/**********************************************/
enum {
MESSENGER_ERROR_NONE,
MESSENGER_ERROR_PORT,
MESSENGER_ERROR_OTHER
};
/* Run this at startup.
* return allocated instance of Messenger on success.
* return 0 if there are problems.
*
* if error is not NULL it will be set to one of the values in the enum above.
*/
Messenger *new_messenger(Messenger_Options *options);
Messenger *new_messenger(Messenger_Options *options, unsigned int *error);
/* Run this before closing shop
* Free all datastructures.
@ -891,7 +763,7 @@ void do_messenger(Messenger *m);
*
* returns time (in ms) before the next do_messenger() needs to be run on success.
*/
uint32_t messenger_run_interval(Messenger *m);
uint32_t messenger_run_interval(const Messenger *m);
/* SAVING AND LOADING FUNCTIONS: */
@ -917,6 +789,6 @@ uint32_t get_num_online_friends(const Messenger *m);
* 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_friendlist(const Messenger *m, int32_t *out_list, uint32_t list_size);
uint32_t copy_friendlist(const Messenger *m, uint32_t *out_list, uint32_t list_size);
#endif

View File

@ -346,6 +346,18 @@ static _Bool add_priority(TCP_Client_Connection *con, const uint8_t *packet, uin
return 1;
}
static void wipe_priority_list(TCP_Client_Connection *con)
{
TCP_Priority_List *p = con->priority_queue_start;
while (p) {
TCP_Priority_List *pp = p;
p = p->next;
free(pp);
}
}
/* return 1 on success.
* return 0 if could not send packet.
* return -1 on failure (connection must be killed).
@ -948,6 +960,7 @@ void kill_TCP_connection(TCP_Client_Connection *TCP_connection)
if (TCP_connection == NULL)
return;
wipe_priority_list(TCP_connection);
kill_sock(TCP_connection->sock);
memset(TCP_connection, 0, sizeof(TCP_Client_Connection));
free(TCP_connection);

View File

@ -78,8 +78,10 @@ typedef struct {
uint64_t ping_response_id;
uint64_t ping_request_id;
//TODO: remove
void *net_crypto_pointer;
uint32_t net_crypto_location;
struct {
uint8_t status; /* 0 if not used, 1 if other is offline, 2 if other is online. */
uint8_t public_key[crypto_box_PUBLICKEYBYTES];
@ -96,6 +98,10 @@ typedef struct {
int (*onion_callback)(void *object, const uint8_t *data, uint16_t length);
void *onion_callback_object;
/* Can be used by user. */
void *custom_object;
uint32_t custom_uint;
} TCP_Client_Connection;
/* Create new TCP connection to ip_port/public_key

968
toxcore/TCP_connection.c Normal file
View File

@ -0,0 +1,968 @@
/* TCP_connection.c
*
* Handles TCP relay connections between two Tox clients.
*
* Copyright (C) 2015 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "TCP_connection.h"
/* Set the size of the array to num.
*
* return -1 if realloc fails.
* return 0 if it succeeds.
*/
#define realloc_tox_array(array, element_size, num, temp_pointer) (num ? (temp_pointer = realloc(array, ((num) * (element_size))), temp_pointer ? (array = temp_pointer, 0) : (-1) ) : (free(array), array = NULL, 0))
/* return 1 if the connections_number is not valid.
* return 0 if the connections_number is valid.
*/
static _Bool connections_number_not_valid(const TCP_Connections *tcp_c, int connections_number)
{
if ((unsigned int)connections_number >= tcp_c->connections_length)
return 1;
if (tcp_c->connections == NULL)
return 1;
if (tcp_c->connections[connections_number].status == TCP_CONN_NONE)
return 1;
return 0;
}
/* return 1 if the tcp_connections_number is not valid.
* return 0 if the tcp_connections_number is valid.
*/
static _Bool tcp_connections_number_not_valid(const TCP_Connections *tcp_c, int tcp_connections_number)
{
if ((unsigned int)tcp_connections_number >= tcp_c->tcp_connections_length)
return 1;
if (tcp_c->tcp_connections == NULL)
return 1;
if (tcp_c->tcp_connections[tcp_connections_number].status == TCP_CONN_NONE)
return 1;
return 0;
}
/* Create a new empty connection.
*
* return -1 on failure.
* return connections_number on success.
*/
static int create_connection(TCP_Connections *tcp_c)
{
uint32_t i;
for (i = 0; i < tcp_c->connections_length; ++i) {
if (tcp_c->connections[i].status == TCP_CONN_NONE)
return i;
}
int id = -1;
TCP_Connection_to *temp_pointer;
if (realloc_tox_array(tcp_c->connections, sizeof(TCP_Connection_to), tcp_c->connections_length + 1,
temp_pointer) == 0) {
id = tcp_c->connections_length;
++tcp_c->connections_length;
memset(&(tcp_c->connections[id]), 0, sizeof(TCP_Connection_to));
}
return id;
}
/* Create a new empty tcp connection.
*
* return -1 on failure.
* return tcp_connections_number on success.
*/
static int create_tcp_connection(TCP_Connections *tcp_c)
{
uint32_t i;
for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
if (tcp_c->tcp_connections[i].status == TCP_CONN_NONE)
return i;
}
int id = -1;
TCP_con *temp_pointer;
if (realloc_tox_array(tcp_c->tcp_connections, sizeof(TCP_con), tcp_c->tcp_connections_length + 1, temp_pointer) == 0) {
id = tcp_c->tcp_connections_length;
++tcp_c->tcp_connections_length;
memset(&(tcp_c->tcp_connections[id]), 0, sizeof(TCP_con));
}
return id;
}
/* Wipe a connection.
*
* return -1 on failure.
* return 0 on success.
*/
static int wipe_connection(TCP_Connections *tcp_c, int connections_number)
{
if (connections_number_not_valid(tcp_c, connections_number))
return -1;
uint32_t i;
memset(&(tcp_c->connections[connections_number]), 0 , sizeof(TCP_Connection_to));
for (i = tcp_c->connections_length; i != 0; --i) {
if (tcp_c->connections[i - 1].status != TCP_CONN_NONE)
break;
}
if (tcp_c->connections_length != i) {
tcp_c->connections_length = i;
TCP_Connection_to *temp_pointer;
realloc_tox_array(tcp_c->connections, sizeof(TCP_Connection_to), tcp_c->connections_length, temp_pointer);
}
return 0;
}
/* Wipe a connection.
*
* return -1 on failure.
* return 0 on success.
*/
static int wipe_tcp_connection(TCP_Connections *tcp_c, int tcp_connections_number)
{
if (tcp_connections_number_not_valid(tcp_c, tcp_connections_number))
return -1;
uint32_t i;
memset(&(tcp_c->tcp_connections[tcp_connections_number]), 0 , sizeof(TCP_con));
for (i = tcp_c->tcp_connections_length; i != 0; --i) {
if (tcp_c->tcp_connections[i - 1].status != TCP_CONN_NONE)
break;
}
if (tcp_c->tcp_connections_length != i) {
tcp_c->tcp_connections_length = i;
TCP_con *temp_pointer;
realloc_tox_array(tcp_c->tcp_connections, sizeof(TCP_con), tcp_c->tcp_connections_length, temp_pointer);
}
return 0;
}
static TCP_Connection_to *get_connection(const TCP_Connections *tcp_c, int connections_number)
{
if (connections_number_not_valid(tcp_c, connections_number))
return 0;
return &tcp_c->connections[connections_number];
}
static TCP_con *get_tcp_connection(const TCP_Connections *tcp_c, int tcp_connections_number)
{
if (tcp_connections_number_not_valid(tcp_c, tcp_connections_number))
return 0;
return &tcp_c->tcp_connections[tcp_connections_number];
}
/* Send a packet to the TCP connection.
*
* return -1 on failure.
* return 0 on success.
*/
int send_packet_tcp_connection(TCP_Connections *tcp_c, int connections_number, const uint8_t *packet, uint16_t length)
{
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
if (!con_to) {
return -1;
}
//TODO: detect and kill bad relays.
//TODO: thread safety?
unsigned int i;
int ret = -1;
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
uint32_t tcp_con_num = con_to->connections[i].tcp_connection;
uint8_t status = con_to->connections[i].status;
uint8_t connection_id = con_to->connections[i].connection_id;
if (tcp_con_num && status == TCP_CONNECTIONS_STATUS_ONLINE) {
tcp_con_num -= 1;
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_con_num);
if (!tcp_con) {
continue;
}
ret = send_data(tcp_con->connection, connection_id, packet, length);
if (ret == 1) {
break;
}
}
}
if (ret == 1) {
return 0;
} else {
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
uint32_t tcp_con_num = con_to->connections[i].tcp_connection;
uint8_t status = con_to->connections[i].status;
uint8_t connection_id = con_to->connections[i].connection_id;
if (tcp_con_num && status == TCP_CONNECTIONS_STATUS_REGISTERED) {
tcp_con_num -= 1;
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_con_num);
if (!tcp_con) {
continue;
}
ret = send_oob_packet(tcp_con->connection, con_to->public_key, packet, length);
if (ret == 1) {
break;
}
}
}
if (ret == 1) {
return 0;
} else {
return -1;
}
}
}
/* Return a random TCP connection number for use in send_tcp_onion_request.
*
* TODO: This number is just the index of an array that the elements can
* change without warning.
*
* return TCP connection number on success.
* return -1 on failure.
*/
int get_random_tcp_conn_number(TCP_Connections *tcp_c)
{
unsigned int i, r = rand();
for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
if (tcp_c->tcp_connections[(i + r) % tcp_c->tcp_connections_length].status == TCP_CONN_CONNECTED) {
return ((i + r) % tcp_c->tcp_connections_length);
}
}
return -1;
}
/* Send an onion packet via the TCP relay corresponding to tcp_connections_number.
*
* return 0 on success.
* return -1 on failure.
*/
int tcp_send_onion_request(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *data,
uint16_t length)
{
if (tcp_connections_number >= tcp_c->tcp_connections_length) {
return -1;
}
if (tcp_c->tcp_connections[tcp_connections_number].status == TCP_CONN_CONNECTED) {
int ret = send_onion_request(tcp_c->tcp_connections[tcp_connections_number].connection, data, length);
if (ret == 1)
return 0;
}
return -1;
}
/* Send an oob packet via the TCP relay corresponding to tcp_connections_number.
*
* return 0 on success.
* return -1 on failure.
*/
int tcp_send_oob_packet(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *public_key,
const uint8_t *packet, uint16_t length)
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
if (!tcp_con)
return -1;
int ret = send_oob_packet(tcp_con->connection, public_key, packet, length);
if (ret == 1)
return 0;
return -1;
}
/* Set the callback for TCP data packets.
*/
void set_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_data_callback)(void *object, int id,
const uint8_t *data, uint16_t length), void *object)
{
tcp_c->tcp_data_callback = tcp_data_callback;
tcp_c->tcp_data_callback_object = object;
}
/* Set the callback for TCP onion packets.
*/
void set_oob_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_oob_callback)(void *object,
const uint8_t *public_key, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length), void *object)
{
tcp_c->tcp_oob_callback = tcp_oob_callback;
tcp_c->tcp_oob_callback_object = object;
}
/* Set the callback for TCP oob data packets.
*/
void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_onion_callback)(void *object,
const uint8_t *data, uint16_t length), void *object)
{
tcp_c->tcp_onion_callback = tcp_onion_callback;
tcp_c->tcp_onion_callback_object = object;
}
/* Find the TCP connection with public_key.
*
* return connections_number on success.
* return -1 on failure.
*/
static int find_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key)
{
unsigned int i;
for (i = 0; i < tcp_c->connections_length; ++i) {
TCP_Connection_to *con_to = get_connection(tcp_c, i);
if (con_to) {
if (memcmp(con_to->public_key, public_key, crypto_box_PUBLICKEYBYTES) == 0) {
return i;
}
}
}
return -1;
}
/* Find the TCP connection to a relay with relay_pk.
*
* return connections_number on success.
* return -1 on failure.
*/
static int find_tcp_connection_relay(TCP_Connections *tcp_c, const uint8_t *relay_pk)
{
unsigned int i;
for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
if (tcp_con) {
if (memcmp(tcp_con->connection->public_key, relay_pk, crypto_box_PUBLICKEYBYTES) == 0) {
return i;
}
}
}
return -1;
}
/* Create a new TCP connection to public_key.
*
* id is the id in the callbacks for that connection.
*
* return connections_number on success.
* return -1 on failure.
*/
int new_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key, int id)
{
if (find_tcp_connection_to(tcp_c, public_key) != -1)
return -1;
int connections_number = create_connection(tcp_c);
if (connections_number == -1)
return -1;
TCP_Connection_to *con_to = &tcp_c->connections[connections_number];
con_to->status = TCP_CONN_VALID;
memcpy(con_to->public_key, public_key, crypto_box_PUBLICKEYBYTES);
con_to->id = id;
return connections_number;
}
/* return 0 on success.
* return -1 on failure.
*/
int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number)
{
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
if (!con_to)
return -1;
unsigned int i;
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
if (con_to->connections[i].tcp_connection) {
unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1;
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
if (!tcp_con)
continue;
if (tcp_con->status == TCP_CONN_CONNECTED) {
send_disconnect_request(tcp_con->connection, con_to->connections[i].connection_id);
}
--tcp_con->lock_count;
}
}
return wipe_connection(tcp_c, connections_number);
}
static _Bool tcp_connection_in_conn(TCP_Connection_to *con_to, int tcp_connections_number)
{
unsigned int i;
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) {
return 1;
}
}
return 0;
}
/* return index on success.
* return -1 on failure.
*/
static int add_tcp_connection_to_conn(TCP_Connection_to *con_to, int tcp_connections_number)
{
unsigned int i;
if (tcp_connection_in_conn(con_to, tcp_connections_number))
return -1;
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
if (con_to->connections[i].tcp_connection == 0) {
con_to->connections[i].tcp_connection = tcp_connections_number + 1;
con_to->connections[i].status = TCP_CONNECTIONS_STATUS_NONE;
con_to->connections[i].connection_id = 0;
return i;
}
}
return -1;
}
/* return index on success.
* return -1 on failure.
*/
static int rm_tcp_connection_from_conn(TCP_Connection_to *con_to, int tcp_connections_number)
{
unsigned int i;
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) {
con_to->connections[i].tcp_connection = 0;
con_to->connections[i].status = TCP_CONNECTIONS_STATUS_NONE;
con_to->connections[i].connection_id = 0;
return i;
}
}
return -1;
}
/* return index on success.
* return -1 on failure.
*/
static int set_tcp_connection_status(TCP_Connection_to *con_to, int tcp_connections_number, unsigned int status,
uint8_t connection_id)
{
unsigned int i;
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) {
con_to->connections[i].status = status;
con_to->connections[i].connection_id = connection_id;
return i;
}
}
return -1;
}
/* Kill a TCP relay connection.
*
* return 0 on success.
* return -1 on failure.
*/
static int kill_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number)
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
if (!tcp_con)
return -1;
unsigned int i;
for (i = 0; i < tcp_c->connections_length; ++i) {
TCP_Connection_to *con_to = get_connection(tcp_c, i);
if (con_to) {
rm_tcp_connection_from_conn(con_to, tcp_connections_number);
}
}
kill_TCP_connection(tcp_con->connection);
return wipe_tcp_connection(tcp_c, tcp_connections_number);
}
/* Send a TCP routing request.
*
* return 0 on success.
* return -1 on failure.
*/
static int send_tcp_relay_routing_request(TCP_Connections *tcp_c, int tcp_connections_number, uint8_t *public_key)
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
if (!tcp_con)
return -1;
if (send_routing_request(tcp_con->connection, public_key) != 1)
return -1;
return 0;
}
static int tcp_response_callback(void *object, uint8_t connection_id, const uint8_t *public_key)
{
TCP_Client_Connection *TCP_client_con = object;
TCP_Connections *tcp_c = TCP_client_con->custom_object;
unsigned int tcp_connections_number = TCP_client_con->custom_uint;
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
if (!tcp_con)
return -1;
int connections_number = find_tcp_connection_to(tcp_c, public_key);
if (connections_number == -1)
return -1;
set_tcp_connection_number(tcp_con->connection, connection_id, connections_number);
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
if (con_to == NULL)
return -1;
if (set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_REGISTERED, connection_id) == -1)
return -1;
return 0;
}
static int tcp_status_callback(void *object, uint32_t number, uint8_t connection_id, uint8_t status)
{
TCP_Client_Connection *TCP_client_con = object;
TCP_Connections *tcp_c = TCP_client_con->custom_object;
unsigned int tcp_connections_number = TCP_client_con->custom_uint;
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
TCP_Connection_to *con_to = get_connection(tcp_c, number);
if (!con_to || !tcp_con)
return -1;
if (status == 1) {
if (set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_REGISTERED, connection_id) == -1)
return -1;
} else if (status == 2) {
if (set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_ONLINE, connection_id) == -1)
return -1;
}
return 0;
}
static int tcp_data_callback(void *object, uint32_t number, uint8_t connection_id, const uint8_t *data, uint16_t length)
{
if (length == 0)
return -1;
TCP_Client_Connection *TCP_client_con = object;
TCP_Connections *tcp_c = TCP_client_con->custom_object;
unsigned int tcp_connections_number = TCP_client_con->custom_uint;
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
if (!tcp_con)
return -1;
TCP_Connection_to *con_to = get_connection(tcp_c, number);
if (!con_to)
return -1;
if (tcp_c->tcp_data_callback)
tcp_c->tcp_data_callback(tcp_c->tcp_data_callback_object, con_to->id, data, length);
return 0;
}
static int tcp_oob_callback(void *object, const uint8_t *public_key, const uint8_t *data, uint16_t length)
{
if (length == 0)
return -1;
TCP_Client_Connection *TCP_client_con = object;
TCP_Connections *tcp_c = TCP_client_con->custom_object;
unsigned int tcp_connections_number = TCP_client_con->custom_uint;
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
if (!tcp_con)
return -1;
/* TODO: optimize */
int connections_number = find_tcp_connection_to(tcp_c, public_key);
if (connections_number == -1) {
if (tcp_c->tcp_oob_callback)
tcp_c->tcp_oob_callback(tcp_c->tcp_oob_callback_object, public_key, tcp_connections_number, data, length);
} else {
return tcp_data_callback(object, connections_number, 0, data, length);
}
return 0;
}
static int tcp_onion_callback(void *object, const uint8_t *data, uint16_t length)
{
TCP_Connections *tcp_c = object;
if (tcp_c->tcp_onion_callback)
tcp_c->tcp_onion_callback(tcp_c->tcp_onion_callback_object, data, length);
return 0;
}
/* Set callbacks for the TCP relay connection.
*
* return 0 on success.
* return -1 on failure.
*/
static int tcp_relay_set_callbacks(TCP_Connections *tcp_c, int tcp_connections_number)
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
if (!tcp_con)
return -1;
TCP_Client_Connection *con = tcp_con->connection;
con->custom_object = tcp_c;
con->custom_uint = tcp_connections_number;
onion_response_handler(con, &tcp_onion_callback, tcp_c);
routing_response_handler(con, &tcp_response_callback, con);
routing_status_handler(con, &tcp_status_callback, con);
routing_data_handler(con, &tcp_data_callback, con);
oob_data_handler(con, &tcp_oob_callback, con);
return 0;
}
static int tcp_relay_on_online(TCP_Connections *tcp_c, int tcp_connections_number)
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
if (!tcp_con)
return -1;
unsigned int i;
for (i = 0; i < tcp_c->connections_length; ++i) {
TCP_Connection_to *con_to = get_connection(tcp_c, i);
if (con_to) {
if (tcp_connection_in_conn(con_to, tcp_connections_number)) {
send_tcp_relay_routing_request(tcp_c, tcp_connections_number, con_to->public_key);
}
}
}
tcp_relay_set_callbacks(tcp_c, tcp_connections_number);
tcp_con->status = TCP_CONN_CONNECTED;
return 0;
}
static int add_tcp_relay(TCP_Connections *tcp_c, IP_Port ip_port, const uint8_t *relay_pk)
{
if (ip_port.ip.family == TCP_INET) {
ip_port.ip.family = AF_INET;
} else if (ip_port.ip.family == TCP_INET6) {
ip_port.ip.family = AF_INET6;
}
if (ip_port.ip.family != AF_INET && ip_port.ip.family != AF_INET6)
return -1;
int tcp_connections_number = create_tcp_connection(tcp_c);
if (tcp_connections_number == -1)
return -1;
TCP_con *tcp_con = &tcp_c->tcp_connections[tcp_connections_number];
tcp_con->connection = new_TCP_connection(ip_port, relay_pk, tcp_c->dht->self_public_key, tcp_c->dht->self_secret_key,
&tcp_c->proxy_info);
if (!tcp_con->connection)
return -1;
tcp_con->status = TCP_CONN_VALID;
return tcp_connections_number;
}
/* Add a TCP relay to the instance.
*
* return 0 on success.
* return -1 on failure.
*/
int add_tcp_relay_global(TCP_Connections *tcp_c, IP_Port ip_port, const uint8_t *relay_pk)
{
int tcp_connections_number = find_tcp_connection_relay(tcp_c, relay_pk);
if (tcp_connections_number != -1)
return -1;
if (add_tcp_relay(tcp_c, ip_port, relay_pk) == -1)
return -1;
return 0;
}
/* Add a TCP relay tied to a connection.
*
* return 0 on success.
* return -1 on failure.
*/
int add_tcp_number_relay_connection(TCP_Connections *tcp_c, int connections_number, unsigned int tcp_connections_number)
{
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
if (!con_to)
return -1;
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
if (!tcp_con)
return -1;
if (add_tcp_connection_to_conn(con_to, tcp_connections_number) == -1)
return -1;
++tcp_con->lock_count;
if (tcp_con->status == TCP_CONN_CONNECTED) {
send_tcp_relay_routing_request(tcp_c, tcp_connections_number, con_to->public_key);
}
return 0;
}
/* Add a TCP relay tied to a connection.
*
* return 0 on success.
* return -1 on failure.
*/
int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, IP_Port ip_port, const uint8_t *relay_pk)
{
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
if (!con_to)
return -1;
int tcp_connections_number = find_tcp_connection_relay(tcp_c, relay_pk);
if (tcp_connections_number != -1) {
return add_tcp_number_relay_connection(tcp_c, connections_number, tcp_connections_number);
} else {
int tcp_connections_number = add_tcp_relay(tcp_c, ip_port, relay_pk);
if (add_tcp_connection_to_conn(con_to, tcp_connections_number) == -1) {
return -1;
}
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
if (!tcp_con)
return -1;
++tcp_con->lock_count;
return 0;
}
}
/* Copy a maximum of max_num TCP relays we are connected to to tcp_relays.
* NOTE that the family of the copied ip ports will be set to TCP_INET or TCP_INET6.
*
* return number of relays copied to tcp_relays on success.
* return 0 on failure.
*/
unsigned int tcp_copy_connected_relays(TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num)
{
unsigned int i, copied = 0;
for (i = 0; (i < tcp_c->tcp_connections_length) && (copied < max_num); ++i) {
TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
if (!tcp_con) {
continue;
}
if (tcp_con->status == TCP_CONN_CONNECTED) {
memcpy(tcp_relays[copied].public_key, tcp_con->connection->public_key, crypto_box_PUBLICKEYBYTES);
tcp_relays[copied].ip_port = tcp_con->connection->ip_port;
if (tcp_relays[copied].ip_port.ip.family == AF_INET) {
tcp_relays[copied].ip_port.ip.family = TCP_INET;
} else if (tcp_relays[copied].ip_port.ip.family == AF_INET6) {
tcp_relays[copied].ip_port.ip.family = TCP_INET6;
}
++copied;
}
}
return copied;
}
TCP_Connections *new_tcp_connections(DHT *dht)
{
if (dht == NULL)
return NULL;
TCP_Connections *temp = calloc(1, sizeof(TCP_Connections));
if (temp == NULL)
return NULL;
temp->dht = dht;
return temp;
}
static void do_tcp_conns(TCP_Connections *tcp_c)
{
unsigned int i;
for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
if (tcp_con) {
do_TCP_connection(tcp_con->connection);
/* callbacks can change TCP connection address. */
tcp_con = get_tcp_connection(tcp_c, i);
if (tcp_con->connection->status == TCP_CLIENT_DISCONNECTED) {
kill_tcp_relay_connection(tcp_c, i);
continue;
}
if (tcp_con->status == TCP_CONN_VALID && tcp_con->connection->status == TCP_CLIENT_CONFIRMED) {
tcp_relay_on_online(tcp_c, i);
}
}
}
}
static void kill_nonused_tcp(TCP_Connections *tcp_c)
{
unsigned int i, num_online = 0;
for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
if (tcp_con) {
if (tcp_con->status == TCP_CONN_CONNECTED) {
if (!tcp_con->lock_count && num_online >= MAX_FRIEND_TCP_CONNECTIONS) {
kill_tcp_relay_connection(tcp_c, i);
continue;
}
++num_online;
}
}
}
}
void do_tcp_connections(TCP_Connections *tcp_c)
{
//TODO reconnect to TCP relays if disconnects happen.
do_tcp_conns(tcp_c);
kill_nonused_tcp(tcp_c);
}
void kill_tcp_connections(TCP_Connections *tcp_c)
{
unsigned int i;
for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
kill_TCP_connection(tcp_c->tcp_connections[i].connection);
}
free(tcp_c->tcp_connections);
free(tcp_c->connections);
free(tcp_c);
}

180
toxcore/TCP_connection.h Normal file
View File

@ -0,0 +1,180 @@
/* TCP_connection.h
*
* Handles TCP relay connections between two Tox clients.
*
* Copyright (C) 2015 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TCP_CONNECTION_H
#define TCP_CONNECTION_H
#include "TCP_client.h"
#define TCP_CONN_NONE 0
#define TCP_CONN_VALID 1
#define TCP_CONN_CONNECTED 2
#define TCP_CONNECTIONS_STATUS_NONE 0
#define TCP_CONNECTIONS_STATUS_REGISTERED 1
#define TCP_CONNECTIONS_STATUS_ONLINE 2
#define MAX_FRIEND_TCP_CONNECTIONS 4
typedef struct {
uint8_t status;
uint8_t public_key[crypto_box_PUBLICKEYBYTES]; /* The dht public key of the peer */
struct {
uint32_t tcp_connection;
unsigned int status;
unsigned int connection_id;
} connections[MAX_FRIEND_TCP_CONNECTIONS];
int id; /* id used in callbacks. */
} TCP_Connection_to;
typedef struct {
uint8_t status;
TCP_Client_Connection *connection;
uint32_t lock_count;
} TCP_con;
typedef struct {
DHT *dht;
TCP_Connection_to *connections;
uint32_t connections_length; /* Length of connections array. */
TCP_con *tcp_connections;
uint32_t tcp_connections_length; /* Length of tcp_connections array. */
int (*tcp_data_callback)(void *object, int id, const uint8_t *data, uint16_t length);
void *tcp_data_callback_object;
int (*tcp_oob_callback)(void *object, const uint8_t *public_key, unsigned int tcp_connections_number,
const uint8_t *data, uint16_t length);
void *tcp_oob_callback_object;
int (*tcp_onion_callback)(void *object, const uint8_t *data, uint16_t length);
void *tcp_onion_callback_object;
TCP_Proxy_Info proxy_info;
} TCP_Connections;
/* Send a packet to the TCP connection.
*
* return -1 on failure.
* return 0 on success.
*/
int send_packet_tcp_connection(TCP_Connections *tcp_c, int connections_number, const uint8_t *packet, uint16_t length);
/* Return a random TCP connection number for use in send_tcp_onion_request.
*
* TODO: This number is just the index of an array that the elements can
* change without warning.
*
* return TCP connection number on success.
* return -1 on failure.
*/
int get_random_tcp_conn_number(TCP_Connections *tcp_c);
/* Send an onion packet via the TCP relay corresponding to tcp_connections_number.
*
* return 0 on success.
* return -1 on failure.
*/
int tcp_send_onion_request(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *data,
uint16_t length);
/* Send an oob packet via the TCP relay corresponding to tcp_connections_number.
*
* return 0 on success.
* return -1 on failure.
*/
int tcp_send_oob_packet(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *public_key,
const uint8_t *packet, uint16_t length);
/* Set the callback for TCP data packets.
*/
void set_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_data_callback)(void *object, int id,
const uint8_t *data, uint16_t length), void *object);
/* Set the callback for TCP onion packets.
*/
void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_onion_callback)(void *object,
const uint8_t *data, uint16_t length), void *object);
/* Set the callback for TCP oob data packets.
*/
void set_oob_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_oob_callback)(void *object,
const uint8_t *public_key, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length), void *object);
/* Create a new TCP connection to public_key.
*
* id is the id in the callbacks for that connection.
*
* return connections_number on success.
* return -1 on failure.
*/
int new_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key, int id);
/* return 0 on success.
* return -1 on failure.
*/
int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number);
/* Add a TCP relay tied to a connection.
*
* NOTE: This can only be used during the tcp_oob_callback.
*
* return 0 on success.
* return -1 on failure.
*/
int add_tcp_number_relay_connection(TCP_Connections *tcp_c, int connections_number,
unsigned int tcp_connections_number);
/* Add a TCP relay tied to a connection.
*
* return 0 on success.
* return -1 on failure.
*/
int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, IP_Port ip_port, const uint8_t *relay_pk);
/* Add a TCP relay to the instance.
*
* return 0 on success.
* return -1 on failure.
*/
int add_tcp_relay_global(TCP_Connections *tcp_c, IP_Port ip_port, const uint8_t *relay_pk);
/* Copy a maximum of max_num TCP relays we are connected to to tcp_relays.
* NOTE that the family of the copied ip ports will be set to TCP_INET or TCP_INET6.
*
* return number of relays copied to tcp_relays on success.
* return 0 on failure.
*/
unsigned int tcp_copy_connected_relays(TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num);
TCP_Connections *new_tcp_connections(DHT *dht);
void do_tcp_connections(TCP_Connections *tcp_c);
void kill_tcp_connections(TCP_Connections *tcp_c);
#endif

View File

@ -925,6 +925,10 @@ static sock_t new_listening_TCP_socket(int family, uint16_t port)
ok = set_socket_dualstack(sock);
}
if (ok) {
ok = set_socket_reuseaddr(sock);
}
ok = ok && bind_to_port(sock, family, port) && (listen(sock, TCP_MAX_BACKLOG) == 0);
if (!ok) {
@ -1187,7 +1191,7 @@ static void do_TCP_epoll(TCP_Server *TCP_server)
for (n = 0; n < nfds; ++n) {
sock_t sock = events[n].data.u64 & 0xFFFFFFFF;
int status = (events[n].data.u64 >> 32) & 0xFFFF, index = (events[n].data.u64 >> 48);
int status = (events[n].data.u64 >> 32) & 0xFF, index = (events[n].data.u64 >> 40);
if ((events[n].events & EPOLLERR) || (events[n].events & EPOLLHUP) || (events[n].events & EPOLLRDHUP)) {
switch (status) {
@ -1241,7 +1245,7 @@ static void do_TCP_epoll(TCP_Server *TCP_server)
struct epoll_event ev = {
.events = EPOLLIN | EPOLLET | EPOLLRDHUP,
.data.u64 = sock_new | ((uint64_t)TCP_SOCKET_INCOMING << 32) | ((uint64_t)index_new << 48)
.data.u64 = sock_new | ((uint64_t)TCP_SOCKET_INCOMING << 32) | ((uint64_t)index_new << 40)
};
if (epoll_ctl(TCP_server->efd, EPOLL_CTL_ADD, sock_new, &ev) == -1) {
@ -1258,7 +1262,7 @@ static void do_TCP_epoll(TCP_Server *TCP_server)
if ((index_new = do_incoming(TCP_server, index)) != -1) {
events[n].events = EPOLLIN | EPOLLET | EPOLLRDHUP;
events[n].data.u64 = sock | ((uint64_t)TCP_SOCKET_UNCONFIRMED << 32) | ((uint64_t)index_new << 48);
events[n].data.u64 = sock | ((uint64_t)TCP_SOCKET_UNCONFIRMED << 32) | ((uint64_t)index_new << 40);
if (epoll_ctl(TCP_server->efd, EPOLL_CTL_MOD, sock, &events[n]) == -1) {
kill_TCP_connection(&TCP_server->unconfirmed_connection_queue[index_new]);
@ -1274,7 +1278,7 @@ static void do_TCP_epoll(TCP_Server *TCP_server)
if ((index_new = do_unconfirmed(TCP_server, index)) != -1) {
events[n].events = EPOLLIN | EPOLLET | EPOLLRDHUP;
events[n].data.u64 = sock | ((uint64_t)TCP_SOCKET_CONFIRMED << 32) | ((uint64_t)index_new << 48);
events[n].data.u64 = sock | ((uint64_t)TCP_SOCKET_CONFIRMED << 32) | ((uint64_t)index_new << 40);
if (epoll_ctl(TCP_server->efd, EPOLL_CTL_MOD, sock, &events[n]) == -1) {
//remove from confirmed connections

View File

@ -194,19 +194,10 @@ void new_symmetric_key(uint8_t *key)
randombytes(key, crypto_box_KEYBYTES);
}
static uint8_t base_nonce[crypto_box_NONCEBYTES];
static uint8_t nonce_set = 0;
/* Gives a nonce guaranteed to be different from previous ones.*/
void new_nonce(uint8_t *nonce)
{
if (nonce_set == 0) {
random_nonce(base_nonce);
nonce_set = 1;
}
increment_nonce(base_nonce);
memcpy(nonce, base_nonce, crypto_box_NONCEBYTES);
random_nonce(nonce);
}
/* Create a request to peer.

View File

@ -122,7 +122,7 @@ void new_nonce(uint8_t *nonce);
#define CRYPTO_PACKET_FRIEND_REQ 32 /* Friend request crypto packet ID. */
#define CRYPTO_PACKET_HARDENING 48 /* Hardening crypto packet ID. */
#define CRYPTO_PACKET_FAKEID 156
#define CRYPTO_PACKET_DHTPK 156
#define CRYPTO_PACKET_NAT_PING 254 /* NAT ping crypto packet ID. */
/* Create a request to peer.

View File

@ -224,23 +224,32 @@ static int handle_status(void *object, int number, uint8_t status)
if (!friend_con)
return -1;
_Bool call_cb = 0;
if (status) { /* Went online. */
call_cb = 1;
friend_con->status = FRIENDCONN_STATUS_CONNECTED;
friend_con->ping_lastrecv = unix_time();
onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status);
} else { /* Went offline. */
if (friend_con->status != FRIENDCONN_STATUS_CONNECTING) {
call_cb = 1;
friend_con->dht_ping_lastrecv = unix_time();
onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status);
}
friend_con->status = FRIENDCONN_STATUS_CONNECTING;
friend_con->crypt_connection_id = -1;
friend_con->dht_ping_lastrecv = unix_time();
onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status);
}
unsigned int i;
if (call_cb) {
unsigned int i;
for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
if (friend_con->callbacks[i].status_callback)
friend_con->callbacks[i].status_callback(friend_con->callbacks[i].status_callback_object,
friend_con->callbacks[i].status_callback_id, status);
for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
if (friend_con->callbacks[i].status_callback)
friend_con->callbacks[i].status_callback(friend_con->callbacks[i].status_callback_object,
friend_con->callbacks[i].status_callback_id, status);
}
}
return 0;

View File

@ -42,7 +42,7 @@ uint32_t get_nospam(const Friend_Requests *fr)
/* Set the function that will be executed when a friend request is received. */
void callback_friendrequest(Friend_Requests *fr, void (*function)(void *, const uint8_t *, const uint8_t *, uint16_t,
void callback_friendrequest(Friend_Requests *fr, void (*function)(void *, const uint8_t *, const uint8_t *, size_t,
void *), void *object, void *userdata)
{
fr->handle_friendrequest = function;

View File

@ -30,7 +30,7 @@
typedef struct {
uint32_t nospam;
void (*handle_friendrequest)(void *, const uint8_t *, const uint8_t *, uint16_t, void *);
void (*handle_friendrequest)(void *, const uint8_t *, const uint8_t *, size_t, void *);
uint8_t handle_friendrequest_isset;
void *handle_friendrequest_object;
void *handle_friendrequest_userdata;
@ -59,9 +59,9 @@ uint32_t get_nospam(const Friend_Requests *fr);
int remove_request_received(Friend_Requests *fr, const uint8_t *real_pk);
/* 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, void * userdata)
* Function format is function(uint8_t * public_key, uint8_t * data, size_t length, void * userdata)
*/
void callback_friendrequest(Friend_Requests *fr, void (*function)(void *, const uint8_t *, const uint8_t *, uint16_t,
void callback_friendrequest(Friend_Requests *fr, void (*function)(void *, const uint8_t *, const uint8_t *, size_t,
void *), void *object, void *userdata);
/* Set the function used to check if a friend request should be displayed to the user or not.

View File

@ -518,7 +518,7 @@ static int delpeer(Group_Chats *g_c, int groupnumber, int peer_index)
static int setnick(Group_Chats *g_c, int groupnumber, int peer_index, const uint8_t *nick, uint16_t nick_len)
{
if (nick_len > MAX_NAME_LENGTH || nick_len == 0)
if (nick_len > MAX_NAME_LENGTH)
return -1;
Group_c *g = get_group_c(g_c, groupnumber);
@ -528,10 +528,12 @@ static int setnick(Group_Chats *g_c, int groupnumber, int peer_index, const uint
/* same name as already stored? */
if (g->group[peer_index].nick_len == nick_len)
if (!memcmp(g->group[peer_index].nick, nick, nick_len))
if (nick_len == 0 || !memcmp(g->group[peer_index].nick, nick, nick_len))
return 0;
memcpy(g->group[peer_index].nick, nick, nick_len);
if (nick_len)
memcpy(g->group[peer_index].nick, nick, nick_len);
g->group[peer_index].nick_len = nick_len;
if (g_c->peer_namelistchange)
@ -1234,7 +1236,7 @@ int group_title_get(const Group_Chats *g_c, int groupnumber, uint8_t *title, uin
return max_length;
}
static void handle_friend_invite_packet(Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length)
static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length)
{
Group_Chats *g_c = m->group_chat_object;

View File

@ -722,7 +722,7 @@ static int handle_request_packet(Packets_Array *send_array, const uint8_t *data,
if (n == data[0]) {
if (send_array->buffer[num]) {
send_array->buffer[num]->time = 0;
send_array->buffer[num]->sent = 0;
}
++data;
@ -803,12 +803,49 @@ static int send_data_packet_helper(Net_Crypto *c, int crypt_connection_id, uint3
uint8_t packet[sizeof(uint32_t) + sizeof(uint32_t) + padding_length + length];
memcpy(packet, &buffer_start, sizeof(uint32_t));
memcpy(packet + sizeof(uint32_t), &num, sizeof(uint32_t));
memset(packet + (sizeof(uint32_t) * 2), 0, padding_length);
memset(packet + (sizeof(uint32_t) * 2), PACKET_ID_PADDING, padding_length);
memcpy(packet + (sizeof(uint32_t) * 2) + padding_length, data, length);
return send_data_packet(c, crypt_connection_id, packet, sizeof(packet));
}
static int reset_max_speed_reached(Net_Crypto *c, int crypt_connection_id)
{
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
if (conn == 0)
return -1;
/* If last packet send failed, try to send packet again.
If sending it fails we won't be able to send the new packet. */
if (conn->maximum_speed_reached) {
Packet_Data *dt = NULL;
uint32_t packet_num = conn->send_array.buffer_end - 1;
int ret = get_data_pointer(&conn->send_array, &dt, packet_num);
uint8_t send_failed = 0;
if (ret == 1) {
if (!dt->sent) {
if (send_data_packet_helper(c, crypt_connection_id, conn->recv_array.buffer_start, packet_num, dt->data,
dt->length) != 0) {
send_failed = 1;
} else {
dt->sent = 1;
}
}
}
if (!send_failed) {
conn->maximum_speed_reached = 0;
} else {
return -1;
}
}
return 0;
}
/* return -1 if data could not be put in packet queue.
* return positive packet number if data was put into the queue.
*/
@ -823,39 +860,16 @@ static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, cons
if (conn == 0)
return -1;
uint64_t temp_time = current_time_monotonic();
/* If last packet send failed, try to send packet again.
If sending it fails we won't be able to send the new packet. */
if (conn->maximum_speed_reached) {
Packet_Data *dt = NULL;
uint32_t packet_num = conn->send_array.buffer_end - 1;
int ret = get_data_pointer(&conn->send_array, &dt, packet_num);
reset_max_speed_reached(c, crypt_connection_id);
uint8_t send_failed = 0;
if (ret == 1) {
if (!dt->time) {
if (send_data_packet_helper(c, crypt_connection_id, conn->recv_array.buffer_start, packet_num, dt->data,
dt->length) != 0) {
if (congestion_control) {
return -1;
} else {
send_failed = 1;
}
} else {
dt->time = temp_time;
}
}
}
if (!send_failed) {
conn->maximum_speed_reached = 0;
}
if (conn->maximum_speed_reached && congestion_control) {
return -1;
}
Packet_Data dt;
dt.time = 0;
dt.sent = 0;
dt.length = length;
memcpy(dt.data, data, length);
pthread_mutex_lock(&conn->mutex);
@ -873,7 +887,7 @@ static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, cons
Packet_Data *dt1 = NULL;
if (get_data_pointer(&conn->send_array, &dt1, packet_num) == 1)
dt1->time = temp_time;
dt1->sent = 1;
} else {
conn->maximum_speed_reached = 1;
LOGGER_ERROR("send_data_packet failed\n");
@ -983,15 +997,15 @@ static int send_requested_packets(Net_Crypto *c, int crypt_connection_id, uint16
continue;
}
if (dt->time != 0) {
if (dt->sent) {
continue;
}
dt->time = current_time_monotonic();
if (send_data_packet_helper(c, crypt_connection_id, conn->recv_array.buffer_start, packet_num, dt->data,
dt->length) == 0)
dt->length) == 0) {
dt->sent = 1;
++num_sent;
}
if (num_sent >= max_num)
break;
@ -1156,7 +1170,7 @@ static int handle_data_packet_helper(const Net_Crypto *c, int crypt_connection_i
uint8_t *real_data = data + (sizeof(uint32_t) * 2);
uint16_t real_length = len - (sizeof(uint32_t) * 2);
while (real_data[0] == 0) { /* Remove Padding */
while (real_data[0] == PACKET_ID_PADDING) { /* Remove Padding */
++real_data;
--real_length;
@ -1189,7 +1203,6 @@ static int handle_data_packet_helper(const Net_Crypto *c, int crypt_connection_i
set_buffer_end(&conn->recv_array, num);
} else if (real_data[0] >= CRYPTO_RESERVED_PACKETS && real_data[0] < PACKET_ID_LOSSY_RANGE_START) {
Packet_Data dt;
dt.time = current_time_monotonic();
dt.length = real_length;
memcpy(dt.data, real_data, real_length);
@ -2529,7 +2542,7 @@ static void send_crypto_packets(Net_Crypto *c)
if (conn->last_packets_left_set == 0) {
conn->last_packets_left_set = temp_time;
conn->packets_left = CRYPTO_MIN_QUEUE_LENGTH;
} else if (((1000.0 / conn->packet_send_rate) + conn->last_packets_left_set) < temp_time) {
} else if (((uint64_t)((1000.0 / conn->packet_send_rate) + 0.5) + conn->last_packets_left_set) < temp_time) {
uint32_t num_packets = conn->packet_send_rate * ((double)(temp_time - conn->last_packets_left_set) / 1000.0) + 0.5;
if (conn->packets_left > num_packets * 4 + CRYPTO_MIN_QUEUE_LENGTH) {
@ -2575,6 +2588,13 @@ static void send_crypto_packets(Net_Crypto *c)
}
}
/* Return 1 if max speed was reached for this connection (no more data can be physically through the pipe).
* Return 0 if it wasn't reached.
*/
_Bool max_speed_reached(Net_Crypto *c, int crypt_connection_id)
{
return reset_max_speed_reached(c, crypt_connection_id) != 0;
}
/* returns the number of packet slots left in the sendbuffer.
* return 0 if failure.
@ -2586,7 +2606,13 @@ uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connecti
if (conn == 0)
return 0;
return conn->packets_left;
uint32_t max_packets = CRYPTO_PACKET_BUFFER_SIZE - num_packets_array(&conn->send_array);
if (conn->packets_left < max_packets) {
return conn->packets_left;
} else {
return max_packets;
}
}
/* Sends a lossless cryptopacket.

View File

@ -61,7 +61,7 @@
#define MAX_NUM_SENDPACKET_TRIES 8
/* The timeout of no received UDP packets before the direct UDP connection is considered dead. */
#define UDP_DIRECT_TIMEOUT (MAX_NUM_SENDPACKET_TRIES * CRYPTO_SEND_PACKET_INTERVAL)
#define UDP_DIRECT_TIMEOUT ((MAX_NUM_SENDPACKET_TRIES * CRYPTO_SEND_PACKET_INTERVAL) / 2)
#define PACKET_ID_PADDING 0 /* Denotes padding */
#define PACKET_ID_REQUEST 1 /* Used to request unreceived packets */
@ -89,7 +89,7 @@
#define CONGESTION_QUEUE_ARRAY_SIZE 24
typedef struct {
uint64_t time;
_Bool sent;
uint16_t length;
uint8_t data[MAX_CRYPTO_DATA_SIZE];
} Packet_Data;
@ -317,6 +317,11 @@ int nc_dht_pk_callback(Net_Crypto *c, int crypt_connection_id, void (*function)(
*/
uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connection_id);
/* Return 1 if max speed was reached for this connection (no more data can be physically through the pipe).
* Return 0 if it wasn't reached.
*/
_Bool max_speed_reached(Net_Crypto *c, int crypt_connection_id);
/* Sends a lossless cryptopacket.
*
* return -1 if data could not be put in packet queue.

View File

@ -172,6 +172,17 @@ int set_socket_nosigpipe(sock_t sock)
#endif
}
/* Enable SO_REUSEADDR on socket.
*
* return 1 on success
* return 0 on failure
*/
int set_socket_reuseaddr(sock_t sock)
{
int set = 1;
return (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&set, sizeof(set)) == 0);
}
/* Set socket to dual (IPv4 + IPv6 socket)
*
* return 1 on success
@ -466,6 +477,14 @@ static void at_shutdown(void)
}
*/
/* Initialize networking.
* Added for reverse compatibility with old new_networking calls.
*/
Networking_Core *new_networking(IP ip, uint16_t port)
{
return new_networking_ex(ip, port, port + (TOX_PORTRANGE_TO - TOX_PORTRANGE_FROM), 0);
}
/* Initialize networking.
* Bind to ip and port.
* ip must be in network order EX: 127.0.0.1 = (7F000001).
@ -473,9 +492,31 @@ static void at_shutdown(void)
*
* return Networking_Core object if no problems
* return NULL if there are problems.
*
* If error is non NULL it is set to 0 if no issues, 1 if socket related error, 2 if other.
*/
Networking_Core *new_networking(IP ip, uint16_t port)
Networking_Core *new_networking_ex(IP ip, uint16_t port_from, uint16_t port_to, unsigned int *error)
{
/* If both from and to are 0, use default port range
* If one is 0 and the other is non-0, use the non-0 value as only port
* If from > to, swap
*/
if (port_from == 0 && port_to == 0) {
port_from = TOX_PORTRANGE_FROM;
port_to = TOX_PORTRANGE_TO;
} else if (port_from == 0 && port_to != 0) {
port_from = port_to;
} else if (port_from != 0 && port_to == 0) {
port_to = port_from;
} else if (port_from > port_to) {
uint16_t temp = port_from;
port_from = port_to;
port_to = temp;
}
if (error)
*error = 2;
/* maybe check for invalid IPs like 224+.x.y.z? if there is any IP set ever */
if (ip.family != AF_INET && ip.family != AF_INET6) {
#ifdef DEBUG
@ -505,6 +546,10 @@ Networking_Core *new_networking(IP ip, uint16_t port)
fprintf(stderr, "Failed to get a socket?! %u, %s\n", errno, strerror(errno));
#endif
free(temp);
if (error)
*error = 1;
return NULL;
}
@ -521,12 +566,20 @@ Networking_Core *new_networking(IP ip, uint16_t port)
/* iOS UDP sockets are weird and apparently can SIGPIPE */
if (!set_socket_nosigpipe(temp->sock)) {
kill_networking(temp);
if (error)
*error = 1;
return NULL;
}
/* Set socket nonblocking. */
if (!set_socket_nonblock(temp->sock)) {
kill_networking(temp);
if (error)
*error = 1;
return NULL;
}
@ -600,11 +653,11 @@ Networking_Core *new_networking(IP ip, uint16_t port)
* some clients might not test return of tox_new(), blindly assuming that
* it worked ok (which it did previously without a successful bind)
*/
uint16_t port_to_try = port;
uint16_t port_to_try = port_from;
*portptr = htons(port_to_try);
int tries;
for (tries = TOX_PORTRANGE_FROM; tries <= TOX_PORTRANGE_TO; tries++) {
for (tries = port_from; tries <= port_to; tries++) {
int res = bind(temp->sock, (struct sockaddr *)&addr, addrsize);
if (!res) {
@ -618,22 +671,28 @@ Networking_Core *new_networking(IP ip, uint16_t port)
if (tries > 0)
errno = 0;
if (error)
*error = 0;
return temp;
}
port_to_try++;
if (port_to_try > TOX_PORTRANGE_TO)
port_to_try = TOX_PORTRANGE_FROM;
if (port_to_try > port_to)
port_to_try = port_from;
*portptr = htons(port_to_try);
}
#ifdef DEBUG
fprintf(stderr, "Failed to bind socket: %u, %s (IP/Port: %s:%u\n", errno,
strerror(errno), ip_ntoa(&ip), port);
#endif
LOGGER_ERROR("Failed to bind socket: %u, %s IP: %s port_from: %u port_to: %u", errno, strerror(errno),
ip_ntoa(&ip), port_from, port_to);
kill_networking(temp);
if (error)
*error = 1;
return NULL;
}

View File

@ -334,6 +334,13 @@ int set_socket_nonblock(sock_t sock);
*/
int set_socket_nosigpipe(sock_t sock);
/* Enable SO_REUSEADDR on socket.
*
* return 1 on success
* return 0 on failure
*/
int set_socket_reuseaddr(sock_t sock);
/* Set socket to dual (IPv4 + IPv6 socket)
*
* return 1 on success
@ -362,8 +369,11 @@ void networking_poll(Networking_Core *net);
*
* return Networking_Core object if no problems
* return NULL if there are problems.
*
* If error is non NULL it is set to 0 if no issues, 1 if socket related error, 2 if other.
*/
Networking_Core *new_networking(IP ip, uint16_t port);
Networking_Core *new_networking_ex(IP ip, uint16_t port_from, uint16_t port_to, unsigned int *error);
/* Function to cleanup networking stuff (doesn't do much right now). */
void kill_networking(Networking_Core *net);

View File

@ -858,7 +858,7 @@ static int send_dht_dhtpk(const Onion_Client *onion_c, int friend_num, const uin
uint8_t packet[MAX_CRYPTO_REQUEST_SIZE];
len = create_request(onion_c->dht->self_public_key, onion_c->dht->self_secret_key, packet,
onion_c->friends_list[friend_num].dht_public_key, temp, sizeof(temp), CRYPTO_PACKET_FAKEID);
onion_c->friends_list[friend_num].dht_public_key, temp, sizeof(temp), CRYPTO_PACKET_DHTPK);
if (len == -1)
return -1;
@ -1351,78 +1351,10 @@ static void do_announce(Onion_Client *onion_c)
}
}
void do_onion_client(Onion_Client *onion_c)
{
unsigned int i;
if (onion_c->last_run == unix_time())
return;
populate_path_nodes(onion_c);
do_announce(onion_c);
if (onion_isconnected(onion_c)) {
for (i = 0; i < onion_c->num_friends; ++i) {
do_friend(onion_c, i);
}
} else {
populate_path_nodes_tcp(onion_c);
}
onion_c->last_run = unix_time();
}
Onion_Client *new_onion_client(Net_Crypto *c)
{
if (c == NULL)
return NULL;
Onion_Client *onion_c = calloc(1, sizeof(Onion_Client));
if (onion_c == NULL)
return NULL;
if (ping_array_init(&onion_c->announce_ping_array, ANNOUNCE_ARRAY_SIZE, ANNOUNCE_TIMEOUT) != 0) {
free(onion_c);
return NULL;
}
onion_c->dht = c->dht;
onion_c->net = c->dht->net;
onion_c->c = c;
new_symmetric_key(onion_c->secret_symmetric_key);
crypto_box_keypair(onion_c->temp_public_key, onion_c->temp_secret_key);
networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_announce_response, onion_c);
networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, &handle_data_response, onion_c);
oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, &handle_dhtpk_announce, onion_c);
cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_FAKEID, &handle_dht_dhtpk, onion_c);
tcp_onion_response_handler(onion_c->c, &handle_tcp_onion, onion_c);
return onion_c;
}
void kill_onion_client(Onion_Client *onion_c)
{
if (onion_c == NULL)
return;
ping_array_free_all(&onion_c->announce_ping_array);
realloc_onion_friends(onion_c, 0);
networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, NULL, NULL);
networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, NULL, NULL);
oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, NULL, NULL);
cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_FAKEID, NULL, NULL);
tcp_onion_response_handler(onion_c->c, NULL, NULL);
memset(onion_c, 0, sizeof(Onion_Client));
free(onion_c);
}
/* return 0 if we are not connected to the network.
* return 1 if we are.
*/
int onion_isconnected(const Onion_Client *onion_c)
static int onion_isconnected(const Onion_Client *onion_c)
{
unsigned int i, num = 0, announced = 0;
@ -1457,3 +1389,102 @@ int onion_isconnected(const Onion_Client *onion_c)
return 0;
}
#define ONION_CONNECTION_SECONDS 2
/* return 0 if we are not connected to the network.
* return 1 if we are connected with TCP only.
* return 2 if we are also connected with UDP.
*/
unsigned int onion_connection_status(const Onion_Client *onion_c)
{
if (onion_c->onion_connected >= ONION_CONNECTION_SECONDS) {
if (onion_c->UDP_connected) {
return 2;
} else {
return 1;
}
}
return 0;
}
void do_onion_client(Onion_Client *onion_c)
{
unsigned int i;
if (onion_c->last_run == unix_time())
return;
populate_path_nodes(onion_c);
do_announce(onion_c);
if (onion_isconnected(onion_c)) {
if (onion_c->onion_connected < ONION_CONNECTION_SECONDS * 2) {
++onion_c->onion_connected;
}
onion_c->UDP_connected = DHT_non_lan_connected(onion_c->dht);
} else {
populate_path_nodes_tcp(onion_c);
if (onion_c->onion_connected != 0) {
--onion_c->onion_connected;
}
}
if (onion_connection_status(onion_c)) {
for (i = 0; i < onion_c->num_friends; ++i) {
do_friend(onion_c, i);
}
}
onion_c->last_run = unix_time();
}
Onion_Client *new_onion_client(Net_Crypto *c)
{
if (c == NULL)
return NULL;
Onion_Client *onion_c = calloc(1, sizeof(Onion_Client));
if (onion_c == NULL)
return NULL;
if (ping_array_init(&onion_c->announce_ping_array, ANNOUNCE_ARRAY_SIZE, ANNOUNCE_TIMEOUT) != 0) {
free(onion_c);
return NULL;
}
onion_c->dht = c->dht;
onion_c->net = c->dht->net;
onion_c->c = c;
new_symmetric_key(onion_c->secret_symmetric_key);
crypto_box_keypair(onion_c->temp_public_key, onion_c->temp_secret_key);
networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_announce_response, onion_c);
networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, &handle_data_response, onion_c);
oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, &handle_dhtpk_announce, onion_c);
cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_DHTPK, &handle_dht_dhtpk, onion_c);
tcp_onion_response_handler(onion_c->c, &handle_tcp_onion, onion_c);
return onion_c;
}
void kill_onion_client(Onion_Client *onion_c)
{
if (onion_c == NULL)
return;
ping_array_free_all(&onion_c->announce_ping_array);
realloc_onion_friends(onion_c, 0);
networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, NULL, NULL);
networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, NULL, NULL);
oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, NULL, NULL);
cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_DHTPK, NULL, NULL);
tcp_onion_response_handler(onion_c->c, NULL, NULL);
memset(onion_c, 0, sizeof(Onion_Client));
free(onion_c);
}

View File

@ -57,7 +57,7 @@
/* Onion data packet ids. */
#define ONION_DATA_FRIEND_REQ CRYPTO_PACKET_FRIEND_REQ
#define ONION_DATA_DHTPK CRYPTO_PACKET_FAKEID
#define ONION_DATA_DHTPK CRYPTO_PACKET_DHTPK
typedef struct {
uint8_t public_key[crypto_box_PUBLICKEYBYTES];
@ -157,6 +157,9 @@ typedef struct {
} Onion_Data_Handlers[256];
uint64_t last_packet_recv;
unsigned int onion_connected;
_Bool UDP_connected;
} Onion_Client;
@ -278,8 +281,9 @@ void kill_onion_client(Onion_Client *onion_c);
/* return 0 if we are not connected to the network.
* return 1 if we are.
* return 1 if we are connected with TCP only.
* return 2 if we are also connected with UDP.
*/
int onion_isconnected(const Onion_Client *onion_c);
unsigned int onion_connection_status(const Onion_Client *onion_c);
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

173
toxcore/tox_old.h Normal file
View File

@ -0,0 +1,173 @@
/**********GROUP CHAT FUNCTIONS ************/
/* Group chat types for tox_callback_group_invite function.
*
* TOX_GROUPCHAT_TYPE_TEXT groupchats must be accepted with the tox_join_groupchat() function.
* The function to accept TOX_GROUPCHAT_TYPE_AV is in toxav.
*/
enum {
TOX_GROUPCHAT_TYPE_TEXT,
TOX_GROUPCHAT_TYPE_AV
};
/* Set the callback for group invites.
*
* Function(Tox *tox, int32_t friendnumber, uint8_t type, const uint8_t *data, uint16_t length, void *userdata)
*
* data of length is what needs to be passed to join_groupchat().
*
* for what type means see the enum right above this comment.
*/
void tox_callback_group_invite(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, const uint8_t *, uint16_t,
void *), void *userdata);
/* Set the callback for group messages.
*
* Function(Tox *tox, int groupnumber, int peernumber, const uint8_t * message, uint16_t length, void *userdata)
*/
void tox_callback_group_message(Tox *tox, void (*function)(Tox *tox, int, int, const uint8_t *, uint16_t, void *),
void *userdata);
/* Set the callback for group actions.
*
* Function(Tox *tox, int groupnumber, int peernumber, const uint8_t * action, uint16_t length, void *userdata)
*/
void tox_callback_group_action(Tox *tox, void (*function)(Tox *tox, int, int, const uint8_t *, uint16_t, void *),
void *userdata);
/* Set callback function for title changes.
*
* Function(Tox *tox, int groupnumber, int peernumber, uint8_t * title, uint8_t length, void *userdata)
* if peernumber == -1, then author is unknown (e.g. initial joining the group)
*/
void tox_callback_group_title(Tox *tox, void (*function)(Tox *tox, int, int, const uint8_t *, uint8_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.
* return -1 on failure.
*/
int tox_add_groupchat(Tox *tox);
/* Delete a groupchat from the chats array.
*
* return 0 on success.
* return -1 if failure.
*/
int tox_del_groupchat(Tox *tox, int groupnumber);
/* Copy the name of peernumber who is in groupnumber to name.
* name must be at least TOX_MAX_NAME_LENGTH long.
*
* return length of name if success
* return -1 if failure
*/
int tox_group_peername(const Tox *tox, int groupnumber, int peernumber, uint8_t *name);
/* Copy the public key of peernumber who is in groupnumber to public_key.
* public_key must be TOX_PUBLIC_KEY_SIZE long.
*
* returns 0 on success
* returns -1 on failure
*/
int tox_group_peer_pubkey(const Tox *tox, int groupnumber, int peernumber, uint8_t *public_key);
/* invite friendnumber to groupnumber
* return 0 on success
* return -1 on failure
*/
int tox_invite_friend(Tox *tox, int32_t friendnumber, int groupnumber);
/* Join a group (you need to have been invited first.) using data of length obtained
* in the group invite callback.
*
* returns group number on success
* returns -1 on failure.
*/
int tox_join_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length);
/* send a group message
* return 0 on success
* return -1 on failure
*/
int tox_group_message_send(Tox *tox, int groupnumber, const uint8_t *message, uint16_t length);
/* send a group action
* return 0 on success
* return -1 on failure
*/
int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint16_t length);
/* set the group's title, limited to MAX_NAME_LENGTH
* return 0 on success
* return -1 on failure
*/
int tox_group_set_title(Tox *tox, int groupnumber, const uint8_t *title, uint8_t length);
/* Get group title from groupnumber and put it in title.
* title needs to be a valid memory location with a max_length size of at least MAX_NAME_LENGTH (128) bytes.
*
* return length of copied title if success.
* return -1 if failure.
*/
int tox_group_get_title(Tox *tox, int groupnumber, uint8_t *title, uint32_t max_length);
/* Check if the current peernumber corresponds to ours.
*
* return 1 if the peernumber corresponds to ours.
* return 0 on failure.
*/
unsigned int tox_group_peernumber_is_ours(const Tox *tox, int groupnumber, int peernumber);
/* Return the number of peers in the group chat on success.
* return -1 on failure
*/
int tox_group_number_peers(const 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.
*
* Copies the lengths of the names to lengths[length]
*
* returns the number of peers on success.
*
* return -1 on failure.
*/
int tox_group_get_names(const Tox *tox, int groupnumber, uint8_t names[][TOX_MAX_NAME_LENGTH], uint16_t lengths[],
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(const 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(const Tox *tox, int32_t *out_list, uint32_t list_size);
/* return the type of groupchat (TOX_GROUPCHAT_TYPE_) that groupnumber is.
*
* return -1 on failure.
* return type on success.
*/
int tox_group_get_type(const Tox *tox, int groupnumber);

237
toxcore/tox_old_code.h Normal file
View File

@ -0,0 +1,237 @@
/**********GROUP CHAT FUNCTIONS: WARNING Group chats will be rewritten so this might change ************/
/* Set the callback for group invites.
*
* Function(Tox *tox, int32_t friendnumber, uint8_t type, uint8_t *data, uint16_t length, void *userdata)
*
* data of length is what needs to be passed to join_groupchat().
*/
void tox_callback_group_invite(Tox *tox, void (*function)(Messenger *tox, int32_t, uint8_t, const uint8_t *, uint16_t,
void *), void *userdata)
{
Messenger *m = tox;
g_callback_group_invite(m->group_chat_object, function, userdata);
}
/* Set the callback for group messages.
*
* Function(Tox *tox, int groupnumber, int peernumber, uint8_t * message, uint16_t length, void *userdata)
*/
void tox_callback_group_message(Tox *tox, void (*function)(Messenger *tox, int, int, const uint8_t *, uint16_t, void *),
void *userdata)
{
Messenger *m = tox;
g_callback_group_message(m->group_chat_object, function, userdata);
}
/* Set the callback for group actions.
*
* Function(Tox *tox, int groupnumber, int peernumber, uint8_t * action, uint16_t length, void *userdata)
*/
void tox_callback_group_action(Tox *tox, void (*function)(Messenger *tox, int, int, const uint8_t *, uint16_t, void *),
void *userdata)
{
Messenger *m = tox;
g_callback_group_action(m->group_chat_object, function, userdata);
}
/* Set callback function for title changes.
*
* Function(Tox *tox, int groupnumber, int peernumber, uint8_t * title, uint8_t length, void *userdata)
* if peernumber == -1, then author is unknown (e.g. initial joining the group)
*/
void tox_callback_group_title(Tox *tox, void (*function)(Messenger *tox, int, int, const uint8_t *, uint8_t,
void *), void *userdata)
{
Messenger *m = tox;
g_callback_group_title(m->group_chat_object, 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;
g_callback_group_namelistchange(m->group_chat_object, function, userdata);
}
/* Creates a new groupchat and puts it in the chats array.
*
* return group number on success.
* return -1 on failure.
*/
int tox_add_groupchat(Tox *tox)
{
Messenger *m = tox;
return add_groupchat(m->group_chat_object, GROUPCHAT_TYPE_TEXT);
}
/* Delete a groupchat from the chats array.
*
* return 0 on success.
* return -1 if failure.
*/
int tox_del_groupchat(Tox *tox, int groupnumber)
{
Messenger *m = tox;
return del_groupchat(m->group_chat_object, groupnumber);
}
/* Copy the name of peernumber who is in groupnumber to name.
* name must be at least MAX_NICK_BYTES long.
*
* return length of name if success
* return -1 if failure
*/
int tox_group_peername(const Tox *tox, int groupnumber, int peernumber, uint8_t *name)
{
const Messenger *m = tox;
return group_peername(m->group_chat_object, groupnumber, peernumber, name);
}
/* Copy the public key of peernumber who is in groupnumber to public_key.
* public_key must be crypto_box_PUBLICKEYBYTES long.
*
* returns 0 on success
* returns -1 on failure
*/
int tox_group_peer_pubkey(const Tox *tox, int groupnumber, int peernumber, uint8_t *public_key)
{
const Messenger *m = tox;
return group_peer_pubkey(m->group_chat_object, groupnumber, peernumber, public_key);
}
/* invite friendnumber to groupnumber
* return 0 on success
* return -1 on failure
*/
int tox_invite_friend(Tox *tox, int32_t friendnumber, int groupnumber)
{
Messenger *m = tox;
return invite_friend(m->group_chat_object, friendnumber, groupnumber);
}
/* Join a group (you need to have been invited first.) using data of length obtained
* in the group invite callback.
*
* returns group number on success
* returns -1 on failure.
*/
int tox_join_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length)
{
Messenger *m = tox;
return join_groupchat(m->group_chat_object, friendnumber, GROUPCHAT_TYPE_TEXT, data, length);
}
/* send a group message
* return 0 on success
* return -1 on failure
*/
int tox_group_message_send(Tox *tox, int groupnumber, const uint8_t *message, uint16_t length)
{
Messenger *m = tox;
return group_message_send(m->group_chat_object, groupnumber, message, length);
}
/* send a group action
* return 0 on success
* return -1 on failure
*/
int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint16_t length)
{
Messenger *m = tox;
return group_action_send(m->group_chat_object, groupnumber, action, length);
}
/* set the group's title, limited to MAX_NAME_LENGTH
* return 0 on success
* return -1 on failure
*/
int tox_group_set_title(Tox *tox, int groupnumber, const uint8_t *title, uint8_t length)
{
Messenger *m = tox;
return group_title_send(m->group_chat_object, groupnumber, title, length);
}
/* Get group title from groupnumber and put it in title.
* title needs to be a valid memory location with a max_length size of at least MAX_NAME_LENGTH (128) bytes.
*
* return length of copied title if success.
* return -1 if failure.
*/
int tox_group_get_title(Tox *tox, int groupnumber, uint8_t *title, uint32_t max_length)
{
Messenger *m = tox;
return group_title_get(m->group_chat_object, groupnumber, title, max_length);
}
/* Check if the current peernumber corresponds to ours.
*
* return 1 if the peernumber corresponds to ours.
* return 0 on failure.
*/
unsigned int tox_group_peernumber_is_ours(const Tox *tox, int groupnumber, int peernumber)
{
const Messenger *m = tox;
return group_peernumber_is_ours(m->group_chat_object, groupnumber, peernumber);
}
/* Return the number of peers in the group chat on success.
* return -1 on failure
*/
int tox_group_number_peers(const Tox *tox, int groupnumber)
{
const Messenger *m = tox;
return group_number_peers(m->group_chat_object, groupnumber);
}
/* List all the peers in the group chat.
*
* Copies the names of the peers to the name[length][MAX_NICK_BYTES] array.
*
* Copies the lengths of the names to lengths[length]
*
* returns the number of peers on success.
*
* return -1 on failure.
*/
int tox_group_get_names(const Tox *tox, int groupnumber, uint8_t names[][TOX_MAX_NAME_LENGTH], uint16_t lengths[],
uint16_t length)
{
const Messenger *m = tox;
return group_names(m->group_chat_object, groupnumber, names, lengths, 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(const Tox *tox)
{
const Messenger *m = tox;
return count_chatlist(m->group_chat_object);
}
/* 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(const Tox *tox, int32_t *out_list, uint32_t list_size)
{
const Messenger *m = tox;
return copy_chatlist(m->group_chat_object, out_list, list_size);
}
/* return the type of groupchat (TOX_GROUPCHAT_TYPE_) that groupnumber is.
*
* return -1 on failure.
* return type on success.
*/
int tox_group_get_type(const Tox *tox, int groupnumber)
{
const Messenger *m = tox;
return group_get_type(m->group_chat_object, groupnumber);
}

2
toxencryptsave/defines.h Normal file
View File

@ -0,0 +1,2 @@
#define TOX_ENC_SAVE_MAGIC_NUMBER "toxEsave"
#define TOX_ENC_SAVE_MAGIC_LENGTH 8

View File

@ -26,8 +26,9 @@
#endif
#include "toxencryptsave.h"
#include "defines.h"
#include "../toxcore/crypto_core.h"
#include "../toxcore/tox.h"
#define SET_ERROR_PARAMETER(param, x) {if(param) {*param = x;}}
#ifdef VANILLA_NACL
#include "crypto_pwhash_scryptsalsa208sha256/crypto_pwhash_scryptsalsa208sha256.h"
@ -35,81 +36,70 @@
#include <crypto_hash_sha256.h>
#endif
#define TOX_PASS_ENCRYPTION_EXTRA_LENGTH (crypto_box_MACBYTES + crypto_box_NONCEBYTES \
+ crypto_pwhash_scryptsalsa208sha256_SALTBYTES + TOX_ENC_SAVE_MAGIC_LENGTH)
#if TOX_PASS_SALT_LENGTH != crypto_pwhash_scryptsalsa208sha256_SALTBYTES
#error TOX_PASS_SALT_LENGTH is assumed to be equal to crypto_pwhash_scryptsalsa208sha256_SALTBYTES
#endif
#define TOX_PASS_KEY_LENGTH (crypto_pwhash_scryptsalsa208sha256_SALTBYTES + crypto_box_KEYBYTES)
#if TOX_PASS_KEY_LENGTH != crypto_box_KEYBYTES
#error TOX_PASS_KEY_LENGTH is assumed to be equal to crypto_box_KEYBYTES
#endif
int tox_pass_encryption_extra_length()
{
return TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
}
#if TOX_PASS_ENCRYPTION_EXTRA_LENGTH != (crypto_box_MACBYTES + crypto_box_NONCEBYTES + crypto_pwhash_scryptsalsa208sha256_SALTBYTES + TOX_ENC_SAVE_MAGIC_LENGTH)
#error TOX_PASS_ENCRYPTION_EXTRA_LENGTH is assumed to be equal to (crypto_box_MACBYTES + crypto_box_NONCEBYTES + crypto_pwhash_scryptsalsa208sha256_SALTBYTES + TOX_ENC_SAVE_MAGIC_LENGTH)
#endif
int tox_pass_key_length()
{
return TOX_PASS_KEY_LENGTH;
}
int tox_pass_salt_length()
{
return crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
}
/* This "module" provides functions analogous to tox_load and tox_save in toxcore
* Clients should consider alerting their users that, unlike plain data, if even one bit
/* Clients should consider alerting their users that, unlike plain data, if even one bit
* becomes corrupted, the data will be entirely unrecoverable.
* Ditto if they forget their password, there is no way to recover the data.
*/
/* return size of the messenger data (for encrypted saving). */
uint32_t tox_encrypted_size(const Tox *tox)
{
return tox_size(tox) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
}
/* This retrieves the salt used to encrypt the given data, which can then be passed to
* derive_key_with_salt to produce the same key as was previously used. Any encrpyted
* data with this module can be used as input.
*
* returns -1 if the magic number is wrong
* returns 0 otherwise (no guarantee about validity of data)
* returns true if magic number matches
* success does not say anything about the validity of the data, only that data of
* the appropriate size was copied
*/
int tox_get_salt(uint8_t *data, uint8_t *salt)
bool tox_get_salt(const uint8_t *data, uint8_t *salt)
{
if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0)
return -1;
return 0;
data += TOX_ENC_SAVE_MAGIC_LENGTH;
memcpy(salt, data, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
return 0;
return 1;
}
/* Generates a secret symmetric key from the given passphrase. out_key must be at least
* TOX_PASS_KEY_LENGTH bytes long.
* Be sure to not compromise the key! Only keep it in memory, do not write to disk.
* This function is fairly cheap, but irungentoo insists that you be allowed to
* cache the result if you want, to minimize computation for repeated encryptions.
* The password is zeroed after key derivation.
* The key should only be used with the other functions in this module, as it
* includes a salt.
* Note that this function is not deterministic; to derive the same key from a
* password, you also must know the random salt that was used. See below.
*
* returns 0 on success
* returns -1 on failure
* returns true on success
*/
int tox_derive_key_from_pass(uint8_t *passphrase, uint32_t pplength, uint8_t *out_key)
bool tox_derive_key_from_pass(uint8_t *passphrase, size_t pplength, TOX_PASS_KEY *out_key,
TOX_ERR_KEY_DERIVATION *error)
{
uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
randombytes(salt, sizeof salt);
return tox_derive_key_with_salt(passphrase, pplength, salt, out_key);
return tox_derive_key_with_salt(passphrase, pplength, salt, out_key, error);
}
/* Same as above, except with use the given salt for deterministic key derivation.
* The salt must be tox_salt_length() bytes in length.
* The salt must be TOX_PASS_SALT_LENGTH bytes in length.
*/
int tox_derive_key_with_salt(uint8_t *passphrase, uint32_t pplength, uint8_t *salt, uint8_t *out_key)
bool tox_derive_key_with_salt(uint8_t *passphrase, size_t pplength, uint8_t *salt, TOX_PASS_KEY *out_key,
TOX_ERR_KEY_DERIVATION *error)
{
if (pplength == 0)
return -1;
if (pplength == 0 || !passphrase || !salt || !out_key) {
SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_NULL);
return 0;
}
uint8_t passkey[crypto_hash_sha256_BYTES];
crypto_hash_sha256(passkey, passphrase, pplength);
@ -125,27 +115,33 @@ int tox_derive_key_with_salt(uint8_t *passphrase, uint32_t pplength, uint8_t *sa
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE * 2, /* slightly stronger */
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) != 0) {
/* out of memory most likely */
return -1;
SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_FAILED);
return 0;
}
sodium_memzero(passkey, crypto_hash_sha256_BYTES); /* wipe plaintext pw */
memcpy(out_key, salt, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
memcpy(out_key + crypto_pwhash_scryptsalsa208sha256_SALTBYTES, key, crypto_box_KEYBYTES);
return 0;
memcpy(out_key->salt, salt, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
memcpy(out_key->key, key, crypto_box_KEYBYTES);
SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_OK);
return 1;
}
/* Encrypt arbitrary with a key produced by tox_derive_key_from_pass. The output
/* Encrypt arbitrary with a key produced by tox_derive_key_*. The output
* array must be at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long.
* key must be TOX_PASS_KEY_LENGTH bytes.
* If you already have a symmetric key from somewhere besides this module, simply
* call encrypt_data_symmetric in toxcore/crypto_core directly.
*
*
* returns 0 on success
* returns -1 on failure
* returns true on success
*/
int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t *key, uint8_t *out)
bool tox_pass_key_encrypt(const uint8_t *data, size_t data_len, const TOX_PASS_KEY *key, uint8_t *out,
TOX_ERR_ENCRYPTION *error)
{
if (data_len == 0 || !data || !key || !out) {
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_NULL);
return 0;
}
/* the output data consists of, in order:
* salt, nonce, mac, enc_data
* where the mac is automatically prepended by the encrypt()
@ -159,8 +155,7 @@ int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t *
out += TOX_ENC_SAVE_MAGIC_LENGTH;
/* then add the rest prefix */
memcpy(out, key, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
key += crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
memcpy(out, key->salt, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
out += crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
uint8_t nonce[crypto_box_NONCEBYTES];
@ -169,177 +164,133 @@ int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t *
out += crypto_box_NONCEBYTES;
/* now encrypt */
if (encrypt_data_symmetric(key, nonce, data, data_len, out)
if (encrypt_data_symmetric(key->key, nonce, data, data_len, out)
!= data_len + crypto_box_MACBYTES) {
return -1;
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_FAILED);
return 0;
}
return 0;
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_OK);
return 1;
}
/* Encrypts the given data with the given passphrase. The output array must be
* at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
* to tox_derive_key_from_pass and tox_pass_key_encrypt.
*
* returns 0 on success
* returns -1 on failure
* returns true on success
*/
int tox_pass_encrypt(const uint8_t *data, uint32_t data_len, uint8_t *passphrase, uint32_t pplength, uint8_t *out)
bool tox_pass_encrypt(const uint8_t *data, size_t data_len, uint8_t *passphrase, size_t pplength, uint8_t *out,
TOX_ERR_ENCRYPTION *error)
{
uint8_t key[TOX_PASS_KEY_LENGTH];
TOX_PASS_KEY key;
TOX_ERR_KEY_DERIVATION _error;
if (tox_derive_key_from_pass(passphrase, pplength, key) == -1)
return -1;
if (!tox_derive_key_from_pass(passphrase, pplength, &key, &_error)) {
if (_error == TOX_ERR_KEY_DERIVATION_NULL) {
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_NULL);
} else if (_error == TOX_ERR_KEY_DERIVATION_FAILED) {
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_KEY_DERIVATION_FAILED);
}
return tox_pass_key_encrypt(data, data_len, key, out);
}
return 0;
}
/* Save the messenger data encrypted with the given password.
* data must be at least tox_encrypted_size().
*
* returns 0 on success
* returns -1 on failure
*/
int tox_encrypted_save(const Tox *tox, uint8_t *data, uint8_t *passphrase, uint32_t pplength)
{
/* first get plain save data */
uint32_t temp_size = tox_size(tox);
uint8_t temp_data[temp_size];
tox_save(tox, temp_data);
/* now encrypt */
return tox_pass_encrypt(temp_data, temp_size, passphrase, pplength, data);
}
/* Save the messenger data encrypted with the given key from tox_derive_key.
* data must be at least tox_encrypted_size().
*
* returns 0 on success
* returns -1 on failure
*/
int tox_encrypted_key_save(const Tox *tox, uint8_t *data, uint8_t *key)
{
/* first get plain save data */
uint32_t temp_size = tox_size(tox);
uint8_t temp_data[temp_size];
tox_save(tox, temp_data);
/* encrypt */
return tox_pass_key_encrypt(temp_data, temp_size, key, data);
return tox_pass_key_encrypt(data, data_len, &key, out, error);
}
/* This is the inverse of tox_pass_key_encrypt, also using only keys produced by
* tox_derive_key_from_pass.
*
* returns the length of the output data (== data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH) on success
* returns -1 on failure
* the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
*
* returns true on success
*/
int tox_pass_key_decrypt(const uint8_t *data, uint32_t length, const uint8_t *key, uint8_t *out)
bool tox_pass_key_decrypt(const uint8_t *data, size_t length, const TOX_PASS_KEY *key, uint8_t *out,
TOX_ERR_DECRYPTION *error)
{
if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH
|| 0 != memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH))
return -1;
if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_INVALID_LENGTH);
return 0;
}
if (!data || !key || !out) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_NULL);
return 0;
}
if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_BAD_FORMAT);
return 0;
}
data += TOX_ENC_SAVE_MAGIC_LENGTH;
data += crypto_pwhash_scryptsalsa208sha256_SALTBYTES; // salt only affects key derivation
size_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint32_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
//uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
uint8_t nonce[crypto_box_NONCEBYTES];
//memcpy(salt, data, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
key += crypto_pwhash_scryptsalsa208sha256_SALTBYTES; // ignore the salt, which is only needed for kdf
data += crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
memcpy(nonce, data, crypto_box_NONCEBYTES);
data += crypto_box_NONCEBYTES;
/* decrypt the data */
if (decrypt_data_symmetric(key, nonce, data, decrypt_length + crypto_box_MACBYTES, out)
if (decrypt_data_symmetric(key->key, nonce, data, decrypt_length + crypto_box_MACBYTES, out)
!= decrypt_length) {
return -1;
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_FAILED);
return 0;
}
return decrypt_length;
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_OK);
return 1;
}
/* Decrypts the given data with the given passphrase. The output array must be
* at least data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long.
* at least data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
* to tox_pass_key_decrypt.
*
* returns the length of the output data (== data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH) on success
* returns -1 on failure
* the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
*
* returns true on success
*/
int tox_pass_decrypt(const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength, uint8_t *out)
bool tox_pass_decrypt(const uint8_t *data, size_t length, uint8_t *passphrase, size_t pplength, uint8_t *out,
TOX_ERR_DECRYPTION *error)
{
uint8_t passkey[crypto_hash_sha256_BYTES];
crypto_hash_sha256(passkey, passphrase, pplength);
if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH || pplength == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_INVALID_LENGTH);
return 0;
}
if (!data || !passphrase || !out) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_NULL);
return 0;
}
if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_BAD_FORMAT);
return 0;
}
uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
memcpy(salt, data + TOX_ENC_SAVE_MAGIC_LENGTH, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
/* derive the key */
uint8_t key[crypto_box_KEYBYTES + crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
TOX_PASS_KEY key;
if (crypto_pwhash_scryptsalsa208sha256(
key + crypto_pwhash_scryptsalsa208sha256_SALTBYTES,
crypto_box_KEYBYTES, (char *)passkey, sizeof(passkey), salt,
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE * 2, /* slightly stronger */
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) != 0) {
if (!tox_derive_key_with_salt(passphrase, pplength, salt, &key, NULL)) {
/* out of memory most likely */
return -1;
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_KEY_DERIVATION_FAILED);
return 0;
}
sodium_memzero(passkey, crypto_hash_sha256_BYTES); /* wipe plaintext pw */
return tox_pass_key_decrypt(data, length, key, out);
}
/* Load the messenger from encrypted data of size length.
*
* returns 0 on success
* returns -1 on failure
*/
int tox_encrypted_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength)
{
uint32_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t temp_data[decrypt_length];
if (tox_pass_decrypt(data, length, passphrase, pplength, temp_data)
!= decrypt_length)
return -1;
return tox_load(tox, temp_data, decrypt_length);
}
/* Load the messenger from encrypted data of size length, with key from tox_derive_key.
*
* returns 0 on success
* returns -1 on failure
*/
int tox_encrypted_key_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *key)
{
uint32_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t temp_data[decrypt_length];
if (tox_pass_key_decrypt(data, length, key, temp_data)
!= decrypt_length)
return -1;
return tox_load(tox, temp_data, decrypt_length);
return tox_pass_key_decrypt(data, length, &key, out, error);
}
/* Determines whether or not the given data is encrypted (by checking the magic number)
*
* returns 1 if it is encrypted
* returns 0 otherwise
*/
int tox_is_data_encrypted(const uint8_t *data)
bool tox_is_data_encrypted(const uint8_t *data)
{
if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) == 0)
return 1;
else
return 0;
}
int tox_is_save_encrypted(const uint8_t *data)
{
return tox_is_data_encrypted(data);
}

View File

@ -29,27 +29,20 @@ extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifndef __TOX_DEFINED__
#define __TOX_DEFINED__
#ifndef TOX_DEFINED
#define TOX_DEFINED
typedef struct Tox Tox;
struct Tox_Options;
#endif
// these functions provide access to these defines in toxencryptsave.c, which
// otherwise aren't actually available in clients...
int tox_pass_encryption_extra_length();
#define TOX_PASS_SALT_LENGTH 32
#define TOX_PASS_KEY_LENGTH 32
#define TOX_PASS_ENCRYPTION_EXTRA_LENGTH 80
int tox_pass_key_length();
int tox_pass_salt_length();
/* return size of the messenger data (for encrypted Messenger saving). */
uint32_t tox_encrypted_size(const Tox *tox);
/* This "module" provides functions analogous to tox_load and tox_save in toxcore,
* as well as functions for encryption of arbitrary client data (e.g. chat logs).
*
* It is conceptually organized into two parts. The first part are the functions
/* This module is conceptually organized into two parts. The first part are the functions
* with "key" in the name. To use these functions, first derive an encryption key
* from a password with tox_derive_key_from_pass, and use the returned key to
* encrypt the data. The second part takes the password itself instead of the key,
@ -59,13 +52,83 @@ uint32_t tox_encrypted_size(const Tox *tox);
* favor using the first part intead of the second part.
*
* The encrypted data is prepended with a magic number, to aid validity checking
* (no guarantees are made of course).
* (no guarantees are made of course). Any data to be decrypted must start with
* the magic number.
*
* Clients should consider alerting their users that, unlike plain data, if even one bit
* becomes corrupted, the data will be entirely unrecoverable.
* Ditto if they forget their password, there is no way to recover the data.
*/
/* Since apparently no one actually bothered to learn about the module previously,
* the recently removed functions tox_encrypted_new and tox_get_encrypted_savedata
* may be trivially replaced by calls to tox_pass_decrypt -> tox_new or
* tox_get_savedata -> tox_pass_encrypt as appropriate. The removed functions
* were never more than 5 line wrappers of the other public API functions anyways.
* (As has always been, tox_pass_decrypt and tox_pass_encrypt are interchangeable
* with tox_pass_key_decrypt and tox_pass_key_encrypt, as the client program requires.)
*/
typedef enum TOX_ERR_KEY_DERIVATION {
TOX_ERR_KEY_DERIVATION_OK,
/**
* Some input data, or maybe the output pointer, was null.
*/
TOX_ERR_KEY_DERIVATION_NULL,
/**
* The crypto lib was unable to derive a key from the given passphrase,
* which is usually a lack of memory issue. The functions accepting keys
* do not produce this error.
*/
TOX_ERR_KEY_DERIVATION_FAILED
} TOX_ERR_KEY_DERIVATION;
typedef enum TOX_ERR_ENCRYPTION {
TOX_ERR_ENCRYPTION_OK,
/**
* Some input data, or maybe the output pointer, was null.
*/
TOX_ERR_ENCRYPTION_NULL,
/**
* The crypto lib was unable to derive a key from the given passphrase,
* which is usually a lack of memory issue. The functions accepting keys
* do not produce this error.
*/
TOX_ERR_ENCRYPTION_KEY_DERIVATION_FAILED,
/**
* The encryption itself failed.
*/
TOX_ERR_ENCRYPTION_FAILED
} TOX_ERR_ENCRYPTION;
typedef enum TOX_ERR_DECRYPTION {
TOX_ERR_DECRYPTION_OK,
/**
* Some input data, or maybe the output pointer, was null.
*/
TOX_ERR_DECRYPTION_NULL,
/**
* The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes
*/
TOX_ERR_DECRYPTION_INVALID_LENGTH,
/**
* The input data is missing the magic number (i.e. wasn't created by this
* module, or is corrupted)
*/
TOX_ERR_DECRYPTION_BAD_FORMAT,
/**
* The crypto lib was unable to derive a key from the given passphrase,
* which is usually a lack of memory issue. The functions accepting keys
* do not produce this error.
*/
TOX_ERR_DECRYPTION_KEY_DERIVATION_FAILED,
/**
* The encrypted byte array could not be decrypted. Either the data was
* corrupt or the password/key was incorrect.
*/
TOX_ERR_DECRYPTION_FAILED
} TOX_ERR_DECRYPTION;
/******************************* BEGIN PART 2 *******************************
* For simplicty, the second part of the module is presented first. The API for
@ -75,41 +138,25 @@ uint32_t tox_encrypted_size(const Tox *tox);
*/
/* Encrypts the given data with the given passphrase. The output array must be
* at least data_len + tox_pass_encryption_extra_length() bytes long. This delegates
* at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
* to tox_derive_key_from_pass and tox_pass_key_encrypt.
*
* tox_encrypted_save() is a good example of how to use this function.
*
* returns 0 on success
* returns -1 on failure
* returns true on success
*/
int tox_pass_encrypt(const uint8_t *data, uint32_t data_len, uint8_t *passphrase, uint32_t pplength, uint8_t *out);
bool tox_pass_encrypt(const uint8_t *data, size_t data_len, uint8_t *passphrase, size_t pplength, uint8_t *out,
TOX_ERR_ENCRYPTION *error);
/* Save the messenger data encrypted with the given password.
* data must be at least tox_encrypted_size().
*
* returns 0 on success
* returns -1 on failure
*/
int tox_encrypted_save(const Tox *tox, uint8_t *data, uint8_t *passphrase, uint32_t pplength);
/* Decrypts the given data with the given passphrase. The output array must be
* at least data_len - tox_pass_encryption_extra_length() bytes long. This delegates
* at least data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
* to tox_pass_key_decrypt.
*
* tox_encrypted_load() is a good example of how to use this function.
* the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
*
* returns the length of the output data (== data_len - tox_pass_encryption_extra_length()) on success
* returns -1 on failure
* returns true on success
*/
int tox_pass_decrypt(const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength, uint8_t *out);
/* Load the messenger from encrypted data of size length.
*
* returns 0 on success
* returns -1 on failure
*/
int tox_encrypted_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength);
bool tox_pass_decrypt(const uint8_t *data, size_t length, uint8_t *passphrase, size_t pplength, uint8_t *out,
TOX_ERR_DECRYPTION *error);
/******************************* BEGIN PART 1 *******************************
@ -117,8 +164,16 @@ int tox_encrypted_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *
* intensive than part one. The first 3 functions are for key handling.
*/
/* This key structure's internals should not be used by any client program, even
* if they are straightforward here.
*/
typedef struct {
uint8_t salt[TOX_PASS_SALT_LENGTH];
uint8_t key[TOX_PASS_KEY_LENGTH];
} TOX_PASS_KEY;
/* Generates a secret symmetric key from the given passphrase. out_key must be at least
* tox_pass_key_length() bytes long.
* TOX_PASS_KEY_LENGTH bytes long.
* Be sure to not compromise the key! Only keep it in memory, do not write to disk.
* The password is zeroed after key derivation.
* The key should only be used with the other functions in this module, as it
@ -126,68 +181,53 @@ int tox_encrypted_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *
* Note that this function is not deterministic; to derive the same key from a
* password, you also must know the random salt that was used. See below.
*
* returns 0 on success
* returns -1 on failure
* returns true on success
*/
int tox_derive_key_from_pass(uint8_t *passphrase, uint32_t pplength, uint8_t *out_key);
bool tox_derive_key_from_pass(uint8_t *passphrase, size_t pplength, TOX_PASS_KEY *out_key,
TOX_ERR_KEY_DERIVATION *error);
/* Same as above, except with use the given salt for deterministic key derivation.
* The salt must be tox_salt_length() bytes in length.
/* Same as above, except use the given salt for deterministic key derivation.
* The salt must be TOX_PASS_SALT_LENGTH bytes in length.
*/
int tox_derive_key_with_salt(uint8_t *passphrase, uint32_t pplength, uint8_t *salt, uint8_t *out_key);
bool tox_derive_key_with_salt(uint8_t *passphrase, size_t pplength, uint8_t *salt, TOX_PASS_KEY *out_key,
TOX_ERR_KEY_DERIVATION *error);
/* This retrieves the salt used to encrypt the given data, which can then be passed to
* derive_key_with_salt to produce the same key as was previously used. Any encrpyted
* data with this module can be used as input.
*
* returns -1 if the magic number is wrong
* returns 0 otherwise (no guarantee about validity of data)
* returns true if magic number matches
* success does not say anything about the validity of the data, only that data of
* the appropriate size was copied
*/
int tox_get_salt(uint8_t *data, uint8_t *salt);
bool tox_get_salt(const uint8_t *data, uint8_t *salt);
/* Now come the functions that are analogous to the part 2 functions. */
/* Encrypt arbitrary with a key produced by tox_derive_key_. The output
* array must be at least data_len + tox_pass_encryption_extra_length() bytes long.
* key must be tox_pass_key_length() bytes.
/* Encrypt arbitrary with a key produced by tox_derive_key_*. The output
* array must be at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long.
* key must be TOX_PASS_KEY_LENGTH bytes.
* If you already have a symmetric key from somewhere besides this module, simply
* call encrypt_data_symmetric in toxcore/crypto_core directly.
*
* returns 0 on success
* returns -1 on failure
* returns true on success
*/
int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t *key, uint8_t *out);
/* Save the messenger data encrypted with the given key from tox_derive_key.
* data must be at least tox_encrypted_size().
*
* returns 0 on success
* returns -1 on failure
*/
int tox_encrypted_key_save(const Tox *tox, uint8_t *data, uint8_t *key);
bool tox_pass_key_encrypt(const uint8_t *data, size_t data_len, const TOX_PASS_KEY *key, uint8_t *out,
TOX_ERR_ENCRYPTION *error);
/* This is the inverse of tox_pass_key_encrypt, also using only keys produced by
* tox_derive_key_from_pass.
*
* returns the length of the output data (== data_len - tox_pass_encryption_extra_length()) on success
* returns -1 on failure
*/
int tox_pass_key_decrypt(const uint8_t *data, uint32_t length, const uint8_t *key, uint8_t *out);
/* Load the messenger from encrypted data of size length, with key from tox_derive_key.
* the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
*
* returns 0 on success
* returns -1 on failure
* returns true on success
*/
int tox_encrypted_key_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *key);
bool tox_pass_key_decrypt(const uint8_t *data, size_t length, const TOX_PASS_KEY *key, uint8_t *out,
TOX_ERR_DECRYPTION *error);
/* Determines whether or not the given data is encrypted (by checking the magic number)
*
* returns 1 if it is encrypted
* returns 0 otherwise
*/
int tox_is_data_encrypted(const uint8_t *data);
int tox_is_save_encrypted(const uint8_t *data); // poorly-named alias for backwards compat (oh irony...)
bool tox_is_data_encrypted(const uint8_t *data);
#ifdef __cplusplus
}