toxcore/auto_tests/group_invite_test.c
iphydf 895a6af122
cleanup: Remove NaCl support.
We now depend on libsodium unconditionally. Future work will require
functions from libsodium, and nobody we're aware of uses the nacl build
for anything other than making sure it still works on CI.
2023-12-25 00:36:38 +00:00

281 lines
9.7 KiB
C

/*
* Tests group invites as well as join restrictions, including password protection, privacy state,
* and peer limits. Ensures sure that the peer being blocked from joining successfully receives
* the invite fail packet with the correct message.
*
* This test also checks that many peers can successfully join the group simultaneously.
*/
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include "auto_test_support.h"
#include "check_compat.h"
typedef struct State {
uint32_t num_peers;
bool peer_limit_fail;
bool password_fail;
bool connected;
size_t messages_received;
} State;
#define NUM_GROUP_TOXES 8 // must be > 7
#define PASSWORD "dadada"
#define PASS_LEN (sizeof(PASSWORD) - 1)
#define WRONG_PASS "dadadada"
#define WRONG_PASS_LEN (sizeof(WRONG_PASS) - 1)
static bool group_has_full_graph(const AutoTox *autotoxes, uint32_t group_number, uint32_t expected_peer_count)
{
for (size_t i = 7; i < NUM_GROUP_TOXES; ++i) {
const State *state = (const State *)autotoxes[i].state;
if (state->num_peers < expected_peer_count) {
return false;
}
}
const State *state0 = (const State *)autotoxes[0].state;
const State *state1 = (const State *)autotoxes[1].state;
const State *state5 = (const State *)autotoxes[5].state;
if (state0->num_peers < expected_peer_count || state1->num_peers < expected_peer_count
|| state5->num_peers < expected_peer_count) {
return false;
}
return true;
}
static void group_join_fail_handler(Tox *tox, uint32_t group_number, Tox_Group_Join_Fail fail_type, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
switch (fail_type) {
case TOX_GROUP_JOIN_FAIL_PEER_LIMIT: {
state->peer_limit_fail = true;
break;
}
case TOX_GROUP_JOIN_FAIL_INVALID_PASSWORD: {
state->password_fail = true;
break;
}
case TOX_GROUP_JOIN_FAIL_UNKNOWN:
// intentional fallthrough
default: {
ck_assert_msg(false, "Got unknown join fail");
return;
}
}
}
static void group_self_join_handler(Tox *tox, uint32_t group_number, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
state->connected = true;
}
static void group_peer_join_handler(Tox *tox, uint32_t group_number, uint32_t peer_id, void *user_data)
{
AutoTox *autotox = (AutoTox *)user_data;
ck_assert(autotox != nullptr);
State *state = (State *)autotox->state;
++state->num_peers;
ck_assert(state->num_peers < NUM_GROUP_TOXES);
}
static void group_invite_test(AutoTox *autotoxes)
{
ck_assert_msg(NUM_GROUP_TOXES > 7, "NUM_GROUP_TOXES is too small: %d", NUM_GROUP_TOXES);
for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
tox_callback_group_peer_join(autotoxes[i].tox, group_peer_join_handler);
tox_callback_group_join_fail(autotoxes[i].tox, group_join_fail_handler);
tox_callback_group_self_join(autotoxes[i].tox, group_self_join_handler);
}
Tox *tox0 = autotoxes[0].tox;
Tox *tox1 = autotoxes[1].tox;
Tox *tox2 = autotoxes[2].tox;
Tox *tox3 = autotoxes[3].tox;
Tox *tox4 = autotoxes[4].tox;
Tox *tox5 = autotoxes[5].tox;
Tox *tox6 = autotoxes[6].tox;
State *state0 = (State *)autotoxes[0].state;
State *state2 = (State *)autotoxes[2].state;
State *state3 = (State *)autotoxes[3].state;
State *state4 = (State *)autotoxes[4].state;
State *state5 = (State *)autotoxes[5].state;
State *state6 = (State *)autotoxes[6].state;
Tox_Err_Group_New new_err;
uint32_t groupnumber = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)"test", 4,
(const uint8_t *)"test", 4, &new_err);
ck_assert_msg(new_err == TOX_ERR_GROUP_NEW_OK, "tox_group_new failed: %d", new_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
Tox_Err_Group_State_Queries id_err;
uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
tox_group_get_chat_id(tox0, groupnumber, chat_id, &id_err);
ck_assert_msg(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "%d", id_err);
// peer 1 joins public group with no password
Tox_Err_Group_Join join_err;
tox_group_join(tox1, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
while (state0->num_peers < 1) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("Peer 1 joined group\n");
// founder sets a password
Tox_Err_Group_Founder_Set_Password pass_set_err;
tox_group_founder_set_password(tox0, groupnumber, (const uint8_t *)PASSWORD, PASS_LEN, &pass_set_err);
ck_assert_msg(pass_set_err == TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK, "%d", pass_set_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, 5000);
// peer 2 attempts to join with no password
tox_group_join(tox2, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
while (!state2->password_fail) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("Peer 2 successfully blocked with no password\n");
// peer 3 attempts to join with invalid password
tox_group_join(tox3, chat_id, (const uint8_t *)"Test", 4, (const uint8_t *)WRONG_PASS, WRONG_PASS_LEN, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
while (!state3->password_fail) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("Peer 3 successfully blocked with invalid password\n");
// founder sets peer limit to 1
Tox_Err_Group_Founder_Set_Peer_Limit limit_set_err;
tox_group_founder_set_peer_limit(tox0, groupnumber, 1, &limit_set_err);
ck_assert_msg(limit_set_err == TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK, "%d", limit_set_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, 5000);
// peer 4 attempts to join with correct password
tox_group_join(tox4, chat_id, (const uint8_t *)"Test", 4, (const uint8_t *)PASSWORD, PASS_LEN, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
while (!state4->peer_limit_fail) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("Peer 4 successfully blocked from joining full group\n");
// founder removes password and increases peer limit to 100
tox_group_founder_set_password(tox0, groupnumber, nullptr, 0, &pass_set_err);
ck_assert_msg(pass_set_err == TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK, "%d", pass_set_err);
tox_group_founder_set_peer_limit(tox0, groupnumber, 100, &limit_set_err);
ck_assert_msg(limit_set_err == TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK, "%d", limit_set_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, 5000);
// peer 5 attempts to join group
tox_group_join(tox5, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
while (!state5->connected) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("Peer 5 successfully joined the group\n");
// founder makes group private
Tox_Err_Group_Founder_Set_Privacy_State priv_err;
tox_group_founder_set_privacy_state(tox0, groupnumber, TOX_GROUP_PRIVACY_STATE_PRIVATE, &priv_err);
ck_assert_msg(priv_err == TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK, "%d", priv_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, 5000);
// peer 6 attempts to join group via chat ID
tox_group_join(tox6, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
// since we don't receive a fail packet in this case we just wait a while and check if we're in the group
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, 20000);
ck_assert(!state6->connected);
printf("Peer 6 failed to join private group via chat ID\n");
// founder makes group public again
tox_group_founder_set_privacy_state(tox0, groupnumber, TOX_GROUP_PRIVACY_STATE_PUBLIC, &priv_err);
ck_assert_msg(priv_err == TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK, "%d", priv_err);
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
const uint32_t num_new_peers = NUM_GROUP_TOXES - 7;
printf("Connecting %u peers at the same time\n", num_new_peers);
for (size_t i = 7; i < NUM_GROUP_TOXES; ++i) {
tox_group_join(autotoxes[i].tox, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "%d", join_err);
}
const uint32_t expected_peer_count = num_new_peers + state0->num_peers + 1;
while (!group_has_full_graph(autotoxes, groupnumber, expected_peer_count)) {
iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
}
printf("Every peer sees every other peer\n");
for (size_t i = 0; i < NUM_GROUP_TOXES; i++) {
Tox_Err_Group_Leave err_exit;
tox_group_leave(autotoxes[i].tox, 0, nullptr, 0, &err_exit);
ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK);
}
printf("All tests passed!\n");
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
Run_Auto_Options autotest_opts = default_run_auto_options();
autotest_opts.graph = GRAPH_COMPLETE;
run_auto_test(nullptr, NUM_GROUP_TOXES, group_invite_test, sizeof(State), &autotest_opts);
return 0;
}
#undef NUM_GROUP_TOXES
#undef PASSWORD
#undef PASS_LEN
#undef WRONG_PASS
#undef WRONG_PASS_LEN