diff --git a/.circleci/cmake-asan b/.circleci/cmake-asan index cc1434b175..ba183a0018 100755 --- a/.circleci/cmake-asan +++ b/.circleci/cmake-asan @@ -21,6 +21,7 @@ cmake -B_build -H. -GNinja \ -DSTRICT_ABI=ON \ -DTEST_TIMEOUT_SECONDS=120 \ -DUSE_IPV6=OFF \ + -DUSE_TEST_NETWORK=ON \ -DAUTOTEST=ON cd _build diff --git a/.circleci/cmake-tsan b/.circleci/cmake-tsan index b42e8f1806..f0cc750ec9 100755 --- a/.circleci/cmake-tsan +++ b/.circleci/cmake-tsan @@ -21,6 +21,7 @@ cmake -B_build -H. -GNinja \ -DSTRICT_ABI=ON \ -DTEST_TIMEOUT_SECONDS=120 \ -DUSE_IPV6=OFF \ + -DUSE_TEST_NETWORK=ON \ -DAUTOTEST=ON cd _build diff --git a/.cirrus.yml b/.cirrus.yml index ab863125de..081407df13 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,7 +1,7 @@ --- cirrus-ci_task: container: - image: toxchat/toktok-stack:0.0.7 + image: toxchat/toktok-stack:0.0.10 cpu: 2 memory: 2G configure_script: @@ -10,6 +10,6 @@ cirrus-ci_task: - bazel test -k --remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST --config=ci - --config=docker --config=release + --config=testnet //c-toxcore/... diff --git a/.gitignore b/.gitignore index 2586b9a0ae..06869a82c5 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,5 @@ cscope.files # rpm tox.spec + +.idea/ diff --git a/.travis/autotools-linux b/.travis/autotools-linux index 96c110ab5d..da7411dbc7 100755 --- a/.travis/autotools-linux +++ b/.travis/autotools-linux @@ -31,6 +31,7 @@ travis_script() { add_config_flag --with-nacl-libs="$CACHEDIR/lib/amd64" add_config_flag --with-nacl-headers="$CACHEDIR/include/amd64" add_config_flag --disable-ipv6 + add_config_flag --enable-test-network add_config_flag --enable-nacl add_config_flag --enable-daemon add_config_flag --enable-logging diff --git a/.travis/cmake-linux b/.travis/cmake-linux index 50adf569e8..c0ab2da42c 100755 --- a/.travis/cmake-linux +++ b/.travis/cmake-linux @@ -81,6 +81,7 @@ travis_script() { -DSTRICT_ABI=ON \ -DTEST_TIMEOUT_SECONDS=120 \ -DUSE_IPV6=OFF \ + -DUSE_TEST_NETWORK=ON \ -DAUTOTEST=ON cmake --build _build --parallel "$NPROC" --target install -- -k 0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d446f71bf..e820421d85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -545,15 +545,4 @@ if (BUILD_MISC_TESTS) testing/afl_toxsave.c) target_link_modules(afl_toxsave toxcore) - add_executable(group_announce_test ${CPUFEATURES} - testing/groupchats/group_announce_test.c) - target_link_modules(group_announce_test toxcore misc_tools) - - add_executable(group_bootstrap_n_chat ${CPUFEATURES} - testing/groupchats/group_bootstrap_n_chat.c) - target_link_modules(group_bootstrap_n_chat toxcore misc_tools) - - add_executable(group_newpeers_dos_attack ${CPUFEATURES} - testing/groupchats/group_newpeers_dos_attack.c) - target_link_modules(group_newpeers_dos_attack toxcore misc_tools) endif() diff --git a/auto_tests/TCP_test.c b/auto_tests/TCP_test.c index 9206e265d9..8016d412ee 100644 --- a/auto_tests/TCP_test.c +++ b/auto_tests/TCP_test.c @@ -122,7 +122,8 @@ START_TEST(test_basic) memcpy(f_nonce_r, response_plain + CRYPTO_SHARED_KEY_SIZE, CRYPTO_NONCE_SIZE); // Building a request - uint8_t r_req_p[1 + CRYPTO_PUBLIC_KEY_SIZE] = {0}; + uint8_t r_req_p[1 + CRYPTO_PUBLIC_KEY_SIZE]; + r_req_p[0] = TCP_PACKET_ROUTING_REQUEST; memcpy(r_req_p + 1, f_public_key, CRYPTO_PUBLIC_KEY_SIZE); uint8_t r_req[2 + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE]; uint16_t size = 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE; @@ -162,7 +163,8 @@ START_TEST(test_basic) ck_assert_msg(ret != -1, "Failed to decrypt the TCP server's response."); increment_nonce(f_nonce_r); - ck_assert_msg(packet_resp_plain[0] == 1, "Server sent the wrong packet id: %u", packet_resp_plain[0]); + ck_assert_msg(packet_resp_plain[0] == TCP_PACKET_ROUTING_RESPONSE, "Server sent the wrong packet id: %u", + packet_resp_plain[0]); ck_assert_msg(packet_resp_plain[1] == 0, "Server did not refuse the connection."); ck_assert_msg(public_key_cmp(packet_resp_plain + 2, f_public_key) == 0, "Server sent the wrong public key."); @@ -284,7 +286,7 @@ START_TEST(test_some) struct sec_TCP_con *con3 = new_TCP_con(tcp_s, mono_time); uint8_t requ_p[1 + CRYPTO_PUBLIC_KEY_SIZE]; - requ_p[0] = 0; + requ_p[0] = TCP_PACKET_ROUTING_REQUEST; // Sending wrong public keys to test server response. memcpy(requ_p + 1, con3->public_key, CRYPTO_PUBLIC_KEY_SIZE); @@ -298,14 +300,14 @@ START_TEST(test_some) uint8_t data[2048]; int len = read_packet_sec_TCP(con1, data, 2 + 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE); ck_assert_msg(len == 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE, "Wrong response packet length of %d.", len); - ck_assert_msg(data[0] == 1, "Wrong response packet id of %d.", data[0]); + ck_assert_msg(data[0] == TCP_PACKET_ROUTING_RESPONSE, "Wrong response packet id of %d.", data[0]); ck_assert_msg(data[1] == 16, "Server didn't refuse connection using wrong public key."); ck_assert_msg(public_key_cmp(data + 2, con3->public_key) == 0, "Key in response packet wrong."); // Connection 3 len = read_packet_sec_TCP(con3, data, 2 + 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE); ck_assert_msg(len == 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE, "Wrong response packet length of %d.", len); - ck_assert_msg(data[0] == 1, "Wrong response packet id of %d.", data[0]); + ck_assert_msg(data[0] == TCP_PACKET_ROUTING_RESPONSE, "Wrong response packet id of %d.", data[0]); ck_assert_msg(data[1] == 16, "Server didn't refuse connection using wrong public key."); ck_assert_msg(public_key_cmp(data + 2, con1->public_key) == 0, "Key in response packet wrong."); @@ -319,11 +321,11 @@ START_TEST(test_some) len = read_packet_sec_TCP(con1, data, 2 + 2 + CRYPTO_MAC_SIZE); ck_assert_msg(len == 2, "wrong len %d", len); - ck_assert_msg(data[0] == 2, "wrong packet id %u", data[0]); + ck_assert_msg(data[0] == TCP_PACKET_CONNECTION_NOTIFICATION, "wrong packet id %u", data[0]); ck_assert_msg(data[1] == 16, "wrong peer id %u", data[1]); len = read_packet_sec_TCP(con3, data, 2 + 2 + CRYPTO_MAC_SIZE); ck_assert_msg(len == 2, "wrong len %d", len); - ck_assert_msg(data[0] == 2, "wrong packet id %u", data[0]); + ck_assert_msg(data[0] == TCP_PACKET_CONNECTION_NOTIFICATION, "wrong packet id %u", data[0]); ck_assert_msg(data[1] == 16, "wrong peer id %u", data[1]); len = read_packet_sec_TCP(con1, data, 2 + sizeof(test_packet) + CRYPTO_MAC_SIZE); ck_assert_msg(len == sizeof(test_packet), "wrong len %d", len); @@ -354,14 +356,14 @@ START_TEST(test_some) ck_assert_msg(memcmp(data, test_packet, sizeof(test_packet)) == 0, "packet is wrong %u %u %u %u", data[0], data[1], data[sizeof(test_packet) - 2], data[sizeof(test_packet) - 1]); - uint8_t ping_packet[1 + sizeof(uint64_t)] = {4, 8, 6, 9, 67}; + uint8_t ping_packet[1 + sizeof(uint64_t)] = {TCP_PACKET_PING, 8, 6, 9, 67}; write_packet_TCP_secure_connection(con1, ping_packet, sizeof(ping_packet)); do_TCP_server_delay(tcp_s, mono_time, 50); len = read_packet_sec_TCP(con1, data, 2 + sizeof(ping_packet) + CRYPTO_MAC_SIZE); ck_assert_msg(len == sizeof(ping_packet), "wrong len %d", len); - ck_assert_msg(data[0] == 5, "wrong packet id %u", data[0]); + ck_assert_msg(data[0] == TCP_PACKET_PONG, "wrong packet id %u", data[0]); ck_assert_msg(memcmp(ping_packet + 1, data + 1, sizeof(uint64_t)) == 0, "wrong packet data"); // Kill off the connections diff --git a/auto_tests/group_announce_test.c b/auto_tests/group_announce_test.c index 3daa223f75..eb3332a547 100644 --- a/auto_tests/group_announce_test.c +++ b/auto_tests/group_announce_test.c @@ -19,13 +19,17 @@ typedef struct State { #include "run_auto_test.h" +#define TEST_MESSAGE "The kiosk in my temporal lobe is shaped like Rosalynn Carter" +#define TEST_GROUP_NAME "NASA Headquarters" +#define PEER0_NICK "Lois" +#define PEER1_NICK "Benjamin" + static void group_invite_handler(Tox *tox, uint32_t friend_number, const uint8_t *invite_data, size_t length, - void *user_data) + const uint8_t *group_name, size_t group_name_length, void *user_data) { - printf("invite arrived; accepting\n"); - TOX_ERR_GROUP_INVITE_ACCEPT err_accept; - tox_group_invite_accept(tox, invite_data, length, nullptr, 0, &err_accept); - ck_assert(err_accept == TOX_ERR_GROUP_INVITE_ACCEPT_OK); + if (tox != nullptr) { + ck_abort_msg("we should not get invited"); + } } static const char *tox_str_group_join_fail(TOX_GROUP_JOIN_FAIL v) @@ -62,9 +66,18 @@ static void group_peer_join_handler(Tox *tox, uint32_t groupnumber, uint32_t pee static void group_message_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type, const uint8_t *message, size_t length, void *user_data) { + if (length > TOX_MAX_MESSAGE_LENGTH) { + printf("Failed to receive message. Invalid length: %zu\n", length); + return; + } + + char message_buf[TOX_MAX_MESSAGE_LENGTH + 1]; + memcpy(message_buf, message, length); + message_buf[length] = 0; + State *state = (State *)user_data; - printf("peer %u sent message: %s\n", peer_id, (const char *)message); - ck_assert(memcmp(message, "hello", 6) == 0); + printf("peer %u sent message: %s\n", peer_id, (const char *)message_buf); + ck_assert(memcmp(message_buf, TEST_MESSAGE, length) == 0); state->message_received = true; } @@ -72,19 +85,18 @@ static void group_message_test(Tox **toxes, State *state) { tox_self_set_name(toxes[0], (const uint8_t *)"a", 1, nullptr); tox_self_set_name(toxes[1], (const uint8_t *)"b", 1, nullptr); - tox_self_set_name(toxes[2], (const uint8_t *)"c", 1, nullptr); - tox_callback_group_invite(toxes[1], group_invite_handler, nullptr); - tox_callback_group_join_fail(toxes[1], group_join_fail_handler, nullptr); - tox_callback_group_peer_join(toxes[1], group_peer_join_handler, nullptr); - tox_callback_group_message(toxes[0], group_message_handler, nullptr); + tox_callback_group_invite(toxes[1], group_invite_handler); + tox_callback_group_join_fail(toxes[1], group_join_fail_handler); + tox_callback_group_peer_join(toxes[1], group_peer_join_handler); + tox_callback_group_message(toxes[0], group_message_handler); // tox0 makes new group. TOX_ERR_GROUP_NEW err_new; uint32_t group_number = tox_group_new( toxes[0], TOX_GROUP_PRIVACY_STATE_PUBLIC, - (const uint8_t *)"my cool group", strlen("my cool group"), &err_new); + (const uint8_t *) TEST_GROUP_NAME, strlen(TEST_GROUP_NAME), (const uint8_t *)PEER0_NICK, strlen(PEER0_NICK), &err_new); ck_assert(err_new == TOX_ERR_GROUP_NEW_OK); // get the chat id of the new group. @@ -93,25 +105,21 @@ static void group_message_test(Tox **toxes, State *state) tox_group_get_chat_id(toxes[0], group_number, chat_id, &err_id); ck_assert(err_id == TOX_ERR_GROUP_STATE_QUERIES_OK); - // tox1 and tox2 joins it. + // tox1 joins it. TOX_ERR_GROUP_JOIN err_join; - tox_group_join(toxes[1], chat_id, nullptr, 0, &err_join); - ck_assert(err_join == TOX_ERR_GROUP_JOIN_OK); - - tox_group_join(toxes[2], chat_id, nullptr, 0, &err_join); + tox_group_join(toxes[1], chat_id, (const uint8_t *)PEER1_NICK, strlen(PEER1_NICK), nullptr, 0, &err_join); ck_assert(err_join == TOX_ERR_GROUP_JOIN_OK); while (!state[0].message_received) { - iterate_all_wait(3, toxes, state, ITERATION_INTERVAL); + iterate_all_wait(2, toxes, state, ITERATION_INTERVAL); if (state[1].peer_joined && !state[1].message_sent) { TOX_ERR_GROUP_SEND_MESSAGE err_send; - tox_group_send_message(toxes[1], group_number, TOX_MESSAGE_TYPE_NORMAL, (const uint8_t *)"hello", 6, &err_send); + tox_group_send_message(toxes[1], group_number, TOX_MESSAGE_TYPE_NORMAL, (const uint8_t *)TEST_MESSAGE, + strlen(TEST_MESSAGE), &err_send); ck_assert(err_send == TOX_ERR_GROUP_SEND_MESSAGE_OK); state[1].message_sent = true; } - - c_sleep(ITERATION_INTERVAL); } TOX_ERR_GROUP_LEAVE err_exit; @@ -120,15 +128,17 @@ static void group_message_test(Tox **toxes, State *state) tox_group_leave(toxes[1], group_number, nullptr, 0, &err_exit); ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK); - - tox_group_leave(toxes[2], group_number, nullptr, 0, &err_exit); - ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK); } +#undef PEER1_NICK +#undef PEER0_NICK +#undef TEST_GROUP_NAME +#undef TEST_MESSAGE + int main(void) { setvbuf(stdout, nullptr, _IONBF, 0); - run_auto_test(3, group_message_test, false); + run_auto_test(2, group_message_test, false); return 0; } diff --git a/auto_tests/group_message_test.c b/auto_tests/group_message_test.c index deef62f004..cbdde118e2 100644 --- a/auto_tests/group_message_test.c +++ b/auto_tests/group_message_test.c @@ -19,12 +19,18 @@ typedef struct State { #include "run_auto_test.h" +#define TEST_MESSAGE "Where is it I've read that someone condemned to death says or thinks, an hour before his death, that if he had to live on some high rock, on such a narrow ledge that he'd only room to stand, and the ocean, everlasting darkness, everlasting solitude, everlasting tempest around him, if he had to remain standing on a square yard of space all his life, a thousand years, eternity, it were better to live so than to die at once. Only to live, to live and live! Life, whatever it may be!" +#define TEST_GROUP_NAME "Utah Data Center" +#define PEER0_NICK "Victor" +#define PEER1_NICK "George" + static void group_invite_handler(Tox *tox, uint32_t friend_number, const uint8_t *invite_data, size_t length, - void *user_data) + const uint8_t *group_name, size_t group_name_length, void *user_data) { printf("invite arrived; accepting\n"); TOX_ERR_GROUP_INVITE_ACCEPT err_accept; - tox_group_invite_accept(tox, invite_data, length, nullptr, 0, &err_accept); + tox_group_invite_accept(tox, friend_number, invite_data, length, (const uint8_t *)PEER0_NICK, strlen(PEER0_NICK), + nullptr, 0, &err_accept); ck_assert(err_accept == TOX_ERR_GROUP_INVITE_ACCEPT_OK); } @@ -62,9 +68,44 @@ static void group_peer_join_handler(Tox *tox, uint32_t groupnumber, uint32_t pee static void group_message_handler(Tox *tox, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type, const uint8_t *message, size_t length, void *user_data) { + if (length > TOX_MAX_MESSAGE_LENGTH) { + printf("Failed to receive message. Invalid length: %zu\n", length); + return; + } + + char message_buf[TOX_MAX_MESSAGE_LENGTH + 1]; + memcpy(message_buf, message, length); + message_buf[length] = 0; + + TOX_ERR_GROUP_PEER_QUERY q_err; + size_t peer_name_len = tox_group_peer_get_name_size(tox, groupnumber, peer_id, &q_err); + + ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK); + ck_assert(peer_name_len <= TOX_MAX_NAME_LENGTH); + + char peer_name[TOX_MAX_NAME_LENGTH + 1]; + tox_group_peer_get_name(tox, groupnumber, peer_id, (uint8_t *) peer_name, &q_err); + peer_name[peer_name_len] = 0; + + ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK); + ck_assert(memcmp(peer_name, PEER1_NICK, peer_name_len)); + + TOX_ERR_GROUP_SELF_QUERY s_err; + size_t self_name_len = tox_group_self_get_name_size(tox, groupnumber, &s_err); + ck_assert(s_err == TOX_ERR_GROUP_SELF_QUERY_OK); + ck_assert(self_name_len <= TOX_MAX_NAME_LENGTH); + + char self_name[TOX_MAX_NAME_LENGTH + 1]; + tox_group_self_get_name(tox, groupnumber, (uint8_t *) self_name, &s_err); + self_name[self_name_len] = 0; + + ck_assert(s_err == TOX_ERR_GROUP_SELF_QUERY_OK); + ck_assert(memcmp(self_name, PEER0_NICK, self_name_len)); + + printf("%s sent message to %s: %s\n", peer_name, self_name, message_buf); + ck_assert(memcmp(message_buf, TEST_MESSAGE, length) == 0); + State *state = (State *)user_data; - printf("peer %u sent message: %s\n", peer_id, (const char *)message); - ck_assert(memcmp(message, "hello", 6) == 0); state->message_received = true; } @@ -73,17 +114,17 @@ static void group_message_test(Tox **toxes, State *state) tox_self_set_name(toxes[0], (const uint8_t *)"a", 1, nullptr); tox_self_set_name(toxes[1], (const uint8_t *)"b", 1, nullptr); - tox_callback_group_invite(toxes[1], group_invite_handler, nullptr); - tox_callback_group_join_fail(toxes[1], group_join_fail_handler, nullptr); - tox_callback_group_peer_join(toxes[1], group_peer_join_handler, nullptr); - tox_callback_group_message(toxes[0], group_message_handler, nullptr); + tox_callback_group_invite(toxes[1], group_invite_handler); + tox_callback_group_join_fail(toxes[1], group_join_fail_handler); + tox_callback_group_peer_join(toxes[1], group_peer_join_handler); + tox_callback_group_message(toxes[0], group_message_handler); // tox0 makes new group. TOX_ERR_GROUP_NEW err_new; uint32_t group_number = tox_group_new( toxes[0], TOX_GROUP_PRIVACY_STATE_PRIVATE, - (const uint8_t *)"my cool group", strlen("my cool group"), &err_new); + (const uint8_t *)TEST_GROUP_NAME, strlen(TEST_GROUP_NAME), (const uint8_t *)PEER1_NICK, strlen(PEER1_NICK), &err_new); ck_assert(err_new == TOX_ERR_GROUP_NEW_OK); // tox0 invites tox1 @@ -97,7 +138,8 @@ static void group_message_test(Tox **toxes, State *state) if (state[1].peer_joined && !state[1].message_sent) { TOX_ERR_GROUP_SEND_MESSAGE err_send; - tox_group_send_message(toxes[1], group_number, TOX_MESSAGE_TYPE_NORMAL, (const uint8_t *)"hello", 6, &err_send); + tox_group_send_message(toxes[1], group_number, TOX_MESSAGE_TYPE_NORMAL, (const uint8_t *)TEST_MESSAGE, + strlen(TEST_MESSAGE), &err_send); ck_assert(err_send == TOX_ERR_GROUP_SEND_MESSAGE_OK); state[1].message_sent = true; } @@ -112,6 +154,11 @@ static void group_message_test(Tox **toxes, State *state) } } +#undef PEER1_NICK +#undef PEER0_NICK +#undef TEST_GROUP_NAME +#undef TEST_MESSAGE + int main(void) { setvbuf(stdout, nullptr, _IONBF, 0); diff --git a/auto_tests/groupchat_test.c b/auto_tests/groupchat_test.c index 2ac8151f03..d8630a0935 100644 --- a/auto_tests/groupchat_test.c +++ b/auto_tests/groupchat_test.c @@ -28,6 +28,8 @@ #define GROUP_NAME "The Gas Chamber" #define GROUP_NAME_LEN (sizeof(GROUP_NAME) - 1) +#define PEER0_NICK "David" + /* Returns 0 if group state is equal to the state passed to this function. * Returns negative integer if state is invalid. */ @@ -143,9 +145,9 @@ START_TEST(test_text_all) ck_assert_msg(error == TOX_ERR_NEW_OK, "tox_new failed to bootstrap: %d\n", error); - size_t i, count = 0; + size_t count = 0; - for (i = 1; i < NUM_GROUP_TOXES; ++i) { + for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) { index[i] = i + 1; toxes[i] = tox_new_log(&tox_opts, &error, &index[i]); ck_assert_msg(error == TOX_ERR_NEW_OK, "tox_new failed: %d\n", error); @@ -161,13 +163,13 @@ START_TEST(test_text_all) } while (1) { - for (i = 0; i < NUM_GROUP_TOXES; ++i) { + for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { tox_iterate(toxes[i], nullptr); } count = 0; - for (i = 0; i < NUM_GROUP_TOXES; ++i) { + for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { if (tox_self_get_connection_status(toxes[i])) { ++count; } @@ -184,48 +186,52 @@ START_TEST(test_text_all) /* Tox1 creates a group and is a founder of a newly created group */ TOX_ERR_GROUP_NEW new_err; - uint32_t groupnum = tox_group_new(toxes[1], TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)GROUP_NAME, GROUP_NAME_LEN, - &new_err); + uint32_t groupnum = tox_group_new(toxes[0], TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)GROUP_NAME, GROUP_NAME_LEN, + (const uint8_t *)PEER0_NICK, strlen(PEER0_NICK), &new_err); + ck_assert_msg(new_err == TOX_ERR_GROUP_NEW_OK, "tox_group_new failed: %d", new_err); /* Set default group state */ - set_group_state(toxes[1], 0, PEER_LIMIT_1, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)PASSWORD, PASS_LEN, + set_group_state(toxes[0], groupnum, PEER_LIMIT_1, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)PASSWORD, PASS_LEN, (const uint8_t *)TOPIC1, TOPIC1_LEN); - for (i = 0; i < NUM_GROUP_TOXES; ++i) { + for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { tox_iterate(toxes[i], nullptr); } /* Tox1 gets the Chat ID and implicitly shares it publicly */ TOX_ERR_GROUP_STATE_QUERIES id_err; uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE]; - tox_group_get_chat_id(toxes[1], groupnum, chat_id, &id_err); + tox_group_get_chat_id(toxes[0], groupnum, chat_id, &id_err); ck_assert_msg(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "tox_group_get_chat_id failed %d", id_err); /* All other peers join the group using the Chat ID and password */ - for (i = 2; i < NUM_GROUP_TOXES; ++i) { + for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) { + char nick[TOX_MAX_NAME_LENGTH + 1]; + snprintf(nick, sizeof(nick), "Follower%zu", i); TOX_ERR_GROUP_JOIN join_err; - tox_group_join(toxes[i], chat_id, (const uint8_t *)PASSWORD, PASS_LEN, &join_err); + tox_group_join(toxes[i], chat_id, (const uint8_t *)nick, strlen(nick), (const uint8_t *)PASSWORD, PASS_LEN, + &join_err); ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "tox_group_join failed: %d", join_err); c_sleep(1000); } /* Keep checking if all instances have connected to the group until test times out */ while (1) { - for (i = 0; i < NUM_GROUP_TOXES; ++i) { + for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { tox_iterate(toxes[i], nullptr); } count = 0; - for (i = 1; i < NUM_GROUP_TOXES; ++i) { + for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { if (tox_group_get_peer_limit(toxes[i], 0, nullptr) == PEER_LIMIT_1) { ++count; } } - if (count == NUM_GROUP_TOXES - 1) { + if (count == NUM_GROUP_TOXES) { break; } @@ -233,7 +239,7 @@ START_TEST(test_text_all) } /* Check that all peers have the correct group state */ - for (i = 1; i < NUM_GROUP_TOXES; ++i) { + for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { tox_iterate(toxes[i], nullptr); int ret = check_group_state(toxes[i], 0, PEER_LIMIT_1, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)PASSWORD, PASS_LEN, (const uint8_t *)TOPIC1, TOPIC1_LEN); @@ -242,36 +248,35 @@ START_TEST(test_text_all) } /* Change group state and check that all peers received the changes */ - set_group_state(toxes[1], 0, PEER_LIMIT_2, TOX_GROUP_PRIVACY_STATE_PRIVATE, nullptr, 0, (const uint8_t *)TOPIC2, + set_group_state(toxes[0], groupnum, PEER_LIMIT_2, TOX_GROUP_PRIVACY_STATE_PRIVATE, nullptr, 0, (const uint8_t *)TOPIC2, TOPIC2_LEN); while (1) { count = 0; - for (i = 1; i < NUM_GROUP_TOXES; ++i) { + for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { tox_iterate(toxes[i], nullptr); - if (check_group_state(toxes[i], 0, PEER_LIMIT_2, TOX_GROUP_PRIVACY_STATE_PRIVATE, nullptr, 0, + if (check_group_state(toxes[i], groupnum, PEER_LIMIT_2, TOX_GROUP_PRIVACY_STATE_PRIVATE, nullptr, 0, (const uint8_t *)TOPIC2, TOPIC2_LEN) == 0) { ++count; } } - if (count == NUM_GROUP_TOXES - 1) { + if (count == NUM_GROUP_TOXES) { break; } c_sleep(20); } - for (i = 0; i < NUM_GROUP_TOXES; ++i) { + for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { TOX_ERR_GROUP_LEAVE err_exit; tox_group_leave(toxes[i], groupnum, nullptr, 0, &err_exit); - // TODO(JFreegman): Fix? - // ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK); + ck_assert_msg(err_exit == TOX_ERR_GROUP_LEAVE_OK, "%d", err_exit); } - for (i = 0; i < NUM_GROUP_TOXES; ++i) { + for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { tox_kill(toxes[i]); } @@ -287,6 +292,25 @@ static Suite *text_groupchats_suite(void) return s; } +#undef PEER0_NICK + +#undef GROUP_NAME_LEN +#undef GROUP_NAME + +#undef TOPIC2_LEN +#undef TOPIC2 + +#undef TOPIC1_LEN +#undef TOPIC1 + +#undef PASS_LEN +#undef PASSWORD + +#undef PEER_LIMIT_2 +#undef PEER_LIMIT_1 + +#undef NUM_GROUP_TOXES + int main(void) { srand((unsigned int) time(nullptr)); diff --git a/auto_tests/onion_test.c b/auto_tests/onion_test.c index 20c7ff8655..bcd091b1b4 100644 --- a/auto_tests/onion_test.c +++ b/auto_tests/onion_test.c @@ -42,12 +42,22 @@ static int handle_test_1(void *object, IP_Port source, const uint8_t *packet, ui { Onion *onion = (Onion *)object; - if (memcmp(packet, "\x83 Install Gentoo", sizeof("\x83 Install Gentoo")) != 0) { + const char req_message[] = "Install Gentoo"; + uint8_t req_packet[1 + sizeof(req_message)]; + req_packet[0] = NET_PACKET_ANNOUNCE_REQUEST; + memcpy(req_packet + 1, req_message, sizeof(req_message)); + + if (memcmp(packet, req_packet, sizeof(req_packet)) != 0) { return 1; } - if (send_onion_response(onion->net, source, (const uint8_t *)"\x84 install gentoo", sizeof("\x84 install gentoo"), - packet + sizeof("\x84 install gentoo")) == -1) { + const char res_message[] = "install gentoo"; + uint8_t res_packet[1 + sizeof(res_message)]; + res_packet[0] = NET_PACKET_ANNOUNCE_RESPONSE; + memcpy(res_packet + 1, res_message, sizeof(res_message)); + + if (send_onion_response(onion->net, source, res_packet, sizeof(res_packet), + packet + sizeof(res_packet)) == -1) { return 1; } @@ -58,11 +68,16 @@ static int handle_test_1(void *object, IP_Port source, const uint8_t *packet, ui static int handled_test_2; static int handle_test_2(void *object, IP_Port source, const uint8_t *packet, uint16_t length, void *userdata) { - if (length != sizeof("\x84 install gentoo")) { + const char res_message[] = "install gentoo"; + uint8_t res_packet[1 + sizeof(res_message)]; + res_packet[0] = NET_PACKET_ANNOUNCE_RESPONSE; + memcpy(res_packet + 1, res_message, sizeof(res_message)); + + if (length != sizeof(res_packet)) { return 1; } - if (memcmp(packet, (const uint8_t *)"\x84 install gentoo", sizeof("\x84 install gentoo")) != 0) { + if (memcmp(packet, res_packet, sizeof(res_packet)) != 0) { return 1; } @@ -89,19 +104,18 @@ static int handle_test_3(void *object, IP_Port source, const uint8_t *packet, ui { Onion *onion = (Onion *)object; - if (length != (1 + CRYPTO_NONCE_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + 1 + CRYPTO_SHA256_SIZE + - CRYPTO_MAC_SIZE)) { + if (length < ONION_ANNOUNCE_RESPONSE_MIN_SIZE || length > ONION_ANNOUNCE_RESPONSE_MAX_SIZE) { return 1; } - uint8_t plain[1 + CRYPTO_SHA256_SIZE]; + uint8_t plain[2 + CRYPTO_SHA256_SIZE]; #if 0 print_client_id(packet, length); #endif int len = decrypt_data(test_3_pub_key, dht_get_self_secret_key(onion->dht), packet + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH, packet + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE, - 1 + CRYPTO_SHA256_SIZE + CRYPTO_MAC_SIZE, plain); + 2 + CRYPTO_SHA256_SIZE + CRYPTO_MAC_SIZE, plain); if (len == -1) { return 1; @@ -179,6 +193,11 @@ static void test_basic(void) memcpy(n2.public_key, dht_get_self_public_key(onion2->dht), CRYPTO_PUBLIC_KEY_SIZE); n2.ip_port = on2; + const char req_message[] = "Install Gentoo"; + uint8_t req_packet[1 + sizeof(req_message)]; + req_packet[0] = NET_PACKET_ANNOUNCE_REQUEST; + memcpy(req_packet + 1, req_message, sizeof(req_message)); + Node_format nodes[4]; nodes[0] = n1; nodes[1] = n2; @@ -186,8 +205,7 @@ static void test_basic(void) nodes[3] = n2; Onion_Path path; create_onion_path(onion1->dht, &path, nodes); - int ret = send_onion_packet(onion1->net, &path, nodes[3].ip_port, (const uint8_t *)"\x83 Install Gentoo", - sizeof("\x83 Install Gentoo")); + int ret = send_onion_packet(onion1->net, &path, nodes[3].ip_port, req_packet, sizeof(req_packet)); ck_assert_msg(ret == 0, "Failed to create/send onion packet."); handled_test_1 = 0; @@ -205,8 +223,9 @@ static void test_basic(void) do_onion(onion2); } while (handled_test_2 == 0); - Onion_Announce *onion1_a = new_onion_announce(mono_time1, onion1->dht); - Onion_Announce *onion2_a = new_onion_announce(mono_time2, onion2->dht); + GC_Announces_List unused_var; + Onion_Announce *onion1_a = new_onion_announce(mono_time1, onion1->dht, &unused_var); + Onion_Announce *onion2_a = new_onion_announce(mono_time2, onion2->dht, &unused_var); networking_registerhandler(onion1->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_test_3, onion1); ck_assert_msg((onion1_a != nullptr) && (onion2_a != nullptr), "Onion_Announce failed initializing."); uint8_t zeroes[64] = {0}; @@ -377,7 +396,8 @@ static Onions *new_onions(uint16_t port, uint32_t *index) return nullptr; } - on->onion_a = new_onion_announce(on->mono_time, dht); + GC_Announces_List unused_var1; + on->onion_a = new_onion_announce(on->mono_time, dht, &unused_var1); if (!on->onion_a) { kill_onion(on->onion); @@ -389,8 +409,9 @@ static Onions *new_onions(uint16_t port, uint32_t *index) return nullptr; } + GC_Session unused_var2; TCP_Proxy_Info inf = {{{{0}}}}; - on->onion_c = new_onion_client(on->mono_time, new_net_crypto(on->log, on->mono_time, dht, &inf)); + on->onion_c = new_onion_client(on->mono_time, new_net_crypto(on->log, on->mono_time, dht, &inf), &unused_var2); if (!on->onion_c) { kill_onion_announce(on->onion_a); diff --git a/auto_tests/tcp_relay_test.c b/auto_tests/tcp_relay_test.c index fcb1fdefe4..7d18b775bf 100644 --- a/auto_tests/tcp_relay_test.c +++ b/auto_tests/tcp_relay_test.c @@ -7,13 +7,20 @@ #include "../testing/misc_tools.h" #include "check_compat.h" -static uint8_t const key[] = { +static uint8_t const key1[] = { 0x3F, 0x0A, 0x45, 0xA2, 0x68, 0x36, 0x7C, 0x1B, 0xEA, 0x65, 0x2F, 0x25, 0x8C, 0x85, 0xF4, 0xA6, 0x6D, 0xA7, 0x6B, 0xCA, 0xA6, 0x67, 0xA4, 0x9E, 0x77, 0x0B, 0xCC, 0x49, 0x17, 0xAB, 0x6A, 0x25, }; +static uint8_t const key2[] = { + 0x79, 0xCA, 0xDA, 0x49, 0x74, 0xB0, 0x92, 0x6F, + 0x28, 0x6F, 0x02, 0x5C, 0xD5, 0xFF, 0xDF, 0x3E, + 0x65, 0x4A, 0x37, 0x58, 0xC5, 0x3E, 0x02, 0x73, + 0xEC, 0xFC, 0x4D, 0x12, 0xC2, 0x1D, 0xCA, 0x48, +}; + int main(void) { setvbuf(stdout, nullptr, _IONBF, 0); @@ -23,10 +30,17 @@ int main(void) Tox *tox_tcp = tox_new_log(opts, nullptr, nullptr); tox_options_free(opts); - tox_bootstrap(tox_tcp, "tox.initramfs.io", 33445, key, nullptr); + // TODO(iphydf): Why do we need to bootstrap in addition to adding TCP + // relays? + tox_bootstrap(tox_tcp, "tox.initramfs.io", 33445, key1, nullptr); + tox_bootstrap(tox_tcp, "172.93.52.70", 33445, key2, nullptr); Tox_Err_Bootstrap tcp_err; - tox_add_tcp_relay(tox_tcp, "tox.initramfs.io", 33445, key, &tcp_err); + tox_add_tcp_relay(tox_tcp, "tox.initramfs.io", 33445, key1, &tcp_err); + ck_assert_msg(tcp_err == TOX_ERR_BOOTSTRAP_OK, + "attempting to add tcp relay returned with an error: %d", + tcp_err); + tox_add_tcp_relay(tox_tcp, "172.93.52.70", 33445, key2, &tcp_err); ck_assert_msg(tcp_err == TOX_ERR_BOOTSTRAP_OK, "attempting to add tcp relay returned with an error: %d", tcp_err); diff --git a/cmake/ApiDsl.cmake b/cmake/ApiDsl.cmake index 759f7f05db..6577474acd 100644 --- a/cmake/ApiDsl.cmake +++ b/cmake/ApiDsl.cmake @@ -8,7 +8,7 @@ find_program(APIDSL NAMES apidsl apidsl.native apidsl.byte - ${CMAKE_SOURCE_DIR}/../apidsl/apigen.native) + ${CMAKE_SOURCE_DIR}/../apidsl/apigen.exe) find_program(ASTYLE NAMES astyle $ENV{ASTYLE}) diff --git a/configure.ac b/configure.ac index 5dfaf22c75..71b5389315 100644 --- a/configure.ac +++ b/configure.ac @@ -172,6 +172,16 @@ if test "$use_ipv6" != "yes"; then AC_DEFINE([USE_IPV6],[0],[define to 0 to force ipv4]) fi +AC_ARG_ENABLE([[test_network]], + [AS_HELP_STRING([[--enable-test-network[=ARG]]], [build tox for a test network incompatible with the main DHT [no]])], + [use_test_network=${enableval}], + [use_test_network='no'] + ) + +if test "$use_test_network" == "yes"; then + AC_DEFINE([USE_TEST_NETWORK],[1],[define to 1 to enable the test network]) +fi + AX_HAVE_EPOLL if test "$enable_epoll" != "no"; then if test "${ax_cv_have_epoll}" = "yes"; then diff --git a/other/DHT_bootstrap.c b/other/DHT_bootstrap.c index 49ad801312..f726ac8e48 100644 --- a/other/DHT_bootstrap.c +++ b/other/DHT_bootstrap.c @@ -143,7 +143,8 @@ int main(int argc, char *argv[]) Mono_Time *mono_time = mono_time_new(); DHT *dht = new_dht(logger, mono_time, new_networking(logger, ip, PORT), true); Onion *onion = new_onion(mono_time, dht); - Onion_Announce *onion_a = new_onion_announce(mono_time, dht); + GC_Announces_List *gc_announces_list = new_gca_list(); + Onion_Announce *onion_a = new_onion_announce(mono_time, dht, gc_announces_list); #ifdef DHT_NODE_EXTRA_PACKETS bootstrap_set_callbacks(dht_get_net(dht), DHT_VERSION_NUMBER, DHT_MOTD, sizeof(DHT_MOTD)); diff --git a/other/analysis/check_recursion b/other/analysis/check_recursion index 9185695e6c..210635276c 100755 --- a/other/analysis/check_recursion +++ b/other/analysis/check_recursion @@ -101,5 +101,4 @@ def find_recursion(expected): find_recursion(expected={ "add_to_closest -> add_to_closest", "add_to_list -> add_to_list", - "sanctions_list_add_entry -> sanctions_list_fix_ban_id -> sanctions_list_add_entry", }) diff --git a/other/bootstrap_daemon/src/tox-bootstrapd.c b/other/bootstrap_daemon/src/tox-bootstrapd.c index 2bef50e95c..0db5579ca1 100644 --- a/other/bootstrap_daemon/src/tox-bootstrapd.c +++ b/other/bootstrap_daemon/src/tox-bootstrapd.c @@ -341,7 +341,8 @@ int main(int argc, char *argv[]) return 1; } - Onion_Announce *onion_a = new_onion_announce(mono_time, dht); + GC_Announces_List *gc_announces_list = new_gca_list(); + Onion_Announce *onion_a = new_onion_announce(mono_time, dht, gc_announces_list); if (!onion_a) { log_write(LOG_LEVEL_ERROR, "Couldn't initialize Tox Onion Announce. Exiting.\n"); diff --git a/testing/BUILD.bazel b/testing/BUILD.bazel index 3a812dc1ae..5a2a019400 100644 --- a/testing/BUILD.bazel +++ b/testing/BUILD.bazel @@ -49,30 +49,3 @@ cc_binary( "//c-toxcore/toxcore", ], ) - -cc_binary( - name = "group_announce_test", - srcs = ["groupchats/group_announce_test.c"], - deps = [ - ":misc_tools", - "//c-toxcore/toxcore", - ], -) - -cc_binary( - name = "group_bootstrap_n_chat", - srcs = ["groupchats/group_bootstrap_n_chat.c"], - deps = [ - ":misc_tools", - "//c-toxcore/toxcore", - ], -) - -cc_binary( - name = "group_newpeers_dos_attack", - srcs = ["groupchats/group_newpeers_dos_attack.c"], - deps = [ - ":misc_tools", - "//c-toxcore/toxcore", - ], -) diff --git a/testing/groupchats/group_announce_test.c b/testing/groupchats/group_announce_test.c deleted file mode 100644 index 0e3d029098..0000000000 --- a/testing/groupchats/group_announce_test.c +++ /dev/null @@ -1,154 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later - * Copyright © 2016-2020 The TokTok team. - * Copyright © 2015 Tox project. - */ - -/* Basic group announcing testing */ - -#include "../../toxcore/DHT.h" -#include "../../toxcore/tox.h" -#include "../../toxcore/network.h" -#include "../../toxcore/ping.h" -#include "../../toxcore/util.h" -#include "../../toxcore/group_announce.h" -#include "../../toxcore/Messenger.h" -#include "../misc_tools.h" - -#include -#include -#include - -#define min(a,b) ((a)>(b)?(b):(a)) - -/* You can change those but be mindful */ -#define PEERCOUNT 20 - -typedef struct Peer { - uint32_t index; - Mono_Time *mono_time; - Messenger *tox; - uint8_t pk[EXT_PUBLIC_KEY]; - uint8_t sk[EXT_SECRET_KEY]; -} Peer; - - -static void idle_cycle(Peer *peers, int peercount, void *userdata) -{ - for (int i = 0; i < peercount; ++i) { - do_messenger(peers[i].tox, userdata); - } -} - -static void idle_n_secs(int n, Peer *peers, int peercount, void *userdata) -{ - for (int i = 0; i < n * 1000; i += 50) { /* msecs */ - idle_cycle(peers, peercount, userdata); - c_sleep(500); /* millis */ - } -} - -static void bootstrap(Peer *peers) -{ - IP_Port *root; - int32_t count = net_getipport("localhost", &root, TOX_SOCK_DGRAM); - assert(count != -1); - - for (int i = 0; i < PEERCOUNT; ++i) { - Messenger *target = peers[i >= (PEERCOUNT - 1) ? 0 : i + 1].tox; - uint16_t bootstrap_port = net_port(dht_get_net(target->dht)); - const uint8_t *bootstrap_key = dht_get_self_public_key(target->dht); - - for (int32_t ip = 0; ip < count; ++ip) { - root[ip].port = bootstrap_port; - dht_bootstrap(peers[i].tox->dht, root[ip], bootstrap_key); - } - } - - net_freeipport(root); -} - -static void basicannouncetest(void) -{ - Peer peers[PEERCOUNT]; - - Messenger_Options options = {0}; - options.ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; - options.port_range[0] = 33445; - options.port_range[1] = 33545; - - printf("DHT public keys:\n"); - - for (int i = 0; i < PEERCOUNT; ++i) { - options.log_callback = (logger_cb *)print_debug_log; - options.log_context = peers[i].tox; - options.log_user_data = &peers[i].index; - - peers[i].index = i + 1; - peers[i].mono_time = mono_time_new(); - peers[i].tox = new_messenger(peers[i].mono_time, &options, nullptr); - create_extended_keypair(peers[i].pk, peers[i].sk); - printf("%s, %d\n", id_toa(dht_get_self_public_key(peers[i].tox->dht)), i); - } - - printf("Bootstrapping everybody from each other\n"); - bootstrap(peers); - - printf("Waiting until every Tox is connected\n"); - - for (;;) { - idle_cycle(peers, PEERCOUNT, nullptr); - - int numconnected = 0; - - for (int i = 0; i < PEERCOUNT; ++i) { - numconnected += dht_isconnected(peers[i].tox->dht); - } - - if (numconnected == PEERCOUNT * min(PEERCOUNT - 1, LCLIENT_LIST)) { - break; - } - - /* TODO: busy wait might be slightly more efficient here */ - c_sleep(500); /* millis */ - } - - printf("Network is connected\n"); - - uint8_t group_pk[EXT_PUBLIC_KEY]; - uint8_t group_sk[EXT_SECRET_KEY]; - - create_extended_keypair(group_pk, group_sk); - - int res; - printf("Sending announce requests\n"); - res = gca_send_announce_request(peers[0].tox->group_handler->announce, peers[0].pk, - peers[0].sk, group_pk); - printf("Announced node: %s\n", id_toa(peers[0].pk)); - - - printf("Number of sent announce requests %d\n", res); - idle_n_secs(10, peers, PEERCOUNT, nullptr); - - printf("Sending get announced nodes requests\n"); - res = gca_send_get_nodes_request(peers[1].tox->group_handler->announce, peers[1].pk, - peers[1].sk, group_pk); - printf("Number of sent get announced nodes requests %d\n", res); - idle_n_secs(10, peers, PEERCOUNT, nullptr); - - printf("Getting announced nodes\n"); - - GC_Announce_Node nodes[10 * 4]; - int num_nodes = gca_get_requested_nodes(peers[1].tox->group_handler->announce, group_pk, nodes); - - printf("Number of announced nodes %d\n", num_nodes); - - for (int i = 0; i < num_nodes; ++i) { - printf("Announced node: %s\n", id_toa(nodes[i].public_key)); - } -} - -int main(void) -{ - basicannouncetest(); - return 0; -} diff --git a/testing/groupchats/group_bootstrap_n_chat.c b/testing/groupchats/group_bootstrap_n_chat.c deleted file mode 100644 index efb368752d..0000000000 --- a/testing/groupchats/group_bootstrap_n_chat.c +++ /dev/null @@ -1,120 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later - * Copyright © 2016-2020 The TokTok team. - * Copyright © 2015 Tox project. - */ - -/* Basic group chats testing */ - -#include "../../toxcore/DHT.h" -#include "../../toxcore/network.h" -#include "../../toxcore/ping.h" -#include "../../toxcore/util.h" -#include "../../toxcore/Messenger.h" -#include "../misc_tools.h" - -#include -#include -#include - -#define PEERCOUNT 20 - -static void on_group_peer_join(Messenger *m, uint32_t groupnumber, uint32_t peernumber, void *userdata) -{ - GC_Chat *ct = gc_get_group(m->group_handler, groupnumber); - printf("Number of peers in the chat: %u\n", ct->numpeers); -} - -int main(int argc, char *argv[]) -{ - /* Set ip to IPv6 loopback. TODO: IPv4 fallback? */ - IP localhost; - ip_init(&localhost, 1); - localhost.ip.v6.uint8[15] = 1; - Messenger_Options options = {0}; - options.ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; - - uint32_t index[PEERCOUNT]; - Mono_Time *mono_times[PEERCOUNT]; - Messenger *tox[PEERCOUNT]; - Mono_Time *mono_time = mono_time_new(); - Messenger *chat = new_messenger(mono_time, &options, nullptr); - assert(chat != nullptr); - - for (int i = 0; i < PEERCOUNT; ++i) { - options.log_callback = (logger_cb *)print_debug_log; - options.log_context = tox[i]; - options.log_user_data = &index[i]; - - index[i] = i + 1; - mono_times[i] = mono_time_new(); - tox[i] = new_messenger(mono_times[i], &options, nullptr); - assert(tox[i] != nullptr); - } - - printf("%s\n", id_toa(dht_get_self_public_key(tox[0]->dht))); - IP_Port ip_port; - ip_copy(&ip_port.ip, &localhost); - ip_port.port = net_port(dht_get_net(tox[0]->dht)); - char buf[IP_NTOA_LEN]; - printf("%s\n", ip_ntoa(&ip_port.ip, buf, sizeof(buf))); - printf("%d\n", ip_port.port); - - printf("Bootstrapping from node\n"); - - for (int i = 1; i < PEERCOUNT; ++i) { - dht_bootstrap(tox[0]->dht, ip_port, dht_get_self_public_key(tox[0]->dht)); - } - - dht_bootstrap(chat->dht, ip_port, dht_get_self_public_key(tox[0]->dht)); - - printf("Waiting until every Tox is connected\n"); - - while (true) { - for (int i = 0; i < PEERCOUNT; ++i) { - do_messenger(tox[i], nullptr); - } - - do_messenger(chat, nullptr); - - int numconnected = 0; - - for (int i = 0; i < PEERCOUNT; ++i) { - numconnected += dht_isconnected(tox[i]->dht); - } - -#if 0 - printf("%d\n", numconnected); -#endif - - if (numconnected > PEERCOUNT * min_s32(PEERCOUNT - 1, LCLIENT_LIST)) { - break; - } - - /* TODO: busy wait might be slightly more efficient here */ - c_sleep(50); /* millis */ - } - - printf("Network is connected\n"); - - chat->group_handler = new_dht_groupchats(chat); - int groupnumber = gc_group_add(chat->group_handler, 0, (const uint8_t *)"Test", 4); - - if (groupnumber < 0) { - printf("Cannot create group\n"); - } - - GC_Chat *ct = gc_get_group(chat->group_handler, groupnumber); - printf("CHAT ENC: %s\n CHAT SIG: %s\n", id_toa(get_enc_key(ct->chat_public_key)), - id_toa(get_sig_pk(ct->chat_public_key))); - - gc_callback_peer_join(chat, on_group_peer_join, nullptr); - - while (true) { - for (int i = 0; i < PEERCOUNT; ++i) { - do_messenger(tox[i], nullptr); - } - - do_messenger(chat, nullptr); - c_sleep(500); /* millis */ - } -} diff --git a/testing/groupchats/group_newpeers_dos_attack.c b/testing/groupchats/group_newpeers_dos_attack.c deleted file mode 100644 index ea01506841..0000000000 --- a/testing/groupchats/group_newpeers_dos_attack.c +++ /dev/null @@ -1,117 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later - * Copyright © 2016-2020 The TokTok team. - * Copyright © 2015 Tox project. - */ - -/* Basic group chats testing */ - -#include "../../toxcore/DHT.h" -#include "../../toxcore/tox.h" -#include "../../toxcore/network.h" -#include "../../toxcore/ping.h" -#include "../../toxcore/util.h" -#include "../../toxcore/group_chats.h" -#include "../../toxcore/Messenger.h" -#include "../misc_tools.h" - -#include -#include -#include - -#define min(a,b) ((a)>(b)?(b):(a)) -#define PEERCOUNT 20 - - -static void do_messenger_cycle(Messenger **peers, int peercount, void *userdata) -{ - for (int i = 0; i < peercount; ++i) { - do_messenger(peers[i], userdata); - } -} - -static void idle_n_secs(int n, Messenger **peers, int peercount, void *userdata) -{ - for (int i = 0; i < n * 1000; i += 50) { /* msecs */ - do_messenger_cycle(peers, peercount, userdata); - c_sleep(500); /* millis */ - } -} - -int main(int argc, char *argv[]) -{ - Messenger *tox[PEERCOUNT]; - - Messenger_Options options = {0}; - options.ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; - - printf("DHT public keys:\n"); - - for (int i = 0; i < PEERCOUNT; ++i) { - Mono_Time *mono_time = mono_time_new(); - tox[i] = new_messenger(mono_time, &options, nullptr); - char nick[32]; - snprintf(nick, sizeof(nick), "Botik %d", rand()); - setname(tox[i], (const uint8_t *)nick, strlen(nick)); - printf("nick: %s\n", nick); - printf("%s, %d\n", id_toa(dht_get_self_public_key(tox[i]->dht)), i); - } - - printf("Bootstrapping from node\n"); - - int argvoffset = cmdline_parsefor_ipv46(argc, argv, &options.ipv6enabled); - uint16_t port = atoi(argv[argvoffset + 2]); - unsigned char *bnode_dht_key = hex_string_to_bin(argv[argvoffset + 3]); - printf("%s\n", id_toa(bnode_dht_key)); - printf("%s\n", argv[argvoffset + 1]); - printf("%d\n", port); - - - for (int i = 0; i < PEERCOUNT; ++i) { - int res = dht_bootstrap_from_address(tox[i]->dht, argv[argvoffset + 1], options.ipv6enabled, port, bnode_dht_key); - - if (!res) { - printf("Bootstrap failed\n"); - } - } - - - printf("Waiting until every Tox instance is connected\n"); - - for (;;) { - do_messenger_cycle(tox, PEERCOUNT, nullptr); - - int numconnected = 0; - - for (int i = 0; i < PEERCOUNT; ++i) { - numconnected += dht_isconnected(tox[i]->dht); - } - - printf("%d\n", numconnected); - - if (numconnected >= PEERCOUNT * min(PEERCOUNT - 1, LCLIENT_LIST)) { - break; - } - - /* TODO: busy wait might be slightly more efficient here */ - c_sleep(500); /* millis */ - } - - idle_n_secs(10, tox, PEERCOUNT, nullptr); - printf("Network is connected\n"); - unsigned char *chatid = hex_string_to_bin(argv[argvoffset + 4]); - printf("Joining groupchat %s\n", id_toa(chatid)); - - for (int i = 0; i < PEERCOUNT; ++i) { - do_messenger_cycle(tox, PEERCOUNT, nullptr); - int res = gc_group_join(tox[i]->group_handler, chatid, nullptr, 0); - idle_n_secs(1, tox, PEERCOUNT, nullptr); // comment this out to spam invites as fast as possible - - if (res < 0) { - printf("Get nodes request failed\n"); - } - } - - idle_n_secs(240, tox, PEERCOUNT, nullptr); - - return 0; -} diff --git a/testing/misc_tools.c b/testing/misc_tools.c index 8ab23db93e..e60b97aa35 100644 --- a/testing/misc_tools.c +++ b/testing/misc_tools.c @@ -36,6 +36,7 @@ #include "../toxcore/ccompat.h" #include "../toxcore/tox.h" +#include "../toxcore/util.h" void c_sleep(uint32_t x) { @@ -70,6 +71,13 @@ uint8_t *hex_string_to_bin(const char *hex_string) return ret; } +// You are responsible for freeing the return value! +char *id_toa(const uint8_t *id) +{ + char *str = (char *)malloc(IDSTRING_LEN); + return id_to_string(id, str, IDSTRING_LEN); +} + void to_hex(char *out, uint8_t *in, int size) { while (size--) { @@ -165,10 +173,14 @@ static const char *tox_log_level_name(Tox_Log_Level level) void print_debug_log(Tox *m, Tox_Log_Level level, const char *file, uint32_t line, const char *func, const char *message, void *user_data) { +#if 1 + if (level == TOX_LOG_LEVEL_TRACE) { return; } +#endif + uint32_t index = user_data ? *(uint32_t *)user_data : 0; fprintf(stderr, "[#%u] %s %s:%u\t%s:\t%s\n", index, tox_log_level_name(level), file, line, func, message); } diff --git a/testing/misc_tools.h b/testing/misc_tools.h index b9c3ca3e36..53d2e28739 100644 --- a/testing/misc_tools.h +++ b/testing/misc_tools.h @@ -13,6 +13,7 @@ extern "C" { void c_sleep(uint32_t x); uint8_t *hex_string_to_bin(const char *hex_string); +char *id_toa(const uint8_t *id); void to_hex(char *out, uint8_t *in, int size); int tox_strncasecmp(const char *s1, const char *s2, size_t n); int cmdline_parsefor_ipv46(int argc, char **argv, bool *ipv6enabled); diff --git a/toxav/rtp.c b/toxav/rtp.c index 69aed2dc05..44c5937084 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c @@ -336,11 +336,11 @@ static void update_bwc_values(const Logger *log, RTPSession *session, const stru * find out and handle it appropriately. * * @param session The current RTP session with: - * + * @code * session->mcb == vc_queue_message() // this function is called from here * session->mp == struct RTPMessage * * session->cs == call->video.second // == VCSession created by vc_new() call - * + * @endcode * @param header The RTP header deserialised from the packet. * @param incoming_data The packet data *not* header, i.e. this is the actual * payload. diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel index 0340bac50b..1577bb95bd 100644 --- a/toxcore/BUILD.bazel +++ b/toxcore/BUILD.bazel @@ -215,58 +215,43 @@ cc_library( cc_library( name = "onion_announce", - srcs = ["onion_announce.c"], - hdrs = ["onion_announce.h"], - deps = [":onion"], -) - -cc_library( - name = "onion_client", - srcs = ["onion_client.c"], - hdrs = ["onion_client.h"], - deps = [ - ":net_crypto", - ":onion_announce", + srcs = [ + "group_announce.c", + "onion_announce.c", ], -) - -cc_library( - name = "friend_connection", - srcs = ["friend_connection.c"], - hdrs = ["friend_connection.h"], - deps = [ - ":DHT", - ":net_crypto", - ":onion_client", + hdrs = [ + "group_announce.h", + "onion_announce.h", ], -) - -cc_library( - name = "friend_requests", - srcs = ["friend_requests.c"], - hdrs = ["friend_requests.h"], - deps = [":friend_connection"], + deps = [":onion"], ) cc_library( name = "Messenger", srcs = [ "Messenger.c", + "friend_connection.c", + "friend_requests.c", "group_announce.c", "group_chats.c", "group_connection.c", "group_moderation.c", + "onion_client.c", ], hdrs = [ "Messenger.h", + "friend_connection.h", + "friend_requests.h", "group_announce.h", "group_chats.h", "group_connection.h", "group_moderation.h", + "onion_client.h", ], visibility = ["//c-toxcore/toxav:__pkg__"], deps = [ - ":friend_requests", + ":net_crypto", + ":onion_announce", ":state", ], ) diff --git a/toxcore/DHT.c b/toxcore/DHT.c index 7525e695e8..d316acb48a 100644 --- a/toxcore/DHT.c +++ b/toxcore/DHT.c @@ -46,31 +46,6 @@ /* Number of get node requests to send to quickly find close nodes. */ #define MAX_BOOTSTRAP_TIMES 5 -typedef struct DHT_Friend_Callback { - dht_ip_cb *ip_callback; - void *data; - int32_t number; -} DHT_Friend_Callback; - -struct DHT_Friend { - uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; - Client_data client_list[MAX_FRIEND_CLIENTS]; - - /* Time at which the last get_nodes request was sent. */ - uint64_t lastgetnode; - /* number of times get_node packets were sent. */ - uint32_t bootstrap_times; - - /* Symmetric NAT hole punching stuff. */ - NAT nat; - - uint16_t lock_count; - DHT_Friend_Callback callbacks[DHT_FRIEND_MAX_LOCKS]; - - Node_format to_bootstrap[MAX_SENT_NODES]; - unsigned int num_to_bootstrap; -}; - typedef struct Cryptopacket_Handler { cryptopacket_handler_cb *function; void *object; @@ -2992,3 +2967,34 @@ bool dht_non_lan_connected(const DHT *dht) return false; } + +/* Copies your own ip_port structure to dest. + * + * Return 0 on success. + * Return -1 on failure. + */ +int ipport_self_copy(const DHT *dht, IP_Port *dest) +{ + for (size_t i = 0; i < LCLIENT_LIST; ++i) { + const Client_data *client = dht_get_close_client(dht, i); + const IP_Port *ip_port4 = &client->assoc4.ret_ip_port; + + if (ipport_isset(ip_port4)) { + ipport_copy(dest, ip_port4); + break; + } + + const IP_Port *ip_port6 = &client->assoc6.ret_ip_port; + + if (ipport_isset(ip_port6)) { + ipport_copy(dest, ip_port6); + break; + } + } + + if (!ipport_isset(dest)) { + return -1; + } + + return 0; +} diff --git a/toxcore/DHT.h b/toxcore/DHT.h index fb22d0cb6f..d88aedc337 100644 --- a/toxcore/DHT.h +++ b/toxcore/DHT.h @@ -425,4 +425,32 @@ bool dht_non_lan_connected(const DHT *dht); uint32_t addto_lists(DHT *dht, IP_Port ip_port, const uint8_t *public_key); +/* Copies your own ip_port structure to dest. */ +int ipport_self_copy(const DHT *dht, IP_Port *dest); + +typedef struct DHT_Friend_Callback { + dht_ip_cb *ip_callback; + void *data; + int32_t number; +} DHT_Friend_Callback; + +struct DHT_Friend { + uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; + Client_data client_list[MAX_FRIEND_CLIENTS]; + + /* Time at which the last get_nodes request was sent. */ + uint64_t lastgetnode; + /* number of times get_node packets were sent. */ + uint32_t bootstrap_times; + + /* Symmetric NAT hole punching stuff. */ + NAT nat; + + uint16_t lock_count; + DHT_Friend_Callback callbacks[DHT_FRIEND_MAX_LOCKS]; + + Node_format to_bootstrap[MAX_SENT_NODES]; + unsigned int num_to_bootstrap; +}; + #endif diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index 62e1f35cbf..4ce3cf3945 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -25,6 +25,10 @@ #include "network.h" #include "state.h" #include "util.h" +#include "group_chats.h" +#include "group_moderation.h" +#include "onion_client.h" +#include "DHT.h" static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data, uint32_t length, uint8_t congestion_control); @@ -35,6 +39,11 @@ bool friend_is_valid(const Messenger *m, int32_t friendnumber) return (unsigned int)friendnumber < m->numfriends && m->friendlist[friendnumber].status != 0; } +static bool group_is_valid(const Messenger *m, int32_t groupnumber) +{ + return (unsigned int)groupnumber < m->numgroups && m->grouplist[groupnumber].active; +} + /* Set the size of the friend list to numfriends. * * return -1 if realloc fails. @@ -57,6 +66,28 @@ static int realloc_friendlist(Messenger *m, uint32_t num) return 0; } +/* Set the size of the group list to numfriends. + * + * return -1 if realloc fails. + */ +static int realloc_grouplist(Messenger *m, uint32_t num) +{ + if (num == 0) { + free(m->grouplist); + m->grouplist = nullptr; + return 0; + } + + Group *newgrouplist = (Group *)realloc(m->grouplist, num * sizeof(Group)); + + if (newgrouplist == nullptr) { + return -1; + } + + m->grouplist = newgrouplist; + return 0; +} + /* return the friend id associated to that public key. * return -1 if no such friend. */ @@ -75,6 +106,24 @@ int32_t getfriend_id(const Messenger *m, const uint8_t *real_pk) return -1; } +/* return the group id associated with real_pk. + * return -1 if group doesn't exit. + */ +static int32_t get_group_id(const Messenger *m, const uint8_t *real_pk) +{ + uint32_t i; + + for (i = 0; i < m->numgroups; ++i) { + if (m->grouplist[i].active) { + if (id_equal(real_pk, m->grouplist[i].real_pk)) { + return i; + } + } + } + + return -1; +} + /* Copies the public key associated to that friend id into real_pk buffer. * Make sure that real_pk is of size CRYPTO_PUBLIC_KEY_SIZE. * @@ -110,9 +159,8 @@ static uint16_t address_checksum(const uint8_t *address, uint32_t len) { uint8_t checksum[2] = {0}; uint16_t check; - uint32_t i; - for (i = 0; i < len; ++i) { + for (uint32_t i = 0; i < len; ++i) { checksum[i % 2] ^= address[i]; } @@ -133,15 +181,11 @@ void getaddress(const Messenger *m, uint8_t *address) memcpy(address + CRYPTO_PUBLIC_KEY_SIZE + sizeof(nospam), &checksum, sizeof(checksum)); } -static int send_online_packet(Messenger *m, int32_t friendnumber) +static int send_online_packet(Messenger *m, int friendcon_id) { - if (!friend_is_valid(m, friendnumber)) { - return 0; - } - uint8_t packet = PACKET_ID_ONLINE; - return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, - m->friendlist[friendnumber].friendcon_id), &packet, sizeof(packet), 0) != -1; + return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, friendcon_id), &packet, + sizeof(packet), 0) != -1; } static int send_offline_packet(Messenger *m, int friendcon_id) @@ -197,7 +241,7 @@ static int32_t init_new_friend(Messenger *m, const uint8_t *real_pk, uint8_t sta } if (friend_con_connected(m->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED) { - send_online_packet(m, i); + send_online_packet(m, friendcon_id); } return i; @@ -207,6 +251,47 @@ static int32_t init_new_friend(Messenger *m, const uint8_t *real_pk, uint8_t sta return FAERR_NOMEM; } +static int32_t init_new_group(Messenger *m, const uint8_t *real_pk) +{ + if (realloc_grouplist(m, m->numgroups + 1) != 0) { + return FAERR_NOMEM; + } + + memset(&m->grouplist[m->numgroups], 0, sizeof(Group)); + + int friendcon_id = new_friend_connection(m->fr_c, real_pk); + + if (friendcon_id == -1) { + return FAERR_NOMEM; + } + + uint32_t i; + + for (i = 0; i <= m->numgroups; ++i) { + if (m->grouplist[i].active) { + continue; + } + + m->grouplist[i].active = true; + m->grouplist[i].friendcon_id = friendcon_id; + id_copy(m->grouplist[i].real_pk, real_pk); + friend_connection_callbacks(m->fr_c, friendcon_id, MESSENGER_CALLBACK_INDEX, &m_handle_status, &m_handle_packet, + &m_handle_lossy_packet, m, i); + + if (m->numgroups == i) { + ++m->numgroups; + } + + if (friend_con_connected(m->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED) { + send_online_packet(m, friendcon_id); + } + + return i; + } + + return FAERR_NOMEM; +} + /* * Add a friend. * Set the data that will be sent along with friend request. @@ -283,12 +368,21 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u return ret; } -int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk) +static int32_t m_add_friend_contact_no_request(Messenger *m, const uint8_t *real_pk) { if (getfriend_id(m, real_pk) != -1) { return FAERR_ALREADYSENT; } + if (id_equal(real_pk, nc_get_self_public_key(m->net_crypto))) { + return FAERR_OWNKEY; + } + + return init_new_friend(m, real_pk, FRIEND_CONFIRMED); +} + +int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk) +{ if (!public_key_valid(real_pk)) { return FAERR_BADCHECKSUM; } @@ -297,7 +391,48 @@ int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk) return FAERR_OWNKEY; } - return init_new_friend(m, real_pk, FRIEND_CONFIRMED); + return m_add_friend_contact_no_request(m, real_pk); +} + +static void try_pack_gc_data(const Messenger *m, GC_Chat *chat, Onion_Friend *onion_friend); + +/* + * Add a group chat to messenger. + * + * Return group_number on success. + * Return -1 on failure. + */ +int32_t m_add_group(Messenger *m, GC_Chat *chat) +{ + random_bytes(chat->m_group_public_key, CRYPTO_PUBLIC_KEY_SIZE); + + int group_number = init_new_group(m, chat->m_group_public_key); + + if (group_number < 0) { + return -1; + } + + Group *g = &m->grouplist[group_number]; + int friend_connection_id = g->friendcon_id; + Friend_Conn *connection = &m->fr_c->conns[friend_connection_id]; + int onion_friend_number = connection->onion_friendnum; + Onion_Friend *onion_friend = &m->onion_c->friends_list[onion_friend_number]; + memcpy(onion_friend->gc_public_key, get_chat_id(chat->chat_public_key), ENC_PUBLIC_KEY); + + try_pack_gc_data(m, chat, onion_friend); + + return group_number; +} + +int32_t m_remove_group(Messenger *m, const GC_Chat *chat) +{ + int group_number = get_group_id(m, chat->m_group_public_key); + + if (group_number >= 0) { + m_delgroup(m, group_number); + } + + return group_number; } static int clear_receipts(Messenger *m, int32_t friendnumber) @@ -434,6 +569,45 @@ int m_delfriend(Messenger *m, int32_t friendnumber) return 0; } +/* Remove a group. + * + * Return 0 if success. + * Return -1 if failure. + */ +int m_delgroup(Messenger *m, int32_t groupnumber) +{ + if (!group_is_valid(m, groupnumber)) { + return -1; + } + + remove_request_received(m->fr, m->grouplist[groupnumber].real_pk); + friend_connection_callbacks(m->fr_c, m->grouplist[groupnumber].friendcon_id, MESSENGER_CALLBACK_INDEX, nullptr, + nullptr, nullptr, nullptr, 0); + + if (friend_con_connected(m->fr_c, m->grouplist[groupnumber].friendcon_id) == FRIENDCONN_STATUS_CONNECTED) { + send_offline_packet(m, m->grouplist[groupnumber].friendcon_id); + } + + kill_friend_connection(m->fr_c, m->grouplist[groupnumber].friendcon_id); + memset(&m->grouplist[groupnumber], 0, sizeof(Group)); + + uint32_t i; + + for (i = m->numgroups; i != 0; --i) { + if (m->grouplist[i - 1].active) { + break; + } + } + + m->numgroups = i; + + if (realloc_grouplist(m, m->numgroups) != 0) { + return FAERR_NOMEM; + } + + return 0; +} + int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber) { if (!friend_is_valid(m, friendnumber)) { @@ -462,6 +636,30 @@ int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber) return CONNECTION_NONE; } +static int m_get_group_connectionstatus(const Messenger *m, int32_t groupnumber) +{ + if (!group_is_valid(m, groupnumber)) { + return -1; + } + + bool direct_connected = 0; + unsigned int num_online_relays = 0; + int crypt_conn_id = friend_connection_crypt_connection_id(m->fr_c, m->grouplist[groupnumber].friendcon_id); + + // FIXME(sudden6): handle return value + crypto_connection_status(m->net_crypto, crypt_conn_id, &direct_connected, &num_online_relays); + + if (direct_connected) { + return CONNECTION_UDP; + } + + if (num_online_relays) { + return CONNECTION_TCP; + } + + return CONNECTION_UNKNOWN; +} + int m_friend_exists(const Messenger *m, int32_t friendnumber) { if (!friend_is_valid(m, friendnumber)) { @@ -912,6 +1110,27 @@ static void check_friend_tcp_udp(Messenger *m, int32_t friendnumber, void *userd m->friendlist[friendnumber].last_connection_udp_tcp = ret; } +static void check_group_tcp_udp(Messenger *m, int32_t groupnumber, void *userdata) +{ + int last_connection_udp_tcp = m->grouplist[groupnumber].last_connection_udp_tcp; + + int ret = m_get_group_connectionstatus(m, groupnumber); + + if (ret == -1) { + return; + } + + if (ret == CONNECTION_UNKNOWN) { + if (last_connection_udp_tcp == CONNECTION_UDP) { + return; + } + + ret = CONNECTION_TCP; + } + + m->grouplist[groupnumber].last_connection_udp_tcp = ret; +} + static void break_files(const Messenger *m, int32_t friendnumber); static void check_friend_connectionstatus(Messenger *m, int32_t friendnumber, uint8_t status, void *userdata) { @@ -2028,7 +2247,7 @@ Messenger *new_messenger(Mono_Time *mono_time, Messenger_Options *options, unsig } #ifndef VANILLA_NACL - m->group_announce = new_gca(m->mono_time, m->dht); + m->group_announce = new_gca_list(); if (m->group_announce == nullptr) { kill_networking(m->net); @@ -2042,9 +2261,9 @@ Messenger *new_messenger(Mono_Time *mono_time, Messenger_Options *options, unsig if (m->group_handler == nullptr) { kill_gca(m->group_announce); + kill_networking(m->net); kill_net_crypto(m->net_crypto); kill_dht(m->dht); - kill_networking(m->net); friendreq_kill(m->fr); logger_kill(m->log); free(m); @@ -2054,8 +2273,8 @@ Messenger *new_messenger(Mono_Time *mono_time, Messenger_Options *options, unsig #endif /* VANILLA_NACL */ m->onion = new_onion(m->mono_time, m->dht); - m->onion_a = new_onion_announce(m->mono_time, m->dht); - m->onion_c = new_onion_client(m->mono_time, m->net_crypto); + m->onion_a = new_onion_announce(m->mono_time, m->dht, m->group_announce); + m->onion_c = new_onion_client(m->mono_time, m->net_crypto, m->group_handler); m->fr_c = new_friend_connections(m->mono_time, m->onion_c, options->local_discovery_enabled); if (!(m->onion && m->onion_a && m->onion_c)) { @@ -2065,7 +2284,6 @@ Messenger *new_messenger(Mono_Time *mono_time, Messenger_Options *options, unsig kill_onion_client(m->onion_c); #ifndef VANILLA_NACL kill_dht_groupchats(m->group_handler); - kill_gca(m->group_announce); #endif /* VANILLA_NACL */ kill_net_crypto(m->net_crypto); kill_dht(m->dht); @@ -2151,6 +2369,7 @@ void kill_messenger(Messenger *m) logger_kill(m->log); free(m->friendlist); + free(m->grouplist); friendreq_kill(m->fr); free(m->options.state_plugins); @@ -2180,7 +2399,7 @@ static int m_handle_status(void *object, int i, uint8_t status, void *userdata) Messenger *m = (Messenger *)object; if (status) { /* Went online. */ - send_online_packet(m, i); + send_online_packet(m, m->friendlist[i].friendcon_id); } else { /* Went offline. */ if (m->friendlist[i].status == FRIEND_ONLINE) { set_friend_status(m, i, FRIEND_CONFIRMED, userdata); @@ -2204,7 +2423,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le if (m->friendlist[i].status != FRIEND_ONLINE) { if (packet_id == PACKET_ID_ONLINE && len == 1) { set_friend_status(m, i, FRIEND_ONLINE, userdata); - send_online_packet(m, i); + send_online_packet(m, m->friendlist[i].friendcon_id); } else { return -1; } @@ -2494,14 +2713,25 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le } case PACKET_ID_INVITE_GROUPCHAT: { - if (data_length <= 1 + CHAT_ID_SIZE) { +#ifndef VANILLA_NACL + + if (data_length < 2 + GC_JOIN_DATA_LENGTH) { break; } - if (m->group_invite) { - (*m->group_invite)(m, i, data + 1, data_length - 1, m->group_invite_userdata); + if (m->group_invite && data[1] == GROUP_INVITE && data_length != 2 + GC_JOIN_DATA_LENGTH) { + if (check_group_invite(m->group_handler, data + 2, data_length - 1)) { + (*m->group_invite)(m, i, data + 2, GC_JOIN_DATA_LENGTH, + data + 2 + GC_JOIN_DATA_LENGTH, data_length - 2 - GC_JOIN_DATA_LENGTH, + m->group_invite_userdata); + } + } else if (data[1] == GROUP_INVITE_ACCEPTED) { + handle_gc_invite_accepted_packet(m->group_handler, i, data + 2, data_length - 2); + } else if (data[1] == GROUP_INVITE_CONFIRMATION) { + handle_gc_invite_confirmed_packet(m->group_handler, i, data + 2, data_length - 2); } +#endif break; } @@ -2514,6 +2744,17 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le return 0; } +static void do_group_connections(Messenger *m, void *userdata) +{ + uint32_t i; + + for (i = 0; i < m->numgroups; ++i) { + if (m->grouplist[i].active) { + check_group_tcp_udp(m, i, userdata); + } + } +} + static void do_friends(Messenger *m, void *userdata) { uint32_t i; @@ -2591,22 +2832,6 @@ static void connection_status_callback(Messenger *m, void *userdata) #define DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS 60UL -#define IDSTRING_LEN (CRYPTO_PUBLIC_KEY_SIZE * 2 + 1) -/* id_str should be of length at least IDSTRING_LEN */ -static char *id_to_string(const uint8_t *pk, char *id_str, size_t length) -{ - if (length < IDSTRING_LEN) { - snprintf(id_str, length, "Bad buf length"); - return id_str; - } - - for (uint32_t i = 0; i < CRYPTO_PUBLIC_KEY_SIZE; ++i) { - sprintf(&id_str[i * 2], "%02X", pk[i]); - } - - id_str[CRYPTO_PUBLIC_KEY_SIZE * 2] = 0; - return id_str; -} /* Minimum messenger run interval in ms TODO(mannol): A/V */ @@ -2628,6 +2853,78 @@ uint32_t messenger_run_interval(const Messenger *m) return crypto_interval; } +static void try_pack_gc_data(const Messenger *m, GC_Chat *chat, Onion_Friend *onion_friend) +{ + GC_Public_Announce announce; + int tcp_num = tcp_copy_connected_relays(chat->tcp_conn, announce.base_announce.tcp_relays, + MAX_ANNOUNCED_TCP_RELAYS); + IP_Port self_ip_port = {{{0}}}; + int copy_ip_port_result = ipport_self_copy(m->dht, &self_ip_port); + bool ip_port_is_set = copy_ip_port_result == 0; + bool can_publish_announce = tcp_num > 0 || ip_port_is_set; + + if (!tcp_num && ip_port_is_set && !ip_is_lan(self_ip_port.ip)) { + // we have only udp connection to network for now + // wait until we will have connected tcp relays, otherwise announce will be broken + can_publish_announce = false; + } + + if (can_publish_announce) { + announce.base_announce.tcp_relays_count = (uint8_t)tcp_num; + announce.base_announce.ip_port_is_set = (uint8_t)(ip_port_is_set ? 1 : 0); + + if (ip_port_is_set) { + memcpy(&announce.base_announce.ip_port, &self_ip_port, sizeof(IP_Port)); + } + + memcpy(announce.base_announce.peer_public_key, chat->self_public_key, ENC_PUBLIC_KEY); + memcpy(announce.chat_public_key, get_chat_id(chat->chat_public_key), ENC_PUBLIC_KEY); + + int length = pack_public_announce(onion_friend->gc_data, GC_MAX_DATA_LENGTH, &announce); + + if (length == -1) { + return; + } + + onion_friend->gc_data_length = (int16_t)length; + + if (tcp_num > 0) { + memcpy((void *)&chat->announced_node, &announce.base_announce.tcp_relays[0], sizeof(Node_format)); + } + + add_gc_announce(m->mono_time, m->group_announce, &announce); + } else { + onion_friend->gc_data_length = -1; // new gc - no connected relays yet and no ip/port + } +} + +#ifndef VANILLA_NACL +static void update_gc_friends_data(const Messenger *m) +{ + int i; + + for (i = 0; i < m->onion_c->num_friends; ++i) { + Onion_Friend *onion_friend = &m->onion_c->friends_list[i]; + + if (!onion_friend->gc_data_length) { + continue; + } + + GC_Chat *chat = gc_get_group_by_public_key(m->group_handler, onion_friend->gc_public_key); + + if (!chat) { + continue; + } + + if (onion_friend->gc_data_length == -1 || chat->should_update_self_announces) { + try_pack_gc_data(m, chat, onion_friend); + + chat->should_update_self_announces = false; + } + } +} +#endif + /* The main loop that needs to be run at least 20 times per second. */ void do_messenger(Messenger *m, void *userdata) { @@ -2666,9 +2963,13 @@ void do_messenger(Messenger *m, void *userdata) do_friend_connections(m->fr_c, userdata); #ifndef VANILLA_NACL do_gc(m->group_handler, userdata); - do_gca(m->group_handler->announce); -#endif /* VANILLA_NACL */ + do_gca(m->mono_time, m->group_announce); +#endif do_friends(m, userdata); + do_group_connections(m, userdata); +#ifndef VANILLA_NACL + update_gc_friends_data(m); +#endif connection_status_callback(m, userdata); if (mono_time_get(m->mono_time) > m->lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) { @@ -3138,79 +3439,48 @@ static State_Load_Status friends_list_load(Messenger *m, const uint8_t *data, ui #ifndef VANILLA_NACL static uint32_t saved_groups_size(const Messenger *m) { - return gc_count_groups(m->group_handler) * sizeof(struct Saved_Group); + return gc_count_groups(m->group_handler) * sizeof(Saved_Group); } static uint8_t *groups_save(const Messenger *m, uint8_t *data) { - uint32_t i; - uint32_t num = 0; - GC_Session *c = m->group_handler; + const GC_Session *c = m->group_handler; data = state_write_section_header(data, STATE_COOKIE_TYPE, saved_groups_size(m), STATE_TYPE_GROUPS); - for (i = 0; i < c->num_chats; ++i) { - if (c->chats[i].connection_state > CS_NONE && c->chats[i].connection_state < CS_INVALID) { - struct Saved_Group temp; - memset(&temp, 0, sizeof(struct Saved_Group)); - - memcpy(temp.founder_public_key, c->chats[i].shared_state.founder_public_key, EXT_PUBLIC_KEY); - temp.group_name_len = net_htons(c->chats[i].shared_state.group_name_len); - memcpy(temp.group_name, c->chats[i].shared_state.group_name, MAX_GC_GROUP_NAME_SIZE); - temp.privacy_state = c->chats[i].shared_state.privacy_state; - temp.maxpeers = net_htons(c->chats[i].shared_state.maxpeers); - temp.passwd_len = net_htons(c->chats[i].shared_state.passwd_len); - memcpy(temp.passwd, c->chats[i].shared_state.passwd, MAX_GC_PASSWD_SIZE); - memcpy(temp.mod_list_hash, c->chats[i].shared_state.mod_list_hash, GC_MODERATION_HASH_SIZE); - temp.sstate_version = net_htonl(c->chats[i].shared_state.version); - memcpy(temp.sstate_signature, c->chats[i].shared_state_sig, SIGNATURE_SIZE); - - temp.topic_len = net_htons(c->chats[i].topic_info.length); - memcpy(temp.topic, c->chats[i].topic_info.topic, MAX_GC_TOPIC_SIZE); - memcpy(temp.topic_public_sig_key, c->chats[i].topic_info.public_sig_key, SIG_PUBLIC_KEY); - temp.topic_version = net_htonl(c->chats[i].topic_info.version); - memcpy(temp.topic_signature, c->chats[i].topic_sig, SIGNATURE_SIZE); - - memcpy(temp.chat_public_key, c->chats[i].chat_public_key, EXT_PUBLIC_KEY); - memcpy(temp.chat_secret_key, c->chats[i].chat_secret_key, EXT_SECRET_KEY); /* empty for non-founders */ - - uint16_t num_addrs = gc_copy_peer_addrs(&c->chats[i], temp.addrs, GROUP_SAVE_MAX_PEERS); - temp.num_addrs = net_htons(num_addrs); - - temp.num_mods = net_htons(c->chats[i].moderation.num_mods); - mod_list_pack(&c->chats[i], temp.mod_list); - - memcpy(temp.self_public_key, c->chats[i].self_public_key, EXT_PUBLIC_KEY); - memcpy(temp.self_secret_key, c->chats[i].self_secret_key, EXT_SECRET_KEY); - memcpy(temp.self_nick, c->chats[i].group[0].nick, MAX_GC_NICK_SIZE); - temp.self_nick_len = net_htons(c->chats[i].group[0].nick_len); - temp.self_role = c->chats[i].group[0].role; - temp.self_status = c->chats[i].group[0].status; - - memcpy(data + num * sizeof(struct Saved_Group), &temp, sizeof(struct Saved_Group)); - ++num; + for (uint32_t i = 0; i < c->num_chats; ++i) { + const GC_Chat *chat = &c->chats[i]; + + if (chat->connection_state <= CS_NONE || chat->connection_state >= CS_INVALID) { + continue; } + + Saved_Group temp; + pack_group_info(chat, &temp, true); + + memcpy(data, &temp, sizeof(Saved_Group)); + data += sizeof(Saved_Group); } - return data + num * sizeof(struct Saved_Group); + return data; } static State_Load_Status groups_load(Messenger *m, const uint8_t *data, uint32_t length) { - if (length % sizeof(struct Saved_Group) != 0) { + if (length % sizeof(Saved_Group) != 0) { return STATE_LOAD_STATUS_ERROR; // TODO(endoffile78): error or continue? } - uint32_t i, num = length / sizeof(struct Saved_Group); + uint32_t i, num = length / sizeof(Saved_Group); for (i = 0; i < num; ++i) { - struct Saved_Group temp; - memcpy(&temp, data + i * sizeof(struct Saved_Group), sizeof(struct Saved_Group)); + Saved_Group temp; + memcpy(&temp, data + i * sizeof(Saved_Group), sizeof(Saved_Group)); - int ret = gc_group_load(m->group_handler, &temp); + int group_number = gc_group_load(m->group_handler, &temp, -1); - if (ret == -1) { + if (group_number == -1) { LOGGER_WARNING(m->log, "Failed to join group"); } } diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h index 697e2b89c3..693838fbc3 100644 --- a/toxcore/Messenger.h +++ b/toxcore/Messenger.h @@ -198,7 +198,8 @@ typedef void m_friend_connectionstatuschange_internal_cb(Messenger *m, uint32_t uint8_t connection_status, void *user_data); typedef void m_conference_invite_cb(Messenger *m, uint32_t friend_number, const uint8_t *cookie, uint16_t length, void *user_data); -typedef void m_group_invite_cb(Messenger *m, uint32_t friendnumber, const uint8_t *data, size_t length, void *userdata); +typedef void m_group_invite_cb(Messenger *m, uint32_t friendnumber, const uint8_t *data, size_t length, + const uint8_t *group_name, size_t group_name_length, void *userdata); typedef void m_msi_packet_cb(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *user_data); typedef int m_lossy_rtp_packet_cb(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t len, void *object); @@ -242,6 +243,13 @@ typedef struct Friend { struct Receipts *receipts_end; } Friend; +typedef struct Group { + uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; + int friendcon_id; + uint8_t last_connection_udp_tcp; + bool active; // true if group is active. +} Group; + struct Messenger { Logger *log; Mono_Time *mono_time; @@ -269,10 +277,13 @@ struct Messenger { Friend *friendlist; uint32_t numfriends; + Group *grouplist; + uint32_t numgroups; + time_t lastdump; GC_Session *group_handler; - GC_Announce *group_announce; + GC_Announces_List *group_announce; bool has_added_relays; // If the first connection has occurred in do_messenger @@ -354,6 +365,10 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u */ int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk); +int32_t m_add_group(Messenger *m, GC_Chat *chat); + +int32_t m_remove_group(Messenger *m, const GC_Chat *chat); + /* return the friend number associated to that client id. * return -1 if no such friend. */ @@ -379,6 +394,13 @@ int getfriendcon_id(const Messenger *m, int32_t friendnumber); */ int m_delfriend(Messenger *m, int32_t friendnumber); +/* Remove a group. + * + * Return 0 if success. + * Return -1 if failure. + */ +int m_delgroup(Messenger *m, int32_t groupnumber); + /* Checks friend's connecting status. * * return CONNECTION_UDP (2) if friend is directly connected to us (Online UDP). @@ -587,12 +609,15 @@ void m_callback_group_invite(Messenger *m, m_group_invite_cb *function, void *us /* Send a conference invite packet. * - * return 0 on success - * return -1 on failure + * return 1 on success + * return 0 on failure */ int send_conference_invite_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length); /* Send a group invite packet. + * + * WARNING: Return-value semantics are different than for + * send_conference_invite_packet(). * * return 0 on success * return -1 on failure diff --git a/toxcore/TCP_client.c b/toxcore/TCP_client.c index 408ec4891a..c4d3d06506 100644 --- a/toxcore/TCP_client.c +++ b/toxcore/TCP_client.c @@ -577,7 +577,7 @@ void oob_data_handler(TCP_Client_Connection *con, tcp_oob_data_cb *oob_data_call */ static int client_send_disconnect_notification(TCP_Client_Connection *con, uint8_t id) { - uint8_t packet[1 + 1]; + uint8_t packet[2]; packet[0] = TCP_PACKET_DISCONNECT_NOTIFICATION; packet[1] = id; return write_packet_TCP_client_secure_connection(con, packet, sizeof(packet), 1); @@ -755,7 +755,7 @@ static int handle_TCP_client_packet(TCP_Client_Connection *conn, const uint8_t * switch (data[0]) { case TCP_PACKET_ROUTING_RESPONSE: { - if (length != 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE) { + if (length != 2 + CRYPTO_PUBLIC_KEY_SIZE) { return -1; } @@ -781,7 +781,7 @@ static int handle_TCP_client_packet(TCP_Client_Connection *conn, const uint8_t * } case TCP_PACKET_CONNECTION_NOTIFICATION: { - if (length != 1 + 1) { + if (length != 2) { return -1; } @@ -806,7 +806,7 @@ static int handle_TCP_client_packet(TCP_Client_Connection *conn, const uint8_t * } case TCP_PACKET_DISCONNECT_NOTIFICATION: { - if (length != 1 + 1) { + if (length != 2) { return -1; } diff --git a/toxcore/TCP_connection.c b/toxcore/TCP_connection.c index bf4d765629..509633d683 100644 --- a/toxcore/TCP_connection.c +++ b/toxcore/TCP_connection.c @@ -13,12 +13,13 @@ #include "TCP_connection.h" #include +#include #include #include #include "mono_time.h" #include "util.h" - +#include "TCP_client.h" struct TCP_Connections { Mono_Time *mono_time; @@ -42,6 +43,9 @@ struct TCP_Connections { tcp_onion_cb *tcp_onion_callback; void *tcp_onion_callback_object; + tcp_connection_status_updated_cb *tcp_connection_status_updated_callback; + void *tcp_connection_status_updated_callback_object; + TCP_Proxy_Info proxy_info; bool onion_status; @@ -426,6 +430,25 @@ int tcp_send_oob_packet(TCP_Connections *tcp_c, unsigned int tcp_connections_num return -1; } +static int find_tcp_connection_relay(TCP_Connections *tcp_c, const uint8_t *relay_pk); + +/* Send an oob packet via the TCP relay corresponding to relay_pk. + * + * return 0 on success. + * return -1 on failure. + */ +int tcp_send_oob_packet_using_relay(TCP_Connections *tcp_c, const uint8_t *relay_pk, const uint8_t *public_key, + const uint8_t *packet, uint16_t length) +{ + int tcp_con_number = find_tcp_connection_relay(tcp_c, relay_pk); + + if (tcp_con_number < 0) { + return -1; + } + + return tcp_send_oob_packet(tcp_c, tcp_con_number, public_key, packet, length); +} + /* Set the callback for TCP data packets. */ void set_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_data_cb *tcp_data_callback, void *object) @@ -450,6 +473,14 @@ void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_onion_ tcp_c->tcp_onion_callback_object = object; } +void set_connection_status_updated_callback(TCP_Connections *tcp_c, + tcp_connection_status_updated_cb *connection_status_updated_callback, + void *object) +{ + tcp_c->tcp_connection_status_updated_callback = connection_status_updated_callback; + tcp_c->tcp_connection_status_updated_callback_object = object; +} + /* Find the TCP connection with public_key. * @@ -717,8 +748,8 @@ static unsigned int online_tcp_connection_from_conn(TCP_Connection_to *con_to) /* return index on success. * return -1 on failure. */ -static int set_tcp_connection_status(TCP_Connection_to *con_to, unsigned int tcp_connections_number, - unsigned int status, uint8_t connection_id) +static int set_tcp_connection_status(TCP_Connections *tcp_c, TCP_Connection_to *con_to, + unsigned int tcp_connections_number, unsigned int status, uint8_t connection_id) { unsigned int i; @@ -731,6 +762,12 @@ static int set_tcp_connection_status(TCP_Connection_to *con_to, unsigned int tcp con_to->connections[i].status = status; con_to->connections[i].connection_id = connection_id; + + if (tcp_c->tcp_connection_status_updated_callback) { + tcp_c->tcp_connection_status_updated_callback(tcp_c->tcp_connection_status_updated_callback_object, + tcp_c, status); + } + return i; } } @@ -743,7 +780,7 @@ static int set_tcp_connection_status(TCP_Connection_to *con_to, unsigned int tcp * return 0 on success. * return -1 on failure. */ -static int kill_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number) +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); @@ -800,7 +837,7 @@ static int reconnect_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connec TCP_Connection_to *con_to = get_connection(tcp_c, i); if (con_to) { - set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_NONE, 0); + set_tcp_connection_status(tcp_c, con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_NONE, 0); } } @@ -846,7 +883,7 @@ static int sleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connection TCP_Connection_to *con_to = get_connection(tcp_c, i); if (con_to) { - set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_NONE, 0); + set_tcp_connection_status(tcp_c, con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_NONE, 0); } } @@ -940,7 +977,8 @@ static int tcp_response_callback(void *object, uint8_t connection_id, const uint return -1; } - if (set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_REGISTERED, connection_id) == -1) { + if (set_tcp_connection_status(tcp_c, con_to, tcp_connections_number, + TCP_CONNECTIONS_STATUS_REGISTERED, connection_id) == -1) { return -1; } @@ -963,7 +1001,8 @@ static int tcp_status_callback(void *object, uint32_t number, uint8_t connection } if (status == 1) { - if (set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_REGISTERED, connection_id) == -1) { + if (set_tcp_connection_status(tcp_c, con_to, tcp_connections_number, + TCP_CONNECTIONS_STATUS_REGISTERED, connection_id) == -1) { return -1; } @@ -973,7 +1012,8 @@ static int tcp_status_callback(void *object, uint32_t number, uint8_t connection --tcp_con->sleep_count; } } else if (status == 2) { - if (set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_ONLINE, connection_id) == -1) { + if (set_tcp_connection_status(tcp_c, con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_ONLINE, + connection_id) == -1) { return -1; } @@ -1411,41 +1451,44 @@ static void do_tcp_conns(TCP_Connections *tcp_c, void *userdata) 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_SLEEPING) { - do_TCP_connection(tcp_c->mono_time, tcp_con->connection, userdata); + if (!tcp_con) { + continue; + } - /* callbacks can change TCP connection address. */ - tcp_con = get_tcp_connection(tcp_c, i); + if (tcp_con->status != TCP_CONN_SLEEPING) { + do_TCP_connection(tcp_c->mono_time, tcp_con->connection, userdata); - // Make sure the TCP connection wasn't dropped in any of the callbacks. - assert(tcp_con != nullptr); + /* callbacks can change TCP connection address. */ + tcp_con = get_tcp_connection(tcp_c, i); - if (tcp_con_status(tcp_con->connection) == TCP_CLIENT_DISCONNECTED) { - if (tcp_con->status == TCP_CONN_CONNECTED) { - reconnect_tcp_relay_connection(tcp_c, i); - } else { - kill_tcp_relay_connection(tcp_c, i); - } + // Make sure the TCP connection wasn't dropped in any of the callbacks. + assert(tcp_con != nullptr); - continue; + if (tcp_con_status(tcp_con->connection) == TCP_CLIENT_DISCONNECTED) { + if (tcp_con->status == TCP_CONN_CONNECTED) { + reconnect_tcp_relay_connection(tcp_c, i); + } else { + kill_tcp_relay_connection(tcp_c, i); } - if (tcp_con->status == TCP_CONN_VALID && tcp_con_status(tcp_con->connection) == TCP_CLIENT_CONFIRMED) { - tcp_relay_on_online(tcp_c, i); - } + continue; + } - if (tcp_con->status == TCP_CONN_CONNECTED && !tcp_con->onion && tcp_con->lock_count - && tcp_con->lock_count == tcp_con->sleep_count - && mono_time_is_timeout(tcp_c->mono_time, tcp_con->connected_time, TCP_CONNECTION_ANNOUNCE_TIMEOUT)) { - sleep_tcp_relay_connection(tcp_c, i); - } + if (tcp_con->status == TCP_CONN_VALID && tcp_con_status(tcp_con->connection) == TCP_CLIENT_CONFIRMED) { + tcp_relay_on_online(tcp_c, i); } - if (tcp_con->status == TCP_CONN_SLEEPING && tcp_con->unsleep) { - unsleep_tcp_relay_connection(tcp_c, i); + if (tcp_con->status == TCP_CONN_CONNECTED + && !tcp_con->onion && tcp_con->lock_count + && tcp_con->lock_count == tcp_con->sleep_count + && mono_time_is_timeout(tcp_c->mono_time, tcp_con->connected_time, TCP_CONNECTION_ANNOUNCE_TIMEOUT)) { + sleep_tcp_relay_connection(tcp_c, i); } } + + if (tcp_con->status == TCP_CONN_SLEEPING && tcp_con->unsleep) { + unsleep_tcp_relay_connection(tcp_c, i); + } } } diff --git a/toxcore/TCP_connection.h b/toxcore/TCP_connection.h index 7a695760b1..cb57844334 100644 --- a/toxcore/TCP_connection.h +++ b/toxcore/TCP_connection.h @@ -9,6 +9,7 @@ #ifndef C_TOXCORE_TOXCORE_TCP_CONNECTION_H #define C_TOXCORE_TOXCORE_TCP_CONNECTION_H +#include #include "TCP_client.h" #define TCP_CONN_NONE 0 @@ -71,6 +72,7 @@ const uint8_t *tcp_connections_public_key(const TCP_Connections *tcp_c); uint32_t tcp_connections_count(const TCP_Connections *tcp_c); + /* Send a packet to the TCP connection. * * return -1 on failure. @@ -115,6 +117,9 @@ int tcp_send_oob_packet(TCP_Connections *tcp_c, unsigned int tcp_connections_num typedef int tcp_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata); +int tcp_send_oob_packet_using_relay(TCP_Connections *tcp_c, const uint8_t *relay_pk, 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, tcp_data_cb *tcp_data_callback, void *object); @@ -125,6 +130,12 @@ typedef int tcp_onion_cb(void *object, const uint8_t *data, uint16_t length, voi */ void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_onion_cb *tcp_onion_callback, void *object); +typedef void tcp_connection_status_updated_cb(void *object, TCP_Connections *tcp_c, int status); + +void set_connection_status_updated_callback(TCP_Connections *tcp_c, + tcp_connection_status_updated_cb *connection_status_updated_callback, + void *object); + typedef int tcp_oob_cb(void *object, const uint8_t *public_key, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length, void *userdata); @@ -184,6 +195,7 @@ int add_tcp_number_relay_connection(TCP_Connections *tcp_c, int connections_numb */ 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. @@ -208,6 +220,8 @@ uint32_t tcp_copy_connected_relays(TCP_Connections *tcp_c, Node_format *tcp_rela */ TCP_Connections *new_tcp_connections(Mono_Time *mono_time, const uint8_t *secret_key, TCP_Proxy_Info *proxy_info); +int kill_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number); + void do_tcp_connections(TCP_Connections *tcp_c, void *userdata); void kill_tcp_connections(TCP_Connections *tcp_c); diff --git a/toxcore/TCP_server.c b/toxcore/TCP_server.c index b22bdb8084..3e55ac2414 100644 --- a/toxcore/TCP_server.c +++ b/toxcore/TCP_server.c @@ -635,7 +635,7 @@ static int read_connection_handshake(TCP_Secure_Connection *con, const uint8_t * */ static int send_routing_response(TCP_Secure_Connection *con, uint8_t rpid, const uint8_t *public_key) { - uint8_t data[1 + 1 + CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t data[2 + CRYPTO_PUBLIC_KEY_SIZE]; data[0] = TCP_PACKET_ROUTING_RESPONSE; data[1] = rpid; memcpy(data + 2, public_key, CRYPTO_PUBLIC_KEY_SIZE); diff --git a/toxcore/friend_connection.c b/toxcore/friend_connection.c index af5d09e370..cc77ee0c4b 100644 --- a/toxcore/friend_connection.c +++ b/toxcore/friend_connection.c @@ -21,63 +21,6 @@ #define PORTS_PER_DISCOVERY 10 -typedef struct Friend_Conn_Callbacks { - fc_status_cb *status_callback; - fc_data_cb *data_callback; - fc_lossy_data_cb *lossy_data_callback; - - void *callback_object; - int callback_id; -} Friend_Conn_Callbacks; - -typedef struct Friend_Conn { - uint8_t status; - - uint8_t real_public_key[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint16_t dht_lock; - IP_Port dht_ip_port; - uint64_t dht_pk_lastrecv; - uint64_t dht_ip_port_lastrecv; - - int onion_friendnum; - int crypt_connection_id; - - uint64_t ping_lastrecv; - uint64_t ping_lastsent; - uint64_t share_relays_lastsent; - - Friend_Conn_Callbacks callbacks[MAX_FRIEND_CONNECTION_CALLBACKS]; - - uint16_t lock_count; - - Node_format tcp_relays[FRIEND_MAX_STORED_TCP_RELAYS]; - uint16_t tcp_relay_counter; - - bool hosting_tcp_relay; -} Friend_Conn; - - -struct Friend_Connections { - const Mono_Time *mono_time; - Net_Crypto *net_crypto; - DHT *dht; - Onion_Client *onion_c; - - Friend_Conn *conns; - uint32_t num_cons; - - fr_request_cb *fr_request_callback; - void *fr_request_object; - - global_status_cb *global_status_callback; - void *global_status_callback_object; - - uint64_t last_lan_discovery; - uint16_t next_lan_port; - - bool local_discovery_enabled; -}; Net_Crypto *friendconn_net_crypto(const Friend_Connections *fr_c) { @@ -266,7 +209,7 @@ static unsigned int send_relays(Friend_Connections *fr_c, int friendcon_id) return 0; } - Node_format nodes[MAX_SHARED_RELAYS]; + Node_format nodes[MAX_SHARED_RELAYS] = {{{0}}}; uint8_t data[1024]; const int n = copy_connected_tcp_relays(fr_c->net_crypto, nodes, MAX_SHARED_RELAYS); diff --git a/toxcore/friend_connection.h b/toxcore/friend_connection.h index 92e3870345..2e3c6810c2 100644 --- a/toxcore/friend_connection.h +++ b/toxcore/friend_connection.h @@ -46,6 +46,7 @@ typedef enum Friendconn_Status { FRIENDCONN_STATUS_CONNECTED, } Friendconn_Status; + typedef struct Friend_Connections Friend_Connections; Net_Crypto *friendconn_net_crypto(const Friend_Connections *fr_c); @@ -157,4 +158,62 @@ void do_friend_connections(Friend_Connections *fr_c, void *userdata); /* Free everything related with friend_connections. */ void kill_friend_connections(Friend_Connections *fr_c); +typedef struct Friend_Conn_Callbacks { + fc_status_cb *status_callback; + fc_data_cb *data_callback; + fc_lossy_data_cb *lossy_data_callback; + + void *callback_object; + int callback_id; +} Friend_Conn_Callbacks; + +typedef struct Friend_Conn { + uint8_t status; + + uint8_t real_public_key[CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; + uint16_t dht_lock; + IP_Port dht_ip_port; + uint64_t dht_pk_lastrecv; + uint64_t dht_ip_port_lastrecv; + + int onion_friendnum; + int crypt_connection_id; + + uint64_t ping_lastrecv; + uint64_t ping_lastsent; + uint64_t share_relays_lastsent; + + Friend_Conn_Callbacks callbacks[MAX_FRIEND_CONNECTION_CALLBACKS]; + + uint16_t lock_count; + + Node_format tcp_relays[FRIEND_MAX_STORED_TCP_RELAYS]; + uint16_t tcp_relay_counter; + + bool hosting_tcp_relay; +} Friend_Conn; + + +struct Friend_Connections { + const Mono_Time *mono_time; + Net_Crypto *net_crypto; + DHT *dht; + Onion_Client *onion_c; + + Friend_Conn *conns; + uint32_t num_cons; + + fr_request_cb *fr_request_callback; + void *fr_request_object; + + global_status_cb *global_status_callback; + void *global_status_callback_object; + + uint64_t last_lan_discovery; + uint16_t next_lan_port; + + bool local_discovery_enabled; +}; + #endif diff --git a/toxcore/group_announce.c b/toxcore/group_announce.c index 51935cc844..5c969e5a83 100644 --- a/toxcore/group_announce.c +++ b/toxcore/group_announce.c @@ -2,1180 +2,358 @@ * Copyright © 2016-2020 The TokTok team. * Copyright © 2015 Tox project. */ +#include "group_announce.h" +#include "LAN_discovery.h" -/* - * Similar to ping.h, but designed for group chat purposes - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include #include #include +#include +#include -#include "Messenger.h" -#include "logger.h" #include "util.h" -#include "mono_time.h" -#include "network.h" -#include "DHT.h" - -#include "group_announce.h" -#include "group_chats.h" - -#ifndef VANILLA_NACL - -enum { - RAND_ID_SIZE = sizeof(uint64_t), - - /* type + sender_dht_pk + nonce + */ - GCA_HEADER_SIZE = 1 + ENC_PUBLIC_KEY + CRYPTO_NONCE_SIZE, - - /* type + ping_id */ - GCA_PING_REQUEST_PLAIN_SIZE = 1 + RAND_ID_SIZE, - GCA_PING_REQUEST_DHT_SIZE = GCA_HEADER_SIZE + ENC_PUBLIC_KEY + GCA_PING_REQUEST_PLAIN_SIZE + CRYPTO_MAC_SIZE, - /* type + ping_id */ - GCA_PING_RESPONSE_PLAIN_SIZE = 1 + RAND_ID_SIZE, - GCA_PING_RESPONSE_DHT_SIZE = GCA_HEADER_SIZE + GCA_PING_RESPONSE_PLAIN_SIZE + CRYPTO_MAC_SIZE, - GCA_PING_INTERVAL = 60, - GCA_NODES_EXPIRATION = GCA_PING_INTERVAL * 3 + 10, - GCA_RELAY_RATE_LIMIT = 30, - - MAX_GCA_PACKET_SIZE = 1024, -}; - -/* Copies your own ip_port structure to dest. (TODO: This should probably go somewhere else) - * - * Return 0 on succcess. - * Return -1 on failure. - */ -int ipport_self_copy(const DHT *dht, IP_Port *dest) +static void remove_announces(GC_Announces_List *gc_announces_list, GC_Announces *announces) { - for (size_t i = 0; i < LCLIENT_LIST; ++i) { - const Client_data *const client = dht_get_close_client(dht, i); - const IP_Port *const ip_port4 = &client->assoc4.ret_ip_port; - - if (ipport_isset(ip_port4)) { - ipport_copy(dest, ip_port4); - break; - } - - const IP_Port *const ip_port6 = &client->assoc6.ret_ip_port; - - if (ipport_isset(ip_port6)) { - ipport_copy(dest, ip_port6); - break; - } - } - - if (!ipport_isset(dest)) { - return -1; - } - - return 0; -} - -/* Creates a GC_Announce_Node using public_key and your own IP_Port struct - * - * Return 0 on success. - * Return -1 on failure. - */ -int make_self_gca_node(const DHT *dht, GC_Announce_Node *node, const uint8_t *public_key) -{ - if (ipport_self_copy(dht, &node->ip_port) == -1) { - return -1; + if (announces->prev_announce) { + announces->prev_announce->next_announce = announces->next_announce; + } else { + gc_announces_list->announces = announces->next_announce; } - memcpy(node->public_key, public_key, ENC_PUBLIC_KEY); - return 0; -} - -/* Pack number of nodes into data of maxlength length. - * - * return length of packed nodes on success. - * return -1 on failure. - */ -int pack_gca_nodes(uint8_t *data, uint16_t length, const GC_Announce_Node *nodes, uint32_t number) -{ - uint32_t i; - int packed_length = 0; - - for (i = 0; i < number; ++i) { - int ipp_size = pack_ip_port(data + packed_length, length - packed_length, &nodes[i].ip_port); - - if (ipp_size == -1) { - return -1; - } - - packed_length += ipp_size; - - if (packed_length + ENC_PUBLIC_KEY > length) { - return -1; - } - - memcpy(data + packed_length, nodes[i].public_key, ENC_PUBLIC_KEY); - packed_length += ENC_PUBLIC_KEY; + if (announces->next_announce) { + announces->next_announce->prev_announce = announces->prev_announce; } - return packed_length; + free(announces); + --gc_announces_list->announces_count; } -/* Unpack data of length into nodes of size max_num_nodes. - * Put the length of the data processed in processed_data_len. - * tcp_enabled sets if TCP nodes are expected (true) or not (false). - * - * return number of unpacked nodes on success. - * return -1 on failure. - */ -int unpack_gca_nodes(GC_Announce_Node *nodes, uint32_t max_num_nodes, uint16_t *processed_data_len, - const uint8_t *data, uint16_t length, uint8_t tcp_enabled) +GC_Announces_List *new_gca_list(void) { - uint32_t num = 0, len_processed = 0; - - while (num < max_num_nodes && len_processed < length) { - int ipp_size = unpack_ip_port(&nodes[num].ip_port, data + len_processed, length - len_processed, tcp_enabled); - - if (ipp_size == -1) { - return -1; - } - - len_processed += ipp_size; - - if (len_processed + ENC_PUBLIC_KEY > length) { - return -1; - } + GC_Announces_List *announces_list = (GC_Announces_List *)calloc(1, sizeof(GC_Announces_List)); - memcpy(nodes[num].public_key, data + len_processed, ENC_PUBLIC_KEY); - len_processed += ENC_PUBLIC_KEY; - ++num; - } - - if (processed_data_len) { - *processed_data_len = len_processed; - } - - return num; + return announces_list; } -/* Removes plaintext header and decrypts packets. - * - * Returns length of plaintext data on success. - * Returns -1 on failure. - */ -static int unwrap_gca_packet(const uint8_t *self_public_key, const uint8_t *self_secret_key, uint8_t *public_key, - uint8_t *data, size_t data_size, uint8_t packet_type, const uint8_t *packet, uint16_t length) +void kill_gca(GC_Announces_List *announces_list) { - if (id_equal(packet + 1, self_public_key)) { - fprintf(stderr, "Announce unwrap failed: id_equal failed\n"); - return -1; + while (announces_list->announces) { + remove_announces(announces_list, announces_list->announces); } - if (public_key) { - memcpy(public_key, packet + 1, ENC_PUBLIC_KEY); - } - - size_t header_len = GCA_HEADER_SIZE; - uint8_t nonce[CRYPTO_NONCE_SIZE]; - - if (packet_type == NET_PACKET_GCA_SEND_NODES) { - header_len += RAND_ID_SIZE; - memcpy(nonce, packet + 1 + ENC_PUBLIC_KEY + RAND_ID_SIZE, CRYPTO_NONCE_SIZE); - } else if (packet_type == NET_PACKET_GCA_PING_REQUEST) { - header_len += ENC_PUBLIC_KEY; - memcpy(nonce, packet + 1 + ENC_PUBLIC_KEY + ENC_PUBLIC_KEY, CRYPTO_NONCE_SIZE); - } else { - memcpy(nonce, packet + 1 + ENC_PUBLIC_KEY, CRYPTO_NONCE_SIZE); - } - - if (length <= header_len + CRYPTO_MAC_SIZE) { - fprintf(stderr, "Announce unwrap failed: Encrypted length is too small %d\n", length); - return -1; - } - - size_t plain_len = length - header_len - CRYPTO_MAC_SIZE; - - if (plain_len > data_size) { - fprintf(stderr, "Announce unwrap failed: plain len (%u) is larger than data_len (%u)\n", (unsigned)plain_len, - (unsigned)data_size); - return -1; - } - - VLA(uint8_t, plain, plain_len); - int len = decrypt_data(public_key, self_secret_key, nonce, packet + header_len, length - header_len, plain); - - if (len != plain_len) { - fprintf(stderr, "Announce unwrap failed: length is %d, type is %u\n", len, plain[0]); - return -1; - } - - if (plain[0] != packet_type) { - fprintf(stderr, "Announce unwrap failed with wrong packet type %d - expected %d\n", plain[0], packet_type); - return -1; - } - - memcpy(data, plain, len); - return len; + free(announces_list); } -/* Encrypts data of length and adds a plaintext header containing the packet type, - * public encryption key of the sender, and the nonce used to encrypt data. - */ -static int wrap_gca_packet(const uint8_t *send_public_key, const uint8_t *send_secret_key, - const uint8_t *recv_public_key, uint8_t *packet, uint32_t packet_size, - const uint8_t *data, uint32_t length, uint8_t packet_type) +void do_gca(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list) { - if (packet_size < length + GCA_HEADER_SIZE + CRYPTO_MAC_SIZE) { - return -1; - } - - uint8_t nonce[CRYPTO_NONCE_SIZE]; - random_nonce(nonce); - - VLA(uint8_t, encrypt, length + CRYPTO_MAC_SIZE); - int len = encrypt_data(recv_public_key, send_secret_key, nonce, data, length, encrypt); - - if (len != SIZEOF_VLA(encrypt)) { - fprintf(stderr, "Announce encrypt failed\n"); - return -1; + if (!gc_announces_list) { + return; } - packet[0] = packet_type; - memcpy(packet + 1, send_public_key, ENC_PUBLIC_KEY); - memcpy(packet + 1 + ENC_PUBLIC_KEY, nonce, CRYPTO_NONCE_SIZE); - memcpy(packet + GCA_HEADER_SIZE, encrypt, len); - - return GCA_HEADER_SIZE + len; -} + GC_Announces *announces = gc_announces_list->announces; -static bool gca_rate_limit(GC_Announce *announce); -static void remove_gca_self_announce(GC_Announce *announce, const uint8_t *chat_id); -static size_t add_gc_announced_node(GC_Announce *announce, const uint8_t *chat_id, const GC_Announce_Node node, - bool self); + while (announces) { + if (announces->last_announce_received_timestamp <= mono_time_get(mono_time) - GC_ANNOUNCE_SAVING_TIMEOUT) { + GC_Announces *announces_to_delete = announces; + announces = announces->next_announce; + remove_announces(gc_announces_list, announces_to_delete); -static int dispatch_packet_announce_request(GC_Announce *announce, const uint8_t *chat_id, - const uint8_t *origin_pk, const uint8_t *self_pk, - const uint8_t *data, uint32_t length, bool self) -{ - Node_format dht_nodes[MAX_SENT_NODES]; - uint32_t nclosest = get_close_nodes(announce->dht, chat_id, dht_nodes, net_family_unspec, 1, 1); - nclosest = min_u32(MAX_GCA_SENT_NODES, nclosest); - - VLA(uint8_t, packet, length + GCA_HEADER_SIZE + CRYPTO_MAC_SIZE); - uint32_t i; - uint16_t sent = 0; - - /* Relay announce request to all nclosest nodes */ - for (i = 0; i < nclosest; ++i) { - if (origin_pk && id_equal(origin_pk, dht_nodes[i].public_key)) { continue; } - if (id_closest(chat_id, dht_nodes[i].public_key, self_pk) != 1) { - continue; - } - - int packet_length = wrap_gca_packet(dht_get_self_public_key(announce->dht), - dht_get_self_secret_key(announce->dht), - dht_nodes[i].public_key, packet, SIZEOF_VLA(packet), data, length, - NET_PACKET_GCA_ANNOUNCE); - - if (packet_length == -1) { - continue; - } - - if (sendpacket(dht_get_net(announce->dht), dht_nodes[i].ip_port, packet, packet_length) != -1) { - ++sent; - } - } - - /* Add to announcements if we're the closest node to chat_id */ - if (sent == 0) { - GC_Announce_Node node; - - if (unpack_gca_nodes(&node, 1, nullptr, data + 1 + CHAT_ID_SIZE, length - 1 - CHAT_ID_SIZE, 0) != 1) { - return -1; - } - - add_gc_announced_node(announce, chat_id, node, self); - - /* We will never need to ping or renew our own announcement */ - if (self) { - remove_gca_self_announce(announce, chat_id); - } + announces = announces->next_announce; } - - return sent; } -static int dispatch_packet_get_nodes_request(GC_Announce *announce, const uint8_t *chat_id, - const uint8_t *origin_pk, const uint8_t *self_pk, - const uint8_t *data, uint32_t length, bool self) +static GC_Announces *get_announces_by_chat_id(const GC_Announces_List *gc_announces_list, const uint8_t *chat_id) { - Node_format dht_nodes[MAX_SENT_NODES]; - uint32_t nclosest = get_close_nodes(announce->dht, chat_id, dht_nodes, net_family_unspec, 1, 1); - nclosest = min_u32(MAX_GCA_SENT_NODES, nclosest); - - VLA(uint8_t, packet, length + GCA_HEADER_SIZE + CRYPTO_MAC_SIZE); - uint32_t i; - uint16_t sent = 0; - - for (i = 0; i < nclosest; ++i) { - if (!self) { - if (origin_pk && id_equal(origin_pk, dht_nodes[i].public_key)) { - continue; - } - - if (id_closest(chat_id, dht_nodes[i].public_key, self_pk) != 1) { - continue; - } - } - - int packet_length = wrap_gca_packet(dht_get_self_public_key(announce->dht), - dht_get_self_secret_key(announce->dht), - dht_nodes[i].public_key, packet, SIZEOF_VLA(packet), data, length, - NET_PACKET_GCA_GET_NODES); + GC_Announces *announces = gc_announces_list->announces; - if (packet_length == -1) { - continue; + while (announces) { + if (!memcmp(announces->chat_id, chat_id, CHAT_ID_SIZE)) { + return announces; } - if (sendpacket(dht_get_net(announce->dht), dht_nodes[i].ip_port, packet, packet_length) != -1) { - ++sent; - } + announces = announces->next_announce; } - return sent; + return nullptr; } -/* Returns the number of sent packets */ -static int dispatch_packet(GC_Announce *announce, const uint8_t *chat_id, const uint8_t *origin_pk, - const uint8_t *self_pk, const uint8_t *data, uint32_t length, uint8_t packet_type, - bool self) +bool cleanup_gca(GC_Announces_List *gc_announces_list, const uint8_t *chat_id) { - if (gca_rate_limit(announce)) { - return 0; + if (!gc_announces_list || !chat_id) { + return false; } - if (packet_type == NET_PACKET_GCA_ANNOUNCE) { - return dispatch_packet_announce_request(announce, chat_id, origin_pk, self_pk, data, length, self); - } + GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, chat_id); - if (packet_type == NET_PACKET_GCA_GET_NODES) { - return dispatch_packet_get_nodes_request(announce, chat_id, origin_pk, self_pk, data, length, self); - } - - return -1; -} - -/* Add requested online chat members to announce->requests - * - * Returns index of match on success. - * Returns -1 on failure. - */ -static int add_requested_gc_nodes(GC_Announce *announce, const GC_Announce_Node *node, uint64_t req_id, - uint32_t nodes_num) -{ - size_t i; - uint32_t j; - - for (i = 0; i < MAX_GCA_SELF_REQUESTS; ++i) { - if (announce->requests[i].req_id != req_id) { - continue; - } - - for (j = 0; j < nodes_num && j < MAX_GCA_SENT_NODES; ++j) { - if (ipport_isset(&node[j].ip_port) - && !id_equal(announce->requests[i].self_public_key, node[j].public_key)) { - memcpy(announce->requests[i].nodes[j].public_key, node[j].public_key, ENC_PUBLIC_KEY); - ipport_copy(&announce->requests[i].nodes[j].ip_port, &node[j].ip_port); - announce->requests[i].ready = true; - } - } + if (announces) { + remove_announces(gc_announces_list, announces); - if (announce->requests[i].ready && announce->update_addresses) { - (*announce->update_addresses)(announce, announce->requests[i].chat_id, announce->update_addresses_obj); - } - - return i; + return true; } - return -1; + return false; } -static size_t add_announced_nodes_helper(GC_Announce *announce, const uint8_t *chat_id, const GC_Announce_Node node, - size_t idx, bool self) +int get_gc_announces(GC_Announces_List *gc_announces_list, GC_Announce *gc_announces, uint8_t max_nodes, + const uint8_t *chat_id, const uint8_t *except_public_key) { - ipport_copy(&announce->announcements[idx].node.ip_port, &node.ip_port); - memcpy(announce->announcements[idx].node.public_key, node.public_key, ENC_PUBLIC_KEY); - memcpy(announce->announcements[idx].chat_id, chat_id, CHAT_ID_SIZE); - announce->announcements[idx].last_rcvd_ping = mono_time_get(announce->mono_time); - announce->announcements[idx].last_sent_ping = mono_time_get(announce->mono_time); - announce->announcements[idx].time_added = mono_time_get(announce->mono_time); - announce->announcements[idx].self = self; - - return idx; -} - -/* Add announced node to announcements. If no slots are free replace the oldest node. - * - * Returns index of added node. - */ -static size_t add_gc_announced_node(GC_Announce *announce, const uint8_t *chat_id, const GC_Announce_Node node, - bool self) -{ - size_t i, oldest_idx = 0; - uint64_t oldest_announce = 0; + if (!gc_announces || !gc_announces_list || !chat_id || !max_nodes || !except_public_key) { + return -1; + } - for (i = 0; i < MAX_GCA_ANNOUNCED_NODES; ++i) { - if (oldest_announce < announce->announcements[i].time_added) { - oldest_announce = announce->announcements[i].time_added; - oldest_idx = i; - } + GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, chat_id); - if (id_equal(announce->announcements[i].node.public_key, node.public_key) - && chat_id_equal(announce->announcements[i].chat_id, chat_id)) { - return add_announced_nodes_helper(announce, chat_id, node, i, self); - } - - if (!ipport_isset(&announce->announcements[i].node.ip_port)) { - return add_announced_nodes_helper(announce, chat_id, node, i, self); - } + if (!announces) { + return 0; } - return add_announced_nodes_helper(announce, chat_id, node, oldest_idx, self); -} + // TODO: add proper selection + int gc_announces_count = 0, i, j; -/* Gets up to MAX_GCA_SENT_NODES nodes that hold chat_id from announcements and add them to nodes array. - * Returns the number of added nodes. - */ -static uint32_t get_gc_announced_nodes(GC_Announce *announce, const uint8_t *chat_id, GC_Announce_Node *nodes) -{ - size_t i; - uint32_t num = 0; + for (i = 0; i < announces->index && i < MAX_GCA_SAVED_ANNOUNCES_PER_GC && gc_announces_count < max_nodes; ++i) { + int index = i % MAX_GCA_SAVED_ANNOUNCES_PER_GC; - for (i = 0; i < MAX_GCA_ANNOUNCED_NODES; ++i) { - if (!ipport_isset(&announce->announcements[i].node.ip_port)) { + if (!memcmp(except_public_key, &announces->announces[index].base_announce.peer_public_key, ENC_PUBLIC_KEY)) { continue; } - if (chat_id_equal(announce->announcements[i].chat_id, chat_id)) { - memcpy(nodes[num].public_key, announce->announcements[i].node.public_key, ENC_PUBLIC_KEY); - ipport_copy(&nodes[num].ip_port, &announce->announcements[i].node.ip_port); + bool already_added = false; - if (++num == MAX_GCA_SENT_NODES) { + for (j = 0; j < gc_announces_count; ++j) { + if (!memcmp(&gc_announces[j].peer_public_key, + &announces->announces[index].base_announce.peer_public_key, + ENC_PUBLIC_KEY)) { + already_added = true; break; } } - } - - return num; -} - -/* Initiates requests holder for our nodes request responses for chat_id. - * If all slots are full the oldest entry is replaced - */ -static void init_gca_self_request(GC_Announce *announce, const uint8_t *chat_id, uint64_t req_id, - const uint8_t *self_public_key, const uint8_t *self_secret_key) -{ - size_t i, idx = 0; - uint64_t oldest_req = 0; - for (i = 0; i < MAX_GCA_SELF_REQUESTS; ++i) { - if (announce->requests[i].req_id == 0) { - idx = i; - break; - } - - if (oldest_req < announce->requests[i].time_added) { - oldest_req = announce->requests[i].time_added; - idx = i; + if (!already_added) { + memcpy(&gc_announces[gc_announces_count], &announces->announces[index], sizeof(GC_Announce)); + ++gc_announces_count; } } - memset(&announce->requests[idx], 0, sizeof(struct GC_AnnounceRequest)); - announce->requests[idx].req_id = req_id; - announce->requests[idx].time_added = mono_time_get(announce->mono_time); - memcpy(announce->requests[idx].chat_id, chat_id, CHAT_ID_SIZE); - memcpy(announce->requests[idx].self_public_key, self_public_key, ENC_PUBLIC_KEY); - memcpy(announce->requests[idx].self_secret_key, self_secret_key, ENC_SECRET_KEY); + return gc_announces_count; } -/* Adds our own announcement to self_announce. - * - * Returns array index on success. - * Returns -1 if self_announce is full. - */ -static int add_gca_self_announce(GC_Announce *announce, const uint8_t *chat_id, const uint8_t *self_public_key, - const uint8_t *self_secret_key) +int pack_announce(uint8_t *data, uint16_t length, GC_Announce *announce) { - size_t i; - - for (i = 0; i < MAX_GCA_SELF_ANNOUNCEMENTS; ++i) { - if (!announce->self_announce[i].is_set) { - announce->self_announce[i].last_rcvd_ping = mono_time_get(announce->mono_time); - announce->self_announce[i].is_set = true; - memcpy(announce->self_announce[i].chat_id, chat_id, CHAT_ID_SIZE); - memcpy(announce->self_announce[i].self_public_key, self_public_key, ENC_PUBLIC_KEY); - memcpy(announce->self_announce[i].self_secret_key, self_secret_key, ENC_SECRET_KEY); - return i; - } + if (!data || !announce || length < GC_ANNOUNCE_MAX_SIZE) { + return -1; } - return -1; -} - -/* Removes all instances of chat_id from self_announce. */ -static void remove_gca_self_announce(GC_Announce *announce, const uint8_t *chat_id) -{ - size_t i; - - for (i = 0; i < MAX_GCA_SELF_ANNOUNCEMENTS; ++i) { - if (!announce->self_announce[i].is_set) { - continue; - } + uint16_t offset = 0; + memcpy(data + offset, announce->peer_public_key, ENC_PUBLIC_KEY); + offset += ENC_PUBLIC_KEY; - if (chat_id_equal(announce->self_announce[i].chat_id, chat_id)) { - memset(&announce->self_announce[i], 0, sizeof(struct GC_AnnouncedSelf)); - } - } -} + data[offset] = announce->ip_port_is_set; + ++offset; -/* Returns true if a self announce entry exists containing chat_id/self_public_key. - * Returns false otherwise. - */ -static bool gca_self_announce_set(GC_Announce *announce, const uint8_t *chat_id, const uint8_t *public_key) -{ - size_t i; + data[offset] = announce->tcp_relays_count; + ++offset; - for (i = 0; i < MAX_GCA_SELF_ANNOUNCEMENTS; ++i) { - if (!announce->self_announce[i].is_set) { - continue; - } + if (announce->ip_port_is_set) { + int ip_port_length = pack_ip_port(data + offset, length - offset, &announce->ip_port); - if (chat_id_equal(announce->self_announce[i].chat_id, chat_id) - && id_equal(announce->self_announce[i].self_public_key, public_key)) { - return true; + if (ip_port_length == -1) { + return -1; } - } - - return false; -} -/* Announce a new group chat. - * - * Returns a non-negative value on success. - * Returns -1 on failure. - */ -int gca_send_announce_request(GC_Announce *announce, const uint8_t *self_public_key, const uint8_t *self_secret_key, - const uint8_t *chat_id) -{ - DHT *dht = announce->dht; - - if (gca_self_announce_set(announce, chat_id, self_public_key)) { - return 0; + offset += ip_port_length; } - add_gca_self_announce(announce, chat_id, self_public_key, self_secret_key); - - /* packet contains: type, chat_id, node */ - uint8_t data[1 + CHAT_ID_SIZE + sizeof(GC_Announce_Node)]; - data[0] = NET_PACKET_GCA_ANNOUNCE; - memcpy(data + 1, chat_id, CHAT_ID_SIZE); - - GC_Announce_Node self_node; + int nodes_length = pack_nodes(data + offset, length - offset, announce->tcp_relays, announce->tcp_relays_count); - if (make_self_gca_node(dht, &self_node, self_public_key) == -1) { + if (nodes_length == -1) { return -1; } - int node_len = pack_gca_nodes(data + 1 + CHAT_ID_SIZE, sizeof(GC_Announce_Node), &self_node, 1); - - if (node_len <= 0) { - return -1; - } - - uint32_t length = 1 + CHAT_ID_SIZE + node_len; - - if (length > MAX_GCA_PACKET_SIZE) { - return -1; - } - - return dispatch_packet(announce, chat_id, nullptr, dht_get_self_public_key(dht), data, length, NET_PACKET_GCA_ANNOUNCE, - true); + return nodes_length + offset; } -/* Attempts to relay an announce request to close nodes. - * If we are the closest node store the node in announcements (this happens in dispatch_packet_announce_request) - */ -static int handle_gca_request(void *object, IP_Port ipp, const uint8_t *packet, uint16_t length, void *userdata) +int unpack_announce(const uint8_t *data, uint16_t length, GC_Announce *announce) { - GC_Announce *announce = (GC_Announce *)object; - DHT *dht = announce->dht; - - if (length <= GCA_HEADER_SIZE + CRYPTO_MAC_SIZE || length > MAX_GCA_PACKET_SIZE) { - return -1; - } - - uint16_t data_length = length - (GCA_HEADER_SIZE + CRYPTO_MAC_SIZE); - uint16_t d_header_len = 1 + CHAT_ID_SIZE; - - if (data_length <= d_header_len) { + if (!data || !announce || length < GC_ANNOUNCE_MIN_SIZE) { return -1; } - VLA(uint8_t, data, data_length); - uint8_t origin_pk[ENC_PUBLIC_KEY]; - int plain_length = unwrap_gca_packet(dht_get_self_public_key(dht), dht_get_self_secret_key(dht), origin_pk, data, - SIZEOF_VLA(data), packet[0], packet, length); + uint16_t offset = 0; + memcpy(announce->peer_public_key, data + offset, ENC_PUBLIC_KEY); + offset += ENC_PUBLIC_KEY; - if (plain_length != SIZEOF_VLA(data)) { - fprintf(stderr, "unwrap failed in handle_gca_request (plain_length: %u, data size: %u)\n", - (unsigned)plain_length, (unsigned)SIZEOF_VLA(data)); - return -1; - } + announce->ip_port_is_set = data[offset]; + ++offset; - GC_Announce_Node node; + announce->tcp_relays_count = data[offset]; + ++offset; - if (unpack_gca_nodes(&node, 1, nullptr, data + d_header_len, plain_length - d_header_len, 0) != 1) { + if (announce->tcp_relays_count > MAX_ANNOUNCED_TCP_RELAYS) { return -1; } - uint8_t chat_id[CHAT_ID_SIZE]; - memcpy(chat_id, data + 1, CHAT_ID_SIZE); - - return dispatch_packet(announce, chat_id, origin_pk, dht_get_self_public_key(dht), data, plain_length, - NET_PACKET_GCA_ANNOUNCE, false); -} - -/* Creates a DHT request for nodes that hold announcements for chat_id. - * - * Returns a non-negative value on success. - * Returns -1 on failure. - */ -int gca_send_get_nodes_request(GC_Announce *announce, const uint8_t *self_public_key, const uint8_t *self_secret_key, - const uint8_t *chat_id) -{ - DHT *dht = announce->dht; - - /* packet contains: type, chat_id, request_id, node */ - uint8_t data[1 + CHAT_ID_SIZE + RAND_ID_SIZE + sizeof(GC_Announce_Node)]; - data[0] = NET_PACKET_GCA_GET_NODES; - memcpy(data + 1, chat_id, CHAT_ID_SIZE); - - uint64_t request_id = random_u64(); - net_pack_u64(data + 1 + CHAT_ID_SIZE, request_id); + if (announce->ip_port_is_set) { + int ip_port_length = unpack_ip_port(&announce->ip_port, data + offset, length - offset, 0); - GC_Announce_Node self_node; + if (ip_port_length == -1) { + return -1; + } - if (make_self_gca_node(dht, &self_node, self_public_key) == -1) { - return -1; + offset += ip_port_length; } - int node_len = pack_gca_nodes(data + 1 + CHAT_ID_SIZE + RAND_ID_SIZE, sizeof(GC_Announce_Node), &self_node, 1); + uint16_t nodes_length; + int nodes_count = unpack_nodes(announce->tcp_relays, announce->tcp_relays_count, &nodes_length, + data + offset, length - offset, 1); - if (node_len <= 0) { - fprintf(stderr, "pack_nodes failed in send_get_nodes_request\n"); + if (nodes_count != announce->tcp_relays_count) { return -1; } - uint32_t length = 1 + CHAT_ID_SIZE + RAND_ID_SIZE + node_len; - init_gca_self_request(announce, chat_id, request_id, self_public_key, self_secret_key); - - return dispatch_packet(announce, chat_id, nullptr, dht_get_self_public_key(dht), data, length, NET_PACKET_GCA_GET_NODES, - true); + return offset + nodes_length; } -/* Sends nodes that hold chat_id to node that requested them */ -static int send_gca_get_nodes_response(GC_Announce *announce, uint64_t request_id, IP_Port ipp, - const uint8_t *receiver_pk, - GC_Announce_Node *nodes, uint32_t num_nodes) +int pack_public_announce(uint8_t *data, uint16_t length, GC_Public_Announce *announce) { - DHT *dht = announce->dht; - - if (gca_rate_limit(announce)) { - return 0; - } - - /* packet contains: type, num_nodes, nodes, request_id */ - VLA(uint8_t, data, 1 + sizeof(uint32_t) + sizeof(GC_Announce_Node) * num_nodes + RAND_ID_SIZE); - data[0] = NET_PACKET_GCA_SEND_NODES; - net_pack_u32(data + 1, num_nodes); - - int nodes_len = pack_gca_nodes(data + 1 + sizeof(uint32_t), sizeof(GC_Announce_Node) * num_nodes, - nodes, num_nodes); - - if (nodes_len <= 0) { - fprintf(stderr, "pack_gca_nodes failed in send_gca_get_nodes_response (%d)\n", nodes_len); + if (!announce || !data || length < CHAT_ID_SIZE) { return -1; } - uint32_t plain_length = 1 + sizeof(uint32_t) + nodes_len + RAND_ID_SIZE; - net_pack_u64(data + plain_length - RAND_ID_SIZE, request_id); + memcpy(data, announce->chat_public_key, CHAT_ID_SIZE); - VLA(uint8_t, packet, plain_length + RAND_ID_SIZE + GCA_HEADER_SIZE + CRYPTO_MAC_SIZE); - int packet_length = wrap_gca_packet(dht_get_self_public_key(dht), dht_get_self_secret_key(dht), receiver_pk, packet, - SIZEOF_VLA(packet), data, plain_length, NET_PACKET_GCA_SEND_NODES); + int packed_size = pack_announce(data + CHAT_ID_SIZE, length - CHAT_ID_SIZE, &announce->base_announce); - if (packet_length == -1) { - fprintf(stderr, "wrap failed in send_gca_get_nodes_response\n"); + if (packed_size < 0) { return -1; } - /* insert request_id into packet header after the packet type and dht_pk */ - memmove(packet + 1 + ENC_PUBLIC_KEY + RAND_ID_SIZE, packet + 1 + ENC_PUBLIC_KEY, packet_length - 1 - ENC_PUBLIC_KEY); - net_pack_u64(packet + 1 + ENC_PUBLIC_KEY, request_id); - packet_length += RAND_ID_SIZE; - - return sendpacket(dht_get_net(dht), ipp, packet, packet_length); + return packed_size + CHAT_ID_SIZE; } -static int handle_gc_get_announced_nodes_request(void *object, IP_Port ipp, const uint8_t *packet, uint16_t length, - void *userdata) +int unpack_public_announce(uint8_t *data, uint16_t length, GC_Public_Announce *announce) { - GC_Announce *announce = (GC_Announce *)object; - DHT *dht = announce->dht; - - if (length <= GCA_HEADER_SIZE + CRYPTO_MAC_SIZE || length > MAX_GCA_PACKET_SIZE) { + if (length < CHAT_ID_SIZE || !announce || !data) { return -1; } - uint16_t data_length = length - (GCA_HEADER_SIZE + CRYPTO_MAC_SIZE); - uint16_t d_header_len = 1 + CHAT_ID_SIZE + RAND_ID_SIZE; + memcpy(announce->chat_public_key, data, CHAT_ID_SIZE); - if (data_length <= d_header_len) { - return -1; - } - - VLA(uint8_t, data, data_length); - uint8_t origin_pk[ENC_PUBLIC_KEY]; - int plain_length = unwrap_gca_packet(dht_get_self_public_key(dht), dht_get_self_secret_key(dht), origin_pk, data, - SIZEOF_VLA(data), packet[0], packet, length); + int base_announce_size = unpack_announce(data + ENC_PUBLIC_KEY, length - ENC_PUBLIC_KEY, &announce->base_announce); - if (plain_length != SIZEOF_VLA(data)) { - fprintf(stderr, "unwrap failed in handle_gc_get_announced_nodes_request %d\n", plain_length); + if (base_announce_size == -1) { return -1; } - GC_Announce_Node node; - - if (unpack_gca_nodes(&node, 1, nullptr, data + d_header_len, plain_length - d_header_len, 0) != 1) { - fprintf(stderr, "unpack failed in handle_gc_get_announced_nodes_request\n"); - return -1; - } - - GC_Announce_Node nodes[MAX_GCA_SENT_NODES]; - uint32_t num_nodes = get_gc_announced_nodes(announce, data + 1, nodes); - - if (num_nodes) { - uint64_t request_id; - net_unpack_u64(data + 1 + CHAT_ID_SIZE, &request_id); - return send_gca_get_nodes_response(announce, request_id, node.ip_port, node.public_key, nodes, num_nodes); - } - - uint8_t chat_id[CHAT_ID_SIZE]; - memcpy(chat_id, data + 1, CHAT_ID_SIZE); - - return dispatch_packet(announce, chat_id, origin_pk, dht_get_self_public_key(dht), data, plain_length, - NET_PACKET_GCA_GET_NODES, false); + return base_announce_size + CHAT_ID_SIZE; } -static int handle_gca_get_nodes_response(void *object, IP_Port ipp, const uint8_t *packet, uint16_t length, - void *userdata) +int pack_announces_list(uint8_t *data, uint16_t length, GC_Announce *announces, uint8_t announces_count, + size_t *processed) { - GC_Announce *announce = (GC_Announce *)object; - - if (length <= GCA_HEADER_SIZE + CRYPTO_MAC_SIZE + RAND_ID_SIZE || length > MAX_GCA_PACKET_SIZE) { + if (!data || !announces) { return -1; } - uint16_t data_length = length - (GCA_HEADER_SIZE + CRYPTO_MAC_SIZE + RAND_ID_SIZE); - - if (data_length <= 1 + sizeof(uint32_t) + RAND_ID_SIZE) { - return -1; - } - - VLA(uint8_t, data, data_length); - uint8_t public_key[ENC_PUBLIC_KEY]; - - uint64_t request_id; - net_unpack_u64(packet + 1 + ENC_PUBLIC_KEY, &request_id); + uint16_t offset = 0; + int i; - int plain_length = 0; - size_t i; + for (i = 0; i < announces_count; ++i) { + int packed_length = pack_announce(data + offset, length - offset, &announces[i]); - for (i = 0; i < MAX_GCA_SELF_REQUESTS; ++i) { - if (announce->requests[i].req_id == request_id) { - plain_length = unwrap_gca_packet(announce->requests[i].self_public_key, - announce->requests[i].self_secret_key, - public_key, data, SIZEOF_VLA(data), - packet[0], packet, length); - break; - } - } - - if (plain_length != SIZEOF_VLA(data)) { - return -1; - } - - uint64_t request_id_enc; - net_unpack_u64(data + plain_length - RAND_ID_SIZE, &request_id_enc); - - if (request_id != request_id_enc) { - return -1; - } - - uint32_t num_nodes; - net_unpack_u32(data + 1, &num_nodes); - - /* this should never happen so assume it's malicious and ignore */ - if (num_nodes > MAX_GCA_SENT_NODES || num_nodes == 0) { - return -1; - } - - VLA(GC_Announce_Node, nodes, num_nodes); - int num_packed = unpack_gca_nodes(nodes, num_nodes, nullptr, data + 1 + sizeof(uint32_t), - plain_length - 1 - sizeof(uint32_t), 0); - - if (num_packed != num_nodes) { - fprintf(stderr, "unpack failed in handle_gca_get_nodes_response (got %d, expected %u)\n", num_packed, num_nodes); - return -1; - } - - if (add_requested_gc_nodes(announce, nodes, request_id, num_nodes) == -1) { - return -1; - } - - return 0; -} - -/* Retrieves nodes for chat_id (nodes must already be obtained via gca_send_announce_request). - * - * returns the number of nodes found. - */ -size_t gca_get_requested_nodes(GC_Announce *announce, const uint8_t *chat_id, GC_Announce_Node *nodes) -{ - size_t i, j, k = 0; - - for (i = 0; i < MAX_GCA_SELF_REQUESTS; ++i) { - if (!(announce->requests[i].ready == 1 && announce->requests[i].req_id != 0)) { - continue; - } - - if (!chat_id_equal(announce->requests[i].chat_id, chat_id)) { - continue; - } - - for (j = 0; j < MAX_GCA_SENT_NODES; ++j) { - if (ipport_isset(&announce->requests[i].nodes[j].ip_port)) { - memcpy(nodes[k].public_key, announce->requests[i].nodes[j].public_key, ENC_PUBLIC_KEY); - ipport_copy(&nodes[k].ip_port, &announce->requests[i].nodes[j].ip_port); - - if (++k == MAX_GCA_SENT_NODES) { - return k; - } - } + if (packed_length == -1) { + return -1; } - } - - return k; -} - -static int handle_gca_ping_response(void *object, IP_Port ipp, const uint8_t *packet, uint16_t length, void *userdata) -{ - GC_Announce *announce = (GC_Announce *)object; - DHT *dht = announce->dht; - - if (length != GCA_PING_RESPONSE_DHT_SIZE) { - return -1; - } - - uint8_t data[GCA_PING_RESPONSE_PLAIN_SIZE]; - uint8_t public_key[ENC_PUBLIC_KEY]; - - int plain_length = unwrap_gca_packet(dht_get_self_public_key(dht), dht_get_self_secret_key(dht), public_key, data, - sizeof(data), packet[0], packet, length); - - if (plain_length != GCA_PING_RESPONSE_PLAIN_SIZE) { - return -1; - } - - uint64_t ping_id; - memcpy(&ping_id, data + 1, RAND_ID_SIZE); - - size_t i; - - for (i = 0; i < MAX_GCA_ANNOUNCED_NODES; ++i) { - if (announce->announcements[i].ping_id == ping_id) { - announce->announcements[i].ping_id = 0; - - if (!ipport_isset(&announce->announcements[i].node.ip_port)) { - return -1; - } - announce->announcements[i].last_rcvd_ping = mono_time_get(announce->mono_time); - return 0; - } + offset += packed_length; } - return -1; -} - -static int send_gca_ping_response(DHT *dht, IP_Port ipp, const uint8_t *data, const uint8_t *rcv_pk) -{ - uint8_t response[GCA_PING_RESPONSE_PLAIN_SIZE]; - response[0] = NET_PACKET_GCA_PING_RESPONSE; - memcpy(response + 1, data + 1, GCA_PING_RESPONSE_PLAIN_SIZE - 1); - - uint8_t packet[GCA_PING_RESPONSE_DHT_SIZE]; - int len = wrap_gca_packet(dht_get_self_public_key(dht), dht_get_self_secret_key(dht), rcv_pk, packet, sizeof(packet), - response, GCA_PING_RESPONSE_PLAIN_SIZE, NET_PACKET_GCA_PING_RESPONSE); - - if (len == -1) { - return -1; + if (processed) { + *processed = offset; } - return sendpacket(dht_get_net(dht), ipp, packet, len); + return announces_count; } -static int handle_gca_ping_request(void *object, IP_Port ipp, const uint8_t *packet, uint16_t length, void *userdata) +int unpack_announces_list(const uint8_t *data, uint16_t length, GC_Announce *announces, uint8_t max_announces_count, + size_t *processed) { - GC_Announce *announce = (GC_Announce *)object; - DHT *dht = announce->dht; - - if (length != GCA_PING_REQUEST_DHT_SIZE) { + if (!data || !announces) { return -1; } - uint8_t self_public_key[ENC_PUBLIC_KEY]; - memcpy(self_public_key, packet + 1 + ENC_PUBLIC_KEY, ENC_PUBLIC_KEY); + uint16_t offset = 0; + int i, announces_count = 0; - size_t i; - bool node_found = false; - - for (i = 0; i < MAX_GCA_SELF_ANNOUNCEMENTS; ++i) { - if (!announce->self_announce[i].is_set) { - continue; - } + for (i = 0; i < max_announces_count && length > offset; ++i) { + int unpacked_length = unpack_announce(data + offset, length - offset, &announces[i]); - if (memcmp(self_public_key, announce->self_announce[i].self_public_key, ENC_PUBLIC_KEY) == 0) { - node_found = true; - break; + if (unpacked_length == -1) { + fprintf(stderr, "unpack error: %d %d\n", length, offset); + return -1; } - } - if (!node_found) { - fprintf(stderr, "handle announce ping request failed\n"); - return -1; + offset += unpacked_length; + ++announces_count; } - uint8_t data[GCA_PING_REQUEST_PLAIN_SIZE]; - uint8_t public_key[ENC_PUBLIC_KEY]; - int plain_length = unwrap_gca_packet(dht_get_self_public_key(dht), announce->self_announce[i].self_secret_key, - public_key, data, sizeof(data), packet[0], packet, length); - - if (plain_length != GCA_PING_REQUEST_PLAIN_SIZE) { - fprintf(stderr, "handle ping request unwrap failed\n"); - return -1; + if (processed) { + *processed = offset; } - announce->self_announce[i].last_rcvd_ping = mono_time_get(announce->mono_time); - - return send_gca_ping_response(dht, ipp, data, public_key); -} - -static int send_gca_ping_request(DHT *dht, GC_Announce_Node *node, uint64_t ping_id) -{ - uint8_t data[GCA_PING_REQUEST_PLAIN_SIZE]; - data[0] = NET_PACKET_GCA_PING_REQUEST; - memcpy(data + 1, &ping_id, RAND_ID_SIZE); - - uint8_t packet[GCA_PING_REQUEST_DHT_SIZE]; - int len = wrap_gca_packet(dht_get_self_public_key(dht), dht_get_self_secret_key(dht), node->public_key, packet, - sizeof(packet), data, GCA_PING_REQUEST_PLAIN_SIZE, NET_PACKET_GCA_PING_REQUEST); - - if (len == -1) { - return -1; - } - - /* insert recipient's public key into packet header after the packet type and dht_pk */ - memmove(packet + 1 + ENC_PUBLIC_KEY + ENC_PUBLIC_KEY, packet + 1 + ENC_PUBLIC_KEY, len - 1 - ENC_PUBLIC_KEY); - memcpy(packet + 1 + ENC_PUBLIC_KEY, node->public_key, ENC_PUBLIC_KEY); - len += ENC_PUBLIC_KEY; - - return sendpacket(dht_get_net(dht), node->ip_port, packet, len); -} - -/* Increases rate limit. - * - * Returns true if we've hit the rate limit. - */ -static bool gca_rate_limit(GC_Announce *announce) -{ - ++announce->packet_relay_rate; - - return announce->packet_relay_rate >= GCA_RELAY_RATE_LIMIT; -} - -/* Keeps track of the rate at which we're sending announce packets. - * - * This is very basic and is only used to hinder spam and infinite DHT loops which may be - * caused by malicious clients or mismatched versions of toxcore. - */ -static void gca_do_rate_limit(GC_Announce *announce) -{ - if (announce->packet_relay_rate == 0) { - return; - } - - uint64_t tm = mono_time_get(announce->mono_time); - - if (announce->relay_rate_timer < tm) { - announce->relay_rate_timer = tm; - announce->packet_relay_rate /= 2; - } + return announces_count; } -static void ping_gca_nodes(GC_Announce *announce) +GC_Peer_Announce *add_gc_announce(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list, + const GC_Public_Announce *announce) { - size_t i; - - for (i = 0; i < MAX_GCA_ANNOUNCED_NODES; ++i) { - if (!ipport_isset(&announce->announcements[i].node.ip_port)) { - continue; - } - - if (announce->announcements[i].self - || !mono_time_is_timeout(announce->mono_time, announce->announcements[i].last_sent_ping, GCA_PING_INTERVAL)) { - continue; - } - - uint64_t ping_id = random_u64(); - announce->announcements[i].ping_id = ping_id; - announce->announcements[i].last_sent_ping = mono_time_get(announce->mono_time); - send_gca_ping_request(announce->dht, &announce->announcements[i].node, ping_id); + if (!gc_announces_list || !announce) { + return nullptr; } -} -#define SELF_ANNOUNCE_TIMEOUT GCA_NODES_EXPIRATION + GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, announce->chat_public_key); -/* Checks time of last received ping request for self announces and renews the announcement if necessary */ -static void renew_gca_self_announces(GC_Announce *announce) -{ - size_t i; + if (!announces) { + ++gc_announces_list->announces_count; + announces = (GC_Announces *)malloc(sizeof(GC_Announces)); + announces->index = 0; + announces->prev_announce = nullptr; - for (i = 0; i < MAX_GCA_SELF_ANNOUNCEMENTS; ++i) { - if (!announce->self_announce[i].is_set) { - continue; + if (gc_announces_list->announces) { + gc_announces_list->announces->prev_announce = announces; } - if (mono_time_is_timeout(announce->mono_time, announce->self_announce[i].last_rcvd_ping, SELF_ANNOUNCE_TIMEOUT)) { - announce->self_announce[i].last_rcvd_ping = mono_time_get(announce->mono_time); - announce->self_announce[i].is_set = false; - gca_send_announce_request(announce, announce->self_announce[i].self_public_key, - announce->self_announce[i].self_secret_key, - announce->self_announce[i].chat_id); - } + announces->next_announce = gc_announces_list->announces; + gc_announces_list->announces = announces; + memcpy(announces->chat_id, announce->chat_public_key, CHAT_ID_SIZE); } -} - -static void check_gca_node_timeouts(GC_Announce *announce) -{ - size_t i; - - for (i = 0; i < MAX_GCA_ANNOUNCED_NODES; ++i) { - if (!ipport_isset(&announce->announcements[i].node.ip_port)) { - continue; - } - if (!announce->announcements[i].self - && mono_time_is_timeout(announce->mono_time, announce->announcements[i].last_rcvd_ping, GCA_NODES_EXPIRATION)) { - memset(&announce->announcements[i], 0, sizeof(struct GC_AnnouncedNode)); - } - } -} - -void do_gca(GC_Announce *announce) -{ - gca_do_rate_limit(announce); - ping_gca_nodes(announce); - check_gca_node_timeouts(announce); - renew_gca_self_announces(announce); + uint64_t index = announces->index % MAX_GCA_SAVED_ANNOUNCES_PER_GC; + announces->last_announce_received_timestamp = mono_time_get(mono_time); + GC_Peer_Announce *gc_peer_announce = &announces->announces[index]; + memcpy(&gc_peer_announce->base_announce, &announce->base_announce, sizeof(GC_Announce)); + gc_peer_announce->timestamp = mono_time_get(mono_time); + ++announces->index; + // TODO; lock + return gc_peer_announce; } -/* Removes peer with public_key in chat_id's group from requests list */ -void gca_peer_cleanup(GC_Announce *announce, const uint8_t *chat_id, const uint8_t *peer_pk) +bool is_valid_announce(const GC_Announce *announce) { - size_t i, j; - - for (i = 0; i < MAX_GCA_SELF_REQUESTS; ++i) { - if (!(announce->requests[i].ready && announce->requests[i].req_id != 0)) { - continue; - } - - if (!chat_id_equal(announce->requests[i].chat_id, chat_id)) { - continue; - } - - for (j = 0; j < MAX_GCA_SENT_NODES; ++j) { - if (id_equal(announce->requests[i].nodes[j].public_key, peer_pk)) { - memset(&announce->requests[i].nodes[j], 0, sizeof(GC_Announce_Node)); - return; - } - } + if (!announce) { + return false; } -} - -void gca_cleanup(GC_Announce *announce, const uint8_t *chat_id) -{ - size_t i; - /* Remove self announcements for chat_id */ - for (i = 0; i < MAX_GCA_ANNOUNCED_NODES; ++i) { - if (announce->announcements[i].self && chat_id_equal(announce->announcements[i].chat_id, chat_id)) { - memset(&announce->announcements[i], 0, sizeof(struct GC_AnnouncedNode)); - } + if (announce->tcp_relays_count) { + return true; } - remove_gca_self_announce(announce, chat_id); -} - -GC_Announce *new_gca(Mono_Time *mono_time, DHT *dht) -{ - GC_Announce *announce = (GC_Announce *)calloc(1, sizeof(GC_Announce)); - - if (announce == nullptr) { - return nullptr; + if (!announce->ip_port_is_set) { + return false; } - announce->mono_time = mono_time; - announce->dht = dht; - networking_registerhandler(dht_get_net(announce->dht), NET_PACKET_GCA_ANNOUNCE, &handle_gca_request, announce); - networking_registerhandler(dht_get_net(announce->dht), NET_PACKET_GCA_GET_NODES, &handle_gc_get_announced_nodes_request, - announce); - networking_registerhandler(dht_get_net(announce->dht), NET_PACKET_GCA_SEND_NODES, &handle_gca_get_nodes_response, - announce); - networking_registerhandler(dht_get_net(announce->dht), NET_PACKET_GCA_PING_REQUEST, &handle_gca_ping_request, announce); - networking_registerhandler(dht_get_net(announce->dht), NET_PACKET_GCA_PING_RESPONSE, &handle_gca_ping_response, - announce); - return announce; + return (bool)ip_is_lan(announce->ip_port.ip); } - -void kill_gca(GC_Announce *announce) -{ - networking_registerhandler(dht_get_net(announce->dht), NET_PACKET_GCA_ANNOUNCE, nullptr, nullptr); - networking_registerhandler(dht_get_net(announce->dht), NET_PACKET_GCA_GET_NODES, nullptr, nullptr); - networking_registerhandler(dht_get_net(announce->dht), NET_PACKET_GCA_SEND_NODES, nullptr, nullptr); - networking_registerhandler(dht_get_net(announce->dht), NET_PACKET_GCA_PING_REQUEST, nullptr, nullptr); - networking_registerhandler(dht_get_net(announce->dht), NET_PACKET_GCA_PING_RESPONSE, nullptr, nullptr); - - free(announce); -} - -#endif /* VANILLA_NACL */ diff --git a/toxcore/group_announce.h b/toxcore/group_announce.h index 3ebe9165f8..a48debb0d8 100644 --- a/toxcore/group_announce.h +++ b/toxcore/group_announce.h @@ -12,137 +12,89 @@ #include "DHT.h" #include "stdbool.h" -typedef struct GC_Announce GC_Announce; +#define MAX_GCA_SAVED_ANNOUNCES_PER_GC 100 +#define GC_ANNOUNCE_SAVING_TIMEOUT 30 +#define MAX_ANNOUNCED_TCP_RELAYS 1 +#define MAX_SENT_ANNOUNCES 4 +#define GC_ANNOUNCE_MIN_SIZE (ENC_PUBLIC_KEY + 2) +#define GC_ANNOUNCE_MAX_SIZE (sizeof(GC_Announce)) +#define GC_PUBLIC_ANNOUNCE_MAX_SIZE (sizeof(GC_Public_Announce)) -#define MAX_GCA_SELF_REQUESTS 30 -#define MAX_GCA_ANNOUNCED_NODES 30 -#define MAX_GCA_SELF_ANNOUNCEMENTS 30 -#define MAX_GCA_SENT_NODES 4 +typedef struct GC_Announce GC_Announce; +typedef struct GC_Peer_Announce GC_Peer_Announce; +typedef struct GC_Announces GC_Announces; +typedef struct GC_Announces_List GC_Announces_List; +typedef struct GC_Public_Announce GC_Public_Announce; -typedef struct GC_Announce_Node { - uint8_t public_key[ENC_PUBLIC_KEY]; +// Base announce +struct GC_Announce { + Node_format tcp_relays[MAX_ANNOUNCED_TCP_RELAYS]; + uint8_t tcp_relays_count; + uint8_t ip_port_is_set; IP_Port ip_port; -} GC_Announce_Node; + uint8_t peer_public_key[ENC_PUBLIC_KEY]; +}; -/* Holds nodes that we receive when we send a request. Used to join groups */ -struct GC_AnnounceRequest { - GC_Announce_Node nodes[MAX_GCA_SENT_NODES]; - uint64_t req_id; - uint64_t time_added; - uint8_t chat_id[CHAT_ID_SIZE]; - uint8_t self_public_key[ENC_PUBLIC_KEY]; - uint8_t self_secret_key[ENC_SECRET_KEY]; - bool ready; +// Peer announce for specific group +struct GC_Peer_Announce { + GC_Announce base_announce; + + uint64_t timestamp; }; -/* Holds announced nodes we get via DHT announcements */ -struct GC_AnnouncedNode { - uint8_t chat_id[CHAT_ID_SIZE]; - GC_Announce_Node node; - uint64_t last_rcvd_ping; - uint64_t last_sent_ping; - uint64_t time_added; - uint64_t ping_id; - bool self; /* true if this is our own announcement; will never be pinged or timeout */ +// Used for announces in public groups +struct GC_Public_Announce { + GC_Announce base_announce; + + uint8_t chat_public_key[ENC_PUBLIC_KEY]; }; -/* Holds our own announcements when we join a group. - * Currently will only keep track of up to MAX_GCA_SELF_ANNOUNCEMENTS groups at once. - */ -struct GC_AnnouncedSelf { +struct GC_Announces { uint8_t chat_id[CHAT_ID_SIZE]; - uint8_t self_public_key[ENC_PUBLIC_KEY]; - uint8_t self_secret_key[ENC_SECRET_KEY]; - uint64_t last_rcvd_ping; - bool is_set; + uint64_t index; + uint64_t last_announce_received_timestamp; + + GC_Peer_Announce announces[MAX_GCA_SAVED_ANNOUNCES_PER_GC]; + + GC_Announces *next_announce; + GC_Announces *prev_announce; }; -typedef void update_addresses_cb(GC_Announce *announce, const uint8_t *chat_id, void *user_data); +struct GC_Announces_List { + GC_Announces *announces; + int announces_count; +}; -struct GC_Announce { - Mono_Time *mono_time; - DHT *dht; - update_addresses_cb *update_addresses; - void *update_addresses_obj; - struct GC_AnnouncedNode announcements[MAX_GCA_ANNOUNCED_NODES]; - struct GC_AnnounceRequest requests[MAX_GCA_SELF_REQUESTS]; - struct GC_AnnouncedSelf self_announce[MAX_GCA_SELF_ANNOUNCEMENTS]; +GC_Announces_List *new_gca_list(void); - uint32_t packet_relay_rate; - uint64_t relay_rate_timer; -}; +void kill_gca(GC_Announces_List *announces_list); -/* Initiate the process of announcing a group to the DHT. - * - * announce: announce object we're operating on. - * self_public_key: encryption public key of the peer announcing its presence - * self_secret_key: encryption secret key of the peer - * chat_id: chat_id of the group (chat public signature key) - * - * Returns a non-negative value on success. - * Returns -1 on failure. - */ -int gca_send_announce_request(GC_Announce *announce, const uint8_t *self_public_key, - const uint8_t *self_secret_key, const uint8_t *chat_id); +void do_gca(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list); -/* Creates a DHT request for nodes that hold announcements for chat_id. - * - * Returns a non-negative value on success. - * Returns -1 on failure. - */ -int gca_send_get_nodes_request(GC_Announce *announce, const uint8_t *self_public_key, - const uint8_t *self_secret_key, const uint8_t *chat_id); +bool cleanup_gca(GC_Announces_List *announces_list, const uint8_t *chat_id); -/* Retrieves nodes for chat_id (nodes must already be obtained via gca_send_announce_request). - * - * returns the number of nodes found. - */ -size_t gca_get_requested_nodes(GC_Announce *announce, const uint8_t *chat_id, GC_Announce_Node *nodes); -/* Main group announce loop: Pings nodes and checks timeouts. */ -void do_gca(GC_Announce *announce); +int get_gc_announces(GC_Announces_List *gc_announces_list, GC_Announce *gc_announces, uint8_t max_nodes, + const uint8_t *chat_id, const uint8_t *except_public_key); -/* Removes peer with public_key in chat_id's group from requests list */ -void gca_peer_cleanup(GC_Announce *announce, const uint8_t *chat_id, const uint8_t *peer_pk); +GC_Peer_Announce *add_gc_announce(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list, + const GC_Public_Announce *announce); -/* Cleans up announcements related to chat_id (call on group exit or when privacy state is set to private) */ -void gca_cleanup(GC_Announce *announce, const uint8_t *chat_id); +int pack_announce(uint8_t *data, uint16_t length, GC_Announce *announce); -GC_Announce *new_gca(Mono_Time *mono_time, DHT *dht); +int unpack_announce(const uint8_t *data, uint16_t length, GC_Announce *announce); -/* Called when associated Messenger object is killed. */ -void kill_gca(GC_Announce *announce); +int pack_announces_list(uint8_t *data, uint16_t length, GC_Announce *announces, uint8_t announces_count, + size_t *processed); -/* Copies your own ip_port structure to dest. (TODO: This should probably go somewhere else) - * - * Return 0 on succcess. - * Return -1 on failure. - */ -int ipport_self_copy(const DHT *dht, IP_Port *dest); +int unpack_announces_list(const uint8_t *data, uint16_t length, GC_Announce *announces, uint8_t max_announces_count, + size_t *processed); -/* Creates a GC_Announce_Node using client_id and your own IP_Port struct - * - * Return 0 on success. - * Return -1 on failure. - */ -int make_self_gca_node(const DHT *dht, GC_Announce_Node *node, const uint8_t *client_id); +int pack_public_announce(uint8_t *data, uint16_t length, GC_Public_Announce *announce); -/* Pack number of nodes into data of maxlength length. - * - * return length of packed nodes on success. - * return -1 on failure. - */ -int pack_gca_nodes(uint8_t *data, uint16_t length, const GC_Announce_Node *nodes, uint32_t number); - -/* Unpack data of length into nodes of size max_num_nodes. - * Put the length of the data processed in processed_data_len. - * tcp_enabled sets if TCP nodes are expected (true) or not (false). - * - * return number of unpacked nodes on success. - * return -1 on failure. - */ -int unpack_gca_nodes(GC_Announce_Node *nodes, uint32_t max_num_nodes, uint16_t *processed_data_len, - const uint8_t *data, uint16_t length, uint8_t tcp_enabled); +int unpack_public_announce(uint8_t *data, uint16_t length, GC_Public_Announce *announce); + +bool is_valid_announce(const GC_Announce *announce); #endif /* GROUP_ANNOUNCE_H */ diff --git a/toxcore/group_chats.c b/toxcore/group_chats.c index 4b767cd843..dcd0c52a52 100644 --- a/toxcore/group_chats.c +++ b/toxcore/group_chats.c @@ -24,6 +24,7 @@ #include "LAN_discovery.h" #include "util.h" #include "Messenger.h" +#include "friend_connection.h" #ifndef VANILLA_NACL @@ -33,13 +34,13 @@ enum { GC_MAX_PACKET_PADDING = 8, GC_PLAIN_HS_PACKET_SIZE = sizeof(uint8_t) + HASH_ID_BYTES + ENC_PUBLIC_KEY + SIG_PUBLIC_KEY - + sizeof(uint8_t) + sizeof(uint8_t), + + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t), GC_ENCRYPTED_HS_PACKET_SIZE = sizeof(uint8_t) + HASH_ID_BYTES + ENC_PUBLIC_KEY + CRYPTO_NONCE_SIZE + GC_PLAIN_HS_PACKET_SIZE + CRYPTO_MAC_SIZE, GC_PACKED_SHARED_STATE_SIZE = EXT_PUBLIC_KEY + sizeof(uint32_t) + MAX_GC_GROUP_NAME_SIZE + sizeof(uint16_t) - + sizeof(uint8_t) + sizeof(uint16_t) + MAX_GC_PASSWD_SIZE + + sizeof(uint8_t) + sizeof(uint16_t) + MAX_GC_PASSWORD_SIZE + GC_MODERATION_HASH_SIZE + sizeof(uint32_t), /* Minimum size of a topic packet; includes topic length, public signature key, and the topic version */ @@ -73,13 +74,17 @@ static uint16_t gc_packet_padding_length(uint16_t length) return (MAX_GC_PACKET_SIZE - length) % GC_MAX_PACKET_PADDING; } -static int groupnumber_valid(const GC_Session *c, int groupnumber); -static int peer_add(Messenger *m, int groupnumber, IP_Port *ipp, const uint8_t *public_key); -static int peer_update(Messenger *m, int groupnumber, GC_GroupPeer *peer, uint32_t peernumber); +static bool group_number_valid(const GC_Session *c, int group_number); +static int peer_add(Messenger *m, int group_number, IP_Port *ipp, const uint8_t *public_key); +static int peer_update(Messenger *m, int group_number, GC_GroupPeer *peer, uint32_t peer_number); static int group_delete(GC_Session *c, GC_Chat *chat); -static int get_nick_peernumber(const GC_Chat *chat, const uint8_t *nick, uint16_t length); -static int sync_gc_announced_nodes(const GC_Session *c, GC_Chat *chat); +static void group_cleanup(GC_Session *c, GC_Chat *chat); +static int get_nick_peer_number(const GC_Chat *chat, const uint8_t *nick, uint16_t length); static bool group_exists(const GC_Session *c, const uint8_t *chat_id); +static int save_tcp_relay(GC_Connection *gconn, Node_format *node); +int gcc_copy_tcp_relay(GC_Connection *gconn, Node_format *node); +static void add_tcp_relays_to_chat(Messenger *m, GC_Chat *chat); + typedef enum Group_Handshake_Packet_Type { GH_REQUEST, @@ -91,19 +96,71 @@ typedef enum Group_Handshake_Request_Type { HS_PEER_INFO_EXCHANGE, } Group_Handshake_Request_Type; -// for debugging -void print_peer(const GC_GroupPeer *peer, const GC_Connection *gconn); -void print_peer(const GC_GroupPeer *peer, const GC_Connection *gconn) + +void pack_group_info(const GC_Chat *chat, Saved_Group *temp, bool can_use_cached_value) +{ + // copy info from cached save struct if possible (for disconnected groups only) + if (can_use_cached_value && chat->save) { + memcpy(temp, chat->save, sizeof(Saved_Group)); + + return; + } + + memset(temp, 0, sizeof(Saved_Group)); + + memcpy(temp->founder_public_key, chat->shared_state.founder_public_key, EXT_PUBLIC_KEY); + temp->group_name_length = net_htons(chat->shared_state.group_name_len); + memcpy(temp->group_name, chat->shared_state.group_name, MAX_GC_GROUP_NAME_SIZE); + temp->privacy_state = chat->shared_state.privacy_state; + temp->maxpeers = net_htons(chat->shared_state.maxpeers); + temp->password_length = net_htons(chat->shared_state.password_length); + memcpy(temp->password, chat->shared_state.password, MAX_GC_PASSWORD_SIZE); + memcpy(temp->mod_list_hash, chat->shared_state.mod_list_hash, GC_MODERATION_HASH_SIZE); + temp->shared_state_version = net_htonl(chat->shared_state.version); + memcpy(temp->shared_state_signature, chat->shared_state_sig, SIGNATURE_SIZE); + + temp->topic_length = net_htons(chat->topic_info.length); + memcpy(temp->topic, chat->topic_info.topic, MAX_GC_TOPIC_SIZE); + memcpy(temp->topic_public_sig_key, chat->topic_info.public_sig_key, SIG_PUBLIC_KEY); + temp->topic_version = net_htonl(chat->topic_info.version); + memcpy(temp->topic_signature, chat->topic_sig, SIGNATURE_SIZE); + + memcpy(temp->chat_public_key, chat->chat_public_key, EXT_PUBLIC_KEY); + memcpy(temp->chat_secret_key, chat->chat_secret_key, EXT_SECRET_KEY); /* empty for non-founders */ + + uint16_t num_addrs = gc_copy_peer_addrs(chat, temp->addrs, GROUP_SAVE_MAX_PEERS); + temp->num_addrs = net_htons(num_addrs); + + temp->num_mods = net_htons(chat->moderation.num_mods); + mod_list_pack(chat, temp->mod_list); + + bool is_manually_disconnected = chat->connection_state == CS_MANUALLY_DISCONNECTED; + temp->group_connection_state = is_manually_disconnected ? SGCS_DISCONNECTED : SGCS_CONNECTED; + + memcpy(temp->self_public_key, chat->self_public_key, EXT_PUBLIC_KEY); + memcpy(temp->self_secret_key, chat->self_secret_key, EXT_SECRET_KEY); + memcpy(temp->self_nick, chat->group[0].nick, MAX_GC_NICK_SIZE); + temp->self_nick_length = net_htons(chat->group[0].nick_length); + temp->self_role = chat->group[0].role; + temp->self_status = chat->group[0].status; +} + +static bool is_peer_confirmed(const GC_Chat *chat, const uint8_t *peer_pk) { - char ip_str[IP_NTOA_LEN]; - fprintf(stderr, "ENC PK: %s\n", id_toa(gconn->addr.public_key)); - fprintf(stderr, "SIG PK: %s\n", id_toa(get_sig_pk(gconn->addr.public_key))); - fprintf(stderr, "IP: %s\n", ip_ntoa(&gconn->addr.ip_port.ip, ip_str, sizeof(ip_str))); - fprintf(stderr, "Nick: %s\n", peer->nick); - fprintf(stderr, "Nick len: %u\n", peer->nick_len); - fprintf(stderr, "Status: %u\n", peer->status); - fprintf(stderr, "Role: %u\n", peer->role); - fprintf(stderr, "Ignore: %d\n", peer->ignore); + int i; + + for (i = 0; i < MAX_GC_CONFIRMED_PEERS; ++i) { + if (!memcmp(chat->confirmed_peers[i], peer_pk, ENC_PUBLIC_KEY)) { // peer in our list + return true; + } + } + + return false; +} + +bool is_public_chat(const GC_Chat *chat) +{ + return chat->shared_state.privacy_state == GI_PUBLIC; } static GC_Chat *get_chat_by_hash(GC_Session *c, uint32_t hash) @@ -137,7 +194,7 @@ static uint32_t get_chat_id_hash(const uint8_t *chat_id) /* Check if peer with the public encryption key is in peer list. * - * return peernumber if peer is in chat. + * return peer_number if peer is in chat. * return -1 if peer is not in chat. */ static int get_peernum_of_enc_pk(const GC_Chat *chat, const uint8_t *public_enc_key) @@ -155,7 +212,7 @@ static int get_peernum_of_enc_pk(const GC_Chat *chat, const uint8_t *public_enc_ /* Check if peer with the public signature key is in peer list. * - * return peernumber if peer is in chat. + * return peer_number if peer is in chat. * return -1 if peer is not in chat. */ static int get_peernum_of_sig_pk(const GC_Chat *chat, const uint8_t *public_sig_key) @@ -176,19 +233,19 @@ static int get_peernum_of_sig_pk(const GC_Chat *chat, const uint8_t *public_sig_ * Returns 0 if role is valid. * Returns -1 if role is invalid. */ -static int validate_gc_peer_role(const GC_Chat *chat, uint32_t peernumber) +static int validate_gc_peer_role(const GC_Chat *chat, uint32_t peer_number) { - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return -1; } - if (chat->group[peernumber].role >= GR_INVALID) { + if (chat->group[peer_number].role >= GR_INVALID) { return -1; } - switch (chat->group[peernumber].role) { + switch (chat->group[peer_number].role) { case GR_FOUNDER: { if (memcmp(chat->shared_state.founder_public_key, gconn->addr.public_key, ENC_PUBLIC_KEY) != 0) { return -1; @@ -215,7 +272,7 @@ static int validate_gc_peer_role(const GC_Chat *chat, uint32_t peernumber) case GR_OBSERVER: { /* Don't validate self as this is called when we don't have the sanctions list yet */ - if (!sanctions_list_is_observer(chat, gconn->addr.public_key) && peernumber != 0) { + if (!sanctions_list_is_observer(chat, gconn->addr.public_key) && peer_number != 0) { return -1; } @@ -230,16 +287,16 @@ static int validate_gc_peer_role(const GC_Chat *chat, uint32_t peernumber) return 0; } -/* Returns true if peernumber exists */ -bool peernumber_valid(const GC_Chat *chat, int peernumber) +/* Returns true if peer_number exists */ +bool peer_number_valid(const GC_Chat *chat, int peer_number) { - return peernumber >= 0 && peernumber < chat->numpeers; + return peer_number >= 0 && peer_number < chat->numpeers; } -/* Returns the peernumber of the peer with peer_id. +/* Returns the peer_number of the peer with peer_id. * Returns -1 if peer_id is invalid. */ -static int get_peernumber_of_peer_id(const GC_Chat *chat, uint32_t peer_id) +static int get_peer_number_of_peer_id(const GC_Chat *chat, uint32_t peer_id) { uint32_t i; @@ -261,7 +318,7 @@ static uint32_t get_new_peer_id(const GC_Chat *chat) { uint32_t new_id = random_u32(); - while (get_peernumber_of_peer_id(chat, new_id) != -1) { + while (get_peer_number_of_peer_id(chat, new_id) != -1) { new_id = random_u32(); } @@ -277,7 +334,6 @@ static bool peer_pk_hash_match(GC_Connection *gconn, uint32_t sender_pk_hash) static void self_gc_connected(const Mono_Time *mono_time, GC_Chat *chat) { chat->connection_state = CS_CONNECTED; - chat->gcc[0].time_added = mono_time_get(mono_time); } /* Sets the password for the group (locally only). @@ -287,56 +343,21 @@ static void self_gc_connected(const Mono_Time *mono_time, GC_Chat *chat) */ static int set_gc_password_local(GC_Chat *chat, const uint8_t *passwd, uint16_t length) { - if (length > MAX_GC_PASSWD_SIZE) { + if (length > MAX_GC_PASSWORD_SIZE) { return -1; } if (!passwd || length == 0) { - chat->shared_state.passwd_len = 0; - memset(chat->shared_state.passwd, 0, MAX_GC_PASSWD_SIZE); + chat->shared_state.password_length = 0; + memset(chat->shared_state.password, 0, MAX_GC_PASSWORD_SIZE); } else { - chat->shared_state.passwd_len = length; - memcpy(chat->shared_state.passwd, passwd, length); + chat->shared_state.password_length = length; + memcpy(chat->shared_state.password, passwd, length); } return 0; } - -/* Sends an announce request to the DHT if group is public. - * - * Returns non-negative value on success. - * Returns -1 on failure. - */ -static int group_announce_request(GC_Session *c, const GC_Chat *chat) -{ - if (chat->shared_state.version == 0) { - return -1; - } - - if (chat->shared_state.privacy_state != GI_PUBLIC) { - return 0; - } - - return gca_send_announce_request(c->announce, chat->self_public_key, chat->self_secret_key, - get_chat_id(chat->chat_public_key)); -} - -/* Sends a get nodes request to the DHT if group is public. - * - * Returns non-negative value on success. - * Returns -1 on failure. - */ -static int group_get_nodes_request(GC_Session *c, const GC_Chat *chat) -{ - if (chat->shared_state.privacy_state != GI_PUBLIC) { - return 0; - } - - return gca_send_get_nodes_request(c->announce, chat->self_public_key, chat->self_secret_key, - get_chat_id(chat->chat_public_key)); -} - /* Expands the chat_id into the extended chat public key (encryption key + signature key) * dest must have room for EXT_PUBLIC_KEY bytes. */ @@ -347,24 +368,23 @@ static int expand_chat_id(uint8_t *dest, const uint8_t *chat_id) return result; } -/* copies GC_PeerAddress info from src to dest */ -static void copy_gc_peer_addr(GC_PeerAddress *dest, const GC_PeerAddress *src) -{ - memcpy(dest, src, sizeof(GC_PeerAddress)); -} /* Copies up to max_addrs peer addresses from chat to addrs. * * Returns number of addresses copied. */ -uint16_t gc_copy_peer_addrs(const GC_Chat *chat, GC_PeerAddress *addrs, size_t max_addrs) +uint16_t gc_copy_peer_addrs(const GC_Chat *chat, GC_SavedPeerInfo *addrs, size_t max_addrs) { uint32_t i; uint16_t num = 0; for (i = 1; i < chat->numpeers && i < max_addrs; ++i) { - if (chat->gcc[i].confirmed) { - addrs[num] = chat->gcc[i].addr; + GC_Connection *gconn = &chat->gcc[i]; + + if (gconn->confirmed || chat->connection_state != CS_CONNECTED) { + gcc_copy_tcp_relay(gconn, &addrs[num].tcp_relay); + memcpy(&addrs[num].ip_port, &gconn->addr.ip_port, sizeof(IP_Port)); + memcpy(addrs[num].public_key, gconn->addr.public_key, ENC_PUBLIC_KEY); ++num; } } @@ -372,58 +392,6 @@ uint16_t gc_copy_peer_addrs(const GC_Chat *chat, GC_PeerAddress *addrs, size_t m return num; } -static void clear_gc_addrs_list(GC_Chat *chat) -{ - memset(chat->addr_list, 0, sizeof(GC_PeerAddress) * MAX_GC_PEER_ADDRS); - chat->addrs_idx = 0; - chat->num_addrs = 0; -} - -/* This callback is triggered when we receive a get nodes response from DHT. - * The respective chat_id's addr_list will be updated with the newly announced nodes. - * - * Note: All previous entries are cleared. - */ -static void handle_update_gc_addresses(GC_Announce *announce, const uint8_t *chat_id, void *object) -{ - GC_Session *c = (GC_Session *)object; - - uint32_t chat_id_hash = get_chat_id_hash(chat_id); - GC_Chat *chat = get_chat_by_hash(c, chat_id_hash); - - if (chat == nullptr) { - return; - } - - clear_gc_addrs_list(chat); - - GC_Announce_Node nodes[MAX_GCA_SELF_REQUESTS]; - uint32_t num_nodes = gca_get_requested_nodes(announce, get_chat_id(chat->chat_public_key), nodes); - chat->num_addrs = min_u32(num_nodes, MAX_GC_PEER_ADDRS); - - if (chat->num_addrs == 0) { - return; - } - - uint16_t i; - - for (i = 0; i < chat->num_addrs; ++i) { - ipport_copy(&chat->addr_list[i].ip_port, &nodes[i].ip_port); - memcpy(chat->addr_list[i].public_key, nodes[i].public_key, ENC_PUBLIC_KEY); - } - - /* If we're already connected this is part of the DHT sync procedure */ - if (chat->connection_state == CS_CONNECTED) { - sync_gc_announced_nodes(c, chat); - } -} - -static void group_callback_update_addresses(GC_Announce *announce, update_addresses_cb *function, void *object) -{ - announce->update_addresses = function; - announce->update_addresses_obj = object; -} - /* Returns the number of confirmed peers in peerlist */ static uint32_t get_gc_confirmed_numpeers(const GC_Chat *chat) { @@ -504,73 +472,6 @@ static int prune_gc_mod_list(GC_Chat *chat) return 0; } -/* Packs number of peer addresses into data of maxlength length. - * Note: Only the encryption public key is packed. - * - * Return length of packed peer addresses on success. - * Return -1 on failure. - */ -static int pack_gc_addresses(uint8_t *data, uint16_t length, const GC_PeerAddress *addrs, uint16_t number) -{ - uint16_t i, packed_len = 0; - - for (i = 0; i < number; ++i) { - int ipp_size = pack_ip_port(data + packed_len, length - packed_len, &addrs[i].ip_port); - - if (ipp_size == -1) { - return -1; - } - - packed_len += ipp_size; - - if (packed_len + ENC_PUBLIC_KEY > length) { - return -1; - } - - memcpy(data + packed_len, addrs[i].public_key, ENC_PUBLIC_KEY); - packed_len += ENC_PUBLIC_KEY; - } - - return packed_len; -} - -/* Unpack data of length into addrs of size max_num_addrs. - * Put the length of the data processed in processed_data_len. - * tcp_enabled sets if TCP nodes are expected (true) or not (false). - * - * return number of unpacked addresses on success. - * return -1 on failure. - */ -static int unpack_gc_addresses(GC_PeerAddress *addrs, uint16_t max_num_addrs, uint16_t *processed_data_len, - const uint8_t *data, uint16_t length, uint8_t tcp_enabled) -{ - uint16_t num = 0, len_processed = 0; - - while (num < max_num_addrs && len_processed < length) { - int ipp_size = unpack_ip_port(&addrs[num].ip_port, data + len_processed, length - len_processed, tcp_enabled); - - if (ipp_size == -1) { - return -1; - } - - len_processed += ipp_size; - - if (len_processed + ENC_PUBLIC_KEY > length) { - return -1; - } - - memcpy(addrs[num].public_key, data + len_processed, ENC_PUBLIC_KEY); - len_processed += ENC_PUBLIC_KEY; - ++num; - } - - if (processed_data_len) { - *processed_data_len = len_processed; - } - - return num; -} - /* Size of peer data that we pack for transfer (nick length must be accounted for separately). * packed data includes: nick, nick length, status, role */ @@ -589,7 +490,7 @@ static int pack_gc_peer(uint8_t *data, uint16_t length, const GC_GroupPeer *peer uint32_t packed_len = 0; - net_pack_u16(data + packed_len, peer->nick_len); + net_pack_u16(data + packed_len, peer->nick_length); packed_len += sizeof(uint16_t); memcpy(data + packed_len, peer->nick, MAX_GC_NICK_SIZE); packed_len += MAX_GC_NICK_SIZE; @@ -614,9 +515,9 @@ static int unpack_gc_peer(GC_GroupPeer *peer, const uint8_t *data, uint16_t leng uint32_t len_processed = 0; - net_unpack_u16(data + len_processed, &peer->nick_len); + net_unpack_u16(data + len_processed, &peer->nick_length); len_processed += sizeof(uint16_t); - peer->nick_len = min_u16(MAX_GC_NICK_SIZE, peer->nick_len); + peer->nick_length = min_u16(MAX_GC_NICK_SIZE, peer->nick_length); memcpy(peer->nick, data + len_processed, MAX_GC_NICK_SIZE); len_processed += MAX_GC_NICK_SIZE; memcpy(&peer->status, data + len_processed, sizeof(uint8_t)); @@ -649,10 +550,10 @@ static uint16_t pack_gc_shared_state(uint8_t *data, uint16_t length, const GC_Sh packed_len += MAX_GC_GROUP_NAME_SIZE; memcpy(data + packed_len, &shared_state->privacy_state, sizeof(uint8_t)); packed_len += sizeof(uint8_t); - net_pack_u16(data + packed_len, shared_state->passwd_len); + net_pack_u16(data + packed_len, shared_state->password_length); packed_len += sizeof(uint16_t); - memcpy(data + packed_len, shared_state->passwd, MAX_GC_PASSWD_SIZE); - packed_len += MAX_GC_PASSWD_SIZE; + memcpy(data + packed_len, shared_state->password, MAX_GC_PASSWORD_SIZE); + packed_len += MAX_GC_PASSWORD_SIZE; memcpy(data + packed_len, shared_state->mod_list_hash, GC_MODERATION_HASH_SIZE); packed_len += GC_MODERATION_HASH_SIZE; net_pack_u32(data + packed_len, shared_state->version); @@ -684,10 +585,10 @@ static uint16_t unpack_gc_shared_state(GC_SharedState *shared_state, const uint8 len_processed += MAX_GC_GROUP_NAME_SIZE; memcpy(&shared_state->privacy_state, data + len_processed, sizeof(uint8_t)); len_processed += sizeof(uint8_t); - net_unpack_u16(data + len_processed, &shared_state->passwd_len); + net_unpack_u16(data + len_processed, &shared_state->password_length); len_processed += sizeof(uint16_t); - memcpy(shared_state->passwd, data + len_processed, MAX_GC_PASSWD_SIZE); - len_processed += MAX_GC_PASSWD_SIZE; + memcpy(shared_state->password, data + len_processed, MAX_GC_PASSWORD_SIZE); + len_processed += MAX_GC_PASSWORD_SIZE; memcpy(shared_state->mod_list_hash, data + len_processed, GC_MODERATION_HASH_SIZE); len_processed += GC_MODERATION_HASH_SIZE; net_unpack_u32(data + len_processed, &shared_state->version); @@ -918,7 +819,7 @@ static int wrap_group_packet(const uint8_t *self_pk, const uint8_t *shared_key, return 1 + HASH_ID_BYTES + ENC_PUBLIC_KEY + CRYPTO_NONCE_SIZE + enc_len; } -/* Sends a lossy packet to peernumber in chat instance. +/* Sends a lossy packet to peer_number in chat instance. * * Returns 0 on success. * Returns -1 on failure. @@ -950,7 +851,7 @@ static int send_lossy_group_packet(const GC_Chat *chat, GC_Connection *gconn, co return 0; } -/* Sends a lossless packet to peernumber in chat instance. +/* Sends a lossless packet to peer_number in chat instance. * * Returns 0 on success. * Returns -1 on failure. @@ -967,6 +868,7 @@ static int send_lossless_group_packet(GC_Chat *chat, GC_Connection *gconn, const } uint64_t message_id = gconn->send_message_id; + // fprintf(stderr, "send_lossless_group_packet (type: %u, id: %ld)\n", packet_type, message_id); uint8_t packet[MAX_GC_PACKET_SIZE]; int len = wrap_group_packet(chat->self_public_key, gconn->shared_key, packet, sizeof(packet), data, length, message_id, packet_type, chat->chat_id_hash, NET_PACKET_GC_LOSSLESS); @@ -976,7 +878,7 @@ static int send_lossless_group_packet(GC_Chat *chat, GC_Connection *gconn, const return -1; } - if (gcc_add_send_ary(chat->mono_time, gconn, packet, len, packet_type) == -1) { + if (gcc_add_to_send_array(chat->mono_time, gconn, packet, len, packet_type) == -1) { return -1; } @@ -992,17 +894,20 @@ static int send_lossless_group_packet(GC_Chat *chat, GC_Connection *gconn, const */ static int send_gc_sync_request(GC_Chat *chat, GC_Connection *gconn, uint32_t num_peers) { + // fprintf(stderr, "send gc sync request\n"); + if (gconn->pending_sync_request) { + fprintf(stderr, "error: pending sync\n"); return -1; } gconn->pending_sync_request = true; - uint32_t length = HASH_ID_BYTES + sizeof(uint32_t) + MAX_GC_PASSWD_SIZE; + uint32_t length = HASH_ID_BYTES + sizeof(uint32_t) + MAX_GC_PASSWORD_SIZE; VLA(uint8_t, data, length); net_pack_u32(data, chat->self_public_key_hash); net_pack_u32(data + HASH_ID_BYTES, num_peers); - memcpy(data + HASH_ID_BYTES + sizeof(uint32_t), chat->shared_state.passwd, MAX_GC_PASSWD_SIZE); + memcpy(data + HASH_ID_BYTES + sizeof(uint32_t), chat->shared_state.password, MAX_GC_PASSWORD_SIZE); return send_lossless_group_packet(chat, gconn, data, length, GP_SYNC_REQUEST); } @@ -1012,25 +917,37 @@ static int send_gc_sync_response(GC_Chat *chat, GC_Connection *gconn, const uint return send_lossless_group_packet(chat, gconn, data, length, GP_SYNC_RESPONSE); } +static int send_new_peer_announcement(GC_Chat *chat, GC_Connection *gconn, const uint8_t *data, uint32_t length) +{ + return send_lossless_group_packet(chat, gconn, data, length, GP_PEER_ANNOUNCE); +} + static int send_gc_peer_exchange(const GC_Session *c, GC_Chat *chat, GC_Connection *gconn); -static int send_gc_handshake_request(Messenger *m, int groupnumber, IP_Port ipp, const uint8_t *public_key, - uint8_t request_type, uint8_t join_type); -static int handle_gc_sync_response(Messenger *m, int groupnumber, uint32_t peernumber, GC_Connection *gconn, +static int send_gc_handshake_packet(GC_Chat *chat, uint32_t peer_number, uint8_t handshake_type, + uint8_t request_type, uint8_t join_type); + +static int send_gc_oob_handshake_packet(GC_Chat *chat, uint32_t peer_number, uint8_t handshake_type, + uint8_t request_type, uint8_t join_type); + +static int handle_gc_sync_response(Messenger *m, int group_number, int peer_number, GC_Connection *gconn, const uint8_t *data, uint32_t length) { - if (length <= sizeof(uint32_t)) { + // fprintf(stderr, "gc sync resp start\n"); + + if (length < sizeof(uint32_t)) { return -1; } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); - if (chat == nullptr) { + if (!chat) { return -1; } if (!gconn->pending_sync_request) { + fprintf(stderr, "pending sync\n"); return 0; } @@ -1038,66 +955,101 @@ static int handle_gc_sync_response(Messenger *m, int groupnumber, uint32_t peern uint32_t num_peers; net_unpack_u32(data, &num_peers); - uint32_t unpacked_len = sizeof(uint32_t); - if (num_peers == 0 || num_peers > MAX_GC_NUM_PEERS) { + if (num_peers > chat->shared_state.maxpeers || num_peers > MAX_GC_NUM_PEERS) { + fprintf(stderr, "peers overflow\n"); return -1; } - GC_PeerAddress *addrs = (GC_PeerAddress *)calloc(1, sizeof(GC_PeerAddress) * num_peers); + mono_time_update(m->mono_time); - if (addrs == nullptr) { - return -1; - } + // fprintf(stderr, "got peers in response: %d\n", num_peers); - uint16_t addrs_len = 0; - int unpacked_addrs = unpack_gc_addresses(addrs, num_peers, &addrs_len, data + unpacked_len, - length - unpacked_len, 1); + if (num_peers) { + uint32_t peers_length = length - sizeof(uint32_t); - if (unpacked_addrs != num_peers || addrs_len == 0) { - free(addrs); - fprintf(stderr, "unpack_gc_addresses failed: got %d expected %u\n", unpacked_addrs, num_peers); - return -1; - } + if (peers_length / GC_ANNOUNCE_MIN_SIZE < num_peers) { // num peers is invalid + return -1; + } - mono_time_update(m->mono_time); + GC_Announce *announces = (GC_Announce *)malloc(sizeof(GC_Announce) * num_peers); - unpacked_len += addrs_len; + if (!announces) { + return -1; + } - uint32_t i; + const uint8_t *announces_pointer = data + sizeof(uint32_t); + int unpacked_announces = unpack_announces_list(announces_pointer, peers_length, announces, num_peers, nullptr); - for (i = 0; i < num_peers; ++i) { - if (get_peernum_of_enc_pk(chat, addrs[i].public_key) == -1) { - send_gc_handshake_request(m, groupnumber, addrs[i].ip_port, addrs[i].public_key, - HS_PEER_INFO_EXCHANGE, chat->join_type); + if (unpacked_announces == -1 || unpacked_announces != num_peers) { + free(announces); + return -1; } - } - for (i = 0; i < chat->numpeers; ++i) { - chat->gcc[i].pending_sync_request = false; - chat->gcc[i].pending_state_sync = false; - } + int i, j; - free(addrs); + for (i = 0; i < num_peers; ++i) { + GC_Announce *curr_announce = &announces[i]; - if (chat->connection_state == CS_CONNECTED) { - return 0; - } + if (!memcmp(curr_announce->peer_public_key, chat->self_public_key, ENC_PUBLIC_KEY)) { // our own info + continue; + } - gconn = gcc_get_connection(chat, peernumber); + if (!is_valid_announce(curr_announce)) { + fprintf(stderr, "invalid announce\n"); + continue; + } - self_gc_connected(c->messenger->mono_time, chat); - send_gc_peer_exchange(c, chat, gconn); - group_announce_request(c, chat); + IP_Port *ip_port = curr_announce->ip_port_is_set ? &curr_announce->ip_port : nullptr; + int new_peer_number = peer_add(c->messenger, group_number, ip_port, curr_announce->peer_public_key); + + if (new_peer_number < 0) { + continue; + } + + GC_Connection *peer_gconn = gcc_get_connection(chat, new_peer_number); - if (chat->num_addrs > 0) { - sync_gc_announced_nodes(c, chat); + if (!peer_gconn) { + continue; + } + + for (j = 0; j < announces->tcp_relays_count; ++j) { + add_tcp_relay_connection(chat->tcp_conn, peer_gconn->tcp_connection_num, + curr_announce->tcp_relays[j].ip_port, + curr_announce->tcp_relays[j].public_key); + save_tcp_relay(peer_gconn, &curr_announce->tcp_relays[j]); + } + + // char id_str[IDSTRING_LEN]; + // fprintf(stderr, "handle_gc_sync_response - added peer %s\n", id_to_string(curr_announce->peer_public_key, id_str, IDSTRING_LEN)); + + if (curr_announce->ip_port_is_set && !curr_announce->tcp_relays_count) { + send_gc_handshake_packet(chat, (uint32_t)new_peer_number, GH_REQUEST, HS_PEER_INFO_EXCHANGE, + chat->join_type); + ++peer_gconn->send_message_id; + // fprintf(stderr, "handle_gc_sync_response - send_message_id %ld\n", peer_gconn->send_message_id); + } else { + peer_gconn->pending_handshake_type = HS_PEER_INFO_EXCHANGE; + peer_gconn->is_oob_handshake = false; + peer_gconn->is_pending_handshake_response = false; + const uint64_t timeout = mono_time_get(chat->mono_time) + HANDSHAKE_SENDING_TIMEOUT; + peer_gconn->last_received_ping_time = timeout; + peer_gconn->pending_handshake = timeout; + } + } + + free(announces); } + gconn = gcc_get_connection(chat, peer_number); + self_gc_connected(c->messenger->mono_time, chat); + send_gc_peer_exchange(c, chat, gconn); + if (c->self_join) { - (*c->self_join)(m, groupnumber, c->self_join_userdata); + (*c->self_join)(m, group_number, c->self_join_userdata); } + // fprintf(stderr, "gc sync resp success\n"); return 0; } @@ -1106,6 +1058,51 @@ static int send_peer_mod_list(GC_Chat *chat, GC_Connection *gconn); static int send_peer_sanctions_list(GC_Chat *chat, GC_Connection *gconn); static int send_peer_topic(GC_Chat *chat, GC_Connection *gconn); +int gcc_copy_tcp_relay(GC_Connection *gconn, Node_format *node) +{ + if (!gconn) { + return 1; + } + + if (!node) { + return 2; + } + + int index = (gconn->tcp_relays_index - 1 + MAX_FRIEND_TCP_CONNECTIONS) % MAX_FRIEND_TCP_CONNECTIONS; + + memcpy(node, &gconn->connected_tcp_relays[index], sizeof(Node_format)); + + return 0; +} + +static bool create_announce_for_peer(GC_Chat *chat, GC_Connection *gconn, uint32_t peer_number, GC_Announce *announce) +{ + if (!chat || !gconn || !announce) { + return false; + } + + // pack tcp relays + if (gconn->any_tcp_connections) { + gcc_copy_tcp_relay(gconn, &announce->tcp_relays[0]); + announce->tcp_relays_count = 1; + } else { + announce->tcp_relays_count = 0; + } + + // pack peer public key + gc_get_peer_public_key(chat, peer_number, announce->peer_public_key); + + // pack peer ip and port + if (gcc_is_ip_set(gconn)) { + memcpy(&announce->ip_port, &gconn->addr.ip_port, sizeof(IP_Port)); + announce->ip_port_is_set = true; + } else { + announce->ip_port_is_set = false; + } + + return true; +} + /* Handles a sync request packet and sends a response containing the peer list. * Additionally sends the group topic, shared state, mod list and sanctions list in respective packets. * @@ -1114,54 +1111,57 @@ static int send_peer_topic(GC_Chat *chat, GC_Connection *gconn); * Returns non-negative value on success. * Returns -1 on failure. */ -static int handle_gc_sync_request(const Messenger *m, int groupnumber, GC_Connection *gconn, const uint8_t *data, +static int handle_gc_sync_request(const Messenger *m, int group_number, int peer_number, + GC_Connection *gconn, const uint8_t *data, uint32_t length) { - if (length != sizeof(uint32_t) + MAX_GC_PASSWD_SIZE) { + // fprintf(stderr, "handle gc sync request\n"); + + if (length != sizeof(uint32_t) + MAX_GC_PASSWORD_SIZE) { + fprintf(stderr, "handle gc sync request1\n"); return -1; } - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(m->group_handler, group_number); - if (chat == nullptr) { + if (!chat) { + fprintf(stderr, "handle gc sync request2\n"); return -1; } - if (chat->connection_state != CS_CONNECTED) { + if (chat->connection_state <= CS_MANUALLY_DISCONNECTED || chat->shared_state.version == 0) { + fprintf(stderr, "handle gc sync request3\n"); return -1; } - uint32_t req_num_peers; - net_unpack_u32(data, &req_num_peers); - - /* Sync is not necessary */ - if (req_num_peers > 0 && req_num_peers >= get_gc_confirmed_numpeers(chat)) { - return 0; - } - - if (chat->shared_state.passwd_len > 0) { - uint8_t passwd[MAX_GC_PASSWD_SIZE]; - memcpy(passwd, data + sizeof(uint32_t), MAX_GC_PASSWD_SIZE); + if (chat->shared_state.password_length > 0) { + uint8_t password[MAX_GC_PASSWORD_SIZE]; + memcpy(password, data + sizeof(uint32_t), MAX_GC_PASSWORD_SIZE); - if (memcmp(chat->shared_state.passwd, passwd, chat->shared_state.passwd_len) != 0) { + if (memcmp(chat->shared_state.password, password, chat->shared_state.password_length) != 0) { + fprintf(stderr, "handle gc sync request4\n"); return -1; } } /* Do not change the order of these four calls or else */ if (send_peer_shared_state(chat, gconn) == -1) { + fprintf(stderr, "handle gc sync request5\n"); return -1; } if (send_peer_mod_list(chat, gconn) == -1) { + fprintf(stderr, "handle gc sync request6\n"); return -1; } if (send_peer_sanctions_list(chat, gconn) == -1) { + fprintf(stderr, "handle gc sync request7\n"); return -1; } if (send_peer_topic(chat, gconn) == -1) { + fprintf(stderr, "handle gc sync request8\n"); return -1; } @@ -1169,31 +1169,51 @@ static int handle_gc_sync_request(const Messenger *m, int groupnumber, GC_Connec net_pack_u32(response, chat->self_public_key_hash); uint32_t len = HASH_ID_BYTES; - size_t packed_addrs_size = (ENC_PUBLIC_KEY + sizeof(IP_Port)) * (chat->numpeers - 1); /* approx. */ + uint32_t i, num = 0; + GC_Announce *existing_peers_announces = (GC_Announce *)malloc(sizeof(GC_Announce) * (chat->numpeers - 1)); - /* This is the technical limit to the number of peers you can have in a group (TODO: split packet?) */ - if (HASH_ID_BYTES + packed_addrs_size > sizeof(response)) { + if (!existing_peers_announces) { + fprintf(stderr, "handle gc sync request11\n"); return -1; } - GC_PeerAddress *peer_addrs = (GC_PeerAddress *)calloc(1, sizeof(GC_PeerAddress) * (chat->numpeers - 1)); + // pack info about new node + GC_Announce new_peer_announce; - if (peer_addrs == nullptr) { + if (!create_announce_for_peer(chat, gconn, (uint32_t)peer_number, &new_peer_announce)) { + free(existing_peers_announces); return -1; } - uint32_t i, num = 0; + uint8_t sender_relay_data[MAX_GC_PACKET_SIZE]; + net_pack_u32(sender_relay_data, chat->self_public_key_hash); + int announce_length = pack_announce(sender_relay_data + HASH_ID_BYTES, sizeof(sender_relay_data) - HASH_ID_BYTES, + &new_peer_announce); + + if (announce_length == -1) { + free(existing_peers_announces); + fprintf(stderr, "handle gc sync request12\n"); + return -1; + } - /* must add self separately because reasons */ - GC_PeerAddress self_addr; - memcpy(&self_addr.public_key, chat->self_public_key, ENC_PUBLIC_KEY); - ipport_self_copy(m->dht, &self_addr.ip_port); - copy_gc_peer_addr(&peer_addrs[num], &self_addr); - ++num; + uint32_t sender_data_length = announce_length + HASH_ID_BYTES; for (i = 1; i < chat->numpeers; ++i) { - if (chat->gcc[i].public_key_hash != gconn->public_key_hash && chat->gcc[i].confirmed) { - copy_gc_peer_addr(&peer_addrs[num], &chat->gcc[i].addr); + if (chat->gcc[i].public_key_hash != gconn->public_key_hash && chat->gcc[i].confirmed && i != peer_number) { + + GC_Connection *peer_gconn = gcc_get_connection(chat, i); + + if (!peer_gconn) { + continue; + } + + // creates existing peer announce. we will send it to new node later + if (!create_announce_for_peer(chat, peer_gconn, i, &existing_peers_announces[num])) { + continue; + } + + // sends new peer announce to selected existing peer + send_new_peer_announcement(chat, peer_gconn, sender_relay_data, sender_data_length); ++num; } } @@ -1201,57 +1221,44 @@ static int handle_gc_sync_request(const Messenger *m, int groupnumber, GC_Connec net_pack_u32(response + len, num); len += sizeof(uint32_t); - int addrs_len = pack_gc_addresses(response + len, sizeof(response) - len, peer_addrs, num); - len += addrs_len; + size_t packed_announces_length; + int announces_count = pack_announces_list(response + len, sizeof(response) - len, existing_peers_announces, + num, &packed_announces_length); - free(peer_addrs); + free(existing_peers_announces); - if (addrs_len <= 0) { - fprintf(stderr, "pack_gc_addresses failed %d\n", addrs_len); + if (announces_count != num) { + fprintf(stderr, "handle gc sync request13\n"); return -1; } + len += packed_announces_length; + + // TODO: split packet !important + // fprintf(stderr, "handle gc sync success\n"); + return send_gc_sync_response(chat, gconn, response, len); } -static void self_to_peer(const GC_Session *c, const GC_Chat *chat, GC_GroupPeer *peer); -static int send_gc_peer_info_request(GC_Chat *chat, GC_Connection *gconn); - -/* Compares our peerlist with our announced nodes and attempts to do a handshake - * with any nodes that are not in our peerlist. - * - * Returns 0 on success. - * Returns -1 on failure. - */ -static int sync_gc_announced_nodes(const GC_Session *c, GC_Chat *chat) -{ - GC_GroupPeer self; - self_to_peer(c, chat, &self); - uint8_t data[MAX_GC_PACKET_SIZE]; - net_pack_u32(data, chat->self_public_key_hash); - uint32_t len = HASH_ID_BYTES; +static void self_to_peer(const GC_Chat *chat, GC_GroupPeer *peer); +static int send_gc_peer_info_request(GC_Chat *chat, GC_Connection *gconn); - int peers_len = pack_gc_peer(data + len, sizeof(data) - len, &self); - len += peers_len; - if (peers_len <= 0) { - fprintf(stderr, "pack_gc_peer failed in sync_gc_announced_nodes %d\n", peers_len); - return -1; +static int save_tcp_relay(GC_Connection *gconn, Node_format *node) +{ + if (!gconn || !node) { + return 1; } - uint16_t i; - - for (i = 0; i < chat->num_addrs; ++i) { - if (get_peernum_of_enc_pk(chat, chat->addr_list[i].public_key) == -1) { - send_gc_handshake_request(c->messenger, chat->groupnumber, chat->addr_list[i].ip_port, - chat->addr_list[i].public_key, HS_PEER_INFO_EXCHANGE, HJ_PUBLIC); - } - } + memcpy(&gconn->connected_tcp_relays[gconn->tcp_relays_index], node, sizeof(Node_format)); + gconn->tcp_relays_index = (gconn->tcp_relays_index + 1) % MAX_FRIEND_TCP_CONNECTIONS; + gconn->any_tcp_connections = true; return 0; } + /* Shares our TCP relays with peer and adds shared relays to our connection with them. * * Returns 0 on success. @@ -1288,6 +1295,7 @@ static int send_gc_tcp_relays(const Mono_Time *mono_time, GC_Chat *chat, GC_Conn } gconn->last_tcp_relays_shared = mono_time_get(mono_time); + return 0; } @@ -1296,7 +1304,7 @@ static int send_gc_tcp_relays(const Mono_Time *mono_time, GC_Chat *chat, GC_Conn * Returns 0 on success. * Returns -1 on failure. */ -static int handle_gc_tcp_relays(Messenger *m, int groupnumber, GC_Connection *gconn, const uint8_t *data, +static int handle_gc_tcp_relays(Messenger *m, int group_number, GC_Connection *gconn, const uint8_t *data, uint32_t length) { if (length == 0) { @@ -1304,13 +1312,14 @@ static int handle_gc_tcp_relays(Messenger *m, int groupnumber, GC_Connection *gc } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } if (chat->connection_state != CS_CONNECTED) { + fprintf(stderr, "handle_gc_tcp_relays() failed. State: %d\n", chat->connection_state); return -1; } @@ -1319,6 +1328,7 @@ static int handle_gc_tcp_relays(Messenger *m, int groupnumber, GC_Connection *gc } Node_format tcp_relays[GCC_MAX_TCP_SHARED_RELAYS]; + int num_nodes = unpack_nodes(tcp_relays, GCC_MAX_TCP_SHARED_RELAYS, nullptr, data, length, 1); if (num_nodes <= 0) { @@ -1335,7 +1345,7 @@ static int handle_gc_tcp_relays(Messenger *m, int groupnumber, GC_Connection *gc return 0; } -/* Send invite request to peernumber. Invite packet contains your nick and the group password. +/* Send invite request to peer_number. Invite packet contains your nick and the group password. * If no group password is necessary the password field will be ignored by the invitee. * * Return -1 if fail @@ -1343,21 +1353,26 @@ static int handle_gc_tcp_relays(Messenger *m, int groupnumber, GC_Connection *gc */ static int send_gc_invite_request(GC_Chat *chat, GC_Connection *gconn) { + // fprintf(stderr, "send gc invite request\n"); uint8_t data[MAX_GC_PACKET_SIZE]; - net_pack_u32(data, chat->self_public_key_hash); uint32_t length = HASH_ID_BYTES; - net_pack_u16(data + length, chat->group[0].nick_len); + + net_pack_u32(data, chat->self_public_key_hash); + + net_pack_u16(data + length, chat->group[0].nick_length); length += sizeof(uint16_t); - memcpy(data + length, chat->group[0].nick, chat->group[0].nick_len); - length += chat->group[0].nick_len; - memcpy(data + length, chat->shared_state.passwd, MAX_GC_PASSWD_SIZE); - length += MAX_GC_PASSWD_SIZE; + + memcpy(data + length, chat->group[0].nick, chat->group[0].nick_length); + length += chat->group[0].nick_length; + + memcpy(data + length, chat->shared_state.password, MAX_GC_PASSWORD_SIZE); + length += MAX_GC_PASSWORD_SIZE; return send_lossless_group_packet(chat, gconn, data, length, GP_INVITE_REQUEST); } /* Return -1 if fail - * Return 0 if succes + * Return 0 if success */ static int send_gc_invite_response(GC_Chat *chat, GC_Connection *gconn) { @@ -1371,31 +1386,30 @@ static int send_gc_invite_response(GC_Chat *chat, GC_Connection *gconn) /* Return -1 if fail * Return 0 if success */ -static int handle_gc_invite_response(Messenger *m, int groupnumber, GC_Connection *gconn, const uint8_t *data, +static int handle_gc_invite_response(Messenger *m, int group_number, GC_Connection *gconn, const uint8_t *data, uint32_t length) { + // fprintf(stderr, "handle gc invite resp\n"); GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } - if (chat->connection_state == CS_CONNECTED) { - return 0; - } - return send_gc_sync_request(chat, gconn, 0); } -static int handle_gc_invite_response_reject(Messenger *m, int groupnumber, const uint8_t *data, uint32_t length) +static int handle_gc_invite_response_reject(Messenger *m, int group_number, const uint8_t *data, uint32_t length) { + // fprintf(stderr, "handle gc invite rejected\n"); + if (length != sizeof(uint8_t)) { return -1; } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; @@ -1414,7 +1428,7 @@ static int handle_gc_invite_response_reject(Messenger *m, int groupnumber, const chat->connection_state = CS_FAILED; if (c->rejected) { - (*c->rejected)(m, groupnumber, type, c->rejected_userdata); + (*c->rejected)(m, group_number, type, c->rejected_userdata); } return 0; @@ -1438,66 +1452,81 @@ static int send_gc_invite_response_reject(GC_Chat *chat, GC_Connection *gconn, u * Returns non-negative value on success. * Returns -1 on failure. */ -static int handle_gc_invite_request(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, +static int handle_gc_invite_request(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length) { - if (length <= sizeof(uint16_t) + MAX_GC_PASSWD_SIZE) { + // fprintf(stderr, "handle_gc_invite_request\n"); + + if (length <= sizeof(uint16_t) + MAX_GC_PASSWORD_SIZE) { + fprintf(stderr, "invite fail1\n"); return -1; } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); - if (chat == nullptr) { + if (!chat) { + fprintf(stderr, "invite fail chat\n"); return -1; } - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); - if (gconn == nullptr) { + if (!gconn) { + fprintf(stderr, "!gconn\n"); return -1; } - if (chat->connection_state != CS_CONNECTED) { + if (chat->connection_state <= CS_MANUALLY_DISCONNECTED || chat->shared_state.version == 0) { + fprintf(stderr, "not connected - state: %d\n", chat->connection_state); return -1; } uint8_t invite_error = GJ_INVITE_FAILED; + uint8_t nick[MAX_GC_NICK_SIZE] = {0}; + uint16_t nick_len; + int peer_number_by_nick; if (get_gc_confirmed_numpeers(chat) >= chat->shared_state.maxpeers) { + fprintf(stderr, "invite full gc\n"); invite_error = GJ_GROUP_FULL; goto FAILED_INVITE; } - uint16_t nick_len; net_unpack_u16(data, &nick_len); if (nick_len > MAX_GC_NICK_SIZE) { + fprintf(stderr, "invite nick\n"); goto FAILED_INVITE; } if (length - sizeof(uint16_t) < nick_len) { + fprintf(stderr, "invali len"); goto FAILED_INVITE; } - uint8_t nick[MAX_GC_NICK_SIZE]; memcpy(nick, data + sizeof(uint16_t), nick_len); - if (get_nick_peernumber(chat, nick, nick_len) != -1) { + peer_number_by_nick = get_nick_peer_number(chat, nick, nick_len); + + if (peer_number_by_nick != -1 && peer_number_by_nick != peer_number) { // in case of duplicate invite + fprintf(stderr, "nick taken\n"); invite_error = GJ_NICK_TAKEN; goto FAILED_INVITE; } - if (length - sizeof(uint16_t) - nick_len < MAX_GC_PASSWD_SIZE) { + if (length - sizeof(uint16_t) - nick_len < MAX_GC_PASSWORD_SIZE) { + fprintf(stderr, "invite pass error\n"); goto FAILED_INVITE; } - if (chat->shared_state.passwd_len > 0) { - uint8_t passwd[MAX_GC_PASSWD_SIZE]; - memcpy(passwd, data + sizeof(uint16_t) + nick_len, MAX_GC_PASSWD_SIZE); + if (chat->shared_state.password_length > 0) { + uint8_t password[MAX_GC_PASSWORD_SIZE]; + memcpy(password, data + sizeof(uint16_t) + nick_len, MAX_GC_PASSWORD_SIZE); - if (memcmp(chat->shared_state.passwd, passwd, chat->shared_state.passwd_len) != 0) { + if (memcmp(chat->shared_state.password, password, chat->shared_state.password_length) != 0) { invite_error = GJ_INVALID_PASSWORD; + fprintf(stderr, "invite pass\n"); goto FAILED_INVITE; } } @@ -1505,12 +1534,37 @@ static int handle_gc_invite_request(Messenger *m, int groupnumber, uint32_t peer return send_gc_invite_response(chat, gconn); FAILED_INVITE: + fprintf(stderr, "failed_invite\n"); send_gc_invite_response_reject(chat, gconn, invite_error); - gc_peer_delete(m, groupnumber, peernumber, nullptr, 0); + gc_peer_delete(m, group_number, peer_number, nullptr, 0, false); return -1; } +/* Sends a lossless packet of type and length to all confirmed peers. */ +static void send_gc_lossless_packet_all_peers(GC_Chat *chat, const uint8_t *data, uint32_t length, uint8_t type) +{ + uint32_t i; + + for (i = 1; i < chat->numpeers; ++i) { + if (chat->gcc[i].confirmed) { + send_lossless_group_packet(chat, &chat->gcc[i], data, length, type); + } + } +} + +/* Sends a lossy packet of type and length to all confirmed peers. */ +static void send_gc_lossy_packet_all_peers(GC_Chat *chat, const uint8_t *data, uint32_t length, uint8_t type) +{ + uint32_t i; + + for (i = 1; i < chat->numpeers; ++i) { + if (chat->gcc[i].confirmed) { + send_lossy_group_packet(chat, &chat->gcc[i], data, length, type); + } + } +} + /* Creates packet with broadcast header info followed by data of length. * Returns length of packet including header. */ @@ -1545,41 +1599,12 @@ static int send_gc_broadcast_message(GC_Chat *chat, const uint8_t *data, uint32_ VLA(uint8_t, packet, length + GC_BROADCAST_ENC_HEADER_SIZE); uint32_t packet_len = make_gc_broadcast_header(chat, data, length, packet, bc_type); - uint32_t i; - for (i = 1; i < chat->numpeers; ++i) { - if (chat->gcc[i].confirmed) { - send_lossless_group_packet(chat, &chat->gcc[i], packet, packet_len, GP_BROADCAST); - } - } + send_gc_lossless_packet_all_peers(chat, packet, packet_len, GP_BROADCAST); return 0; } -/* Sends a lossless packet of type and length to all confirmed peers. */ -static void send_gc_lossless_packet_all_peers(GC_Chat *chat, const uint8_t *data, uint32_t length, uint8_t type) -{ - uint32_t i; - - for (i = 1; i < chat->numpeers; ++i) { - if (chat->gcc[i].confirmed) { - send_lossless_group_packet(chat, &chat->gcc[i], data, length, type); - } - } -} - -/* Sends a lossy packet of type and length to all confirmed peers. */ -static void send_gc_lossy_packet_all_peers(GC_Chat *chat, const uint8_t *data, uint32_t length, uint8_t type) -{ - uint32_t i; - - for (i = 1; i < chat->numpeers; ++i) { - if (chat->gcc[i].confirmed) { - send_lossy_group_packet(chat, &chat->gcc[i], data, length, type); - } - } -} - /* Compares a peer's group sync info that we received in a ping packet to our own. * * If their info appears to be more recent than ours we will first set a sync request flag. @@ -1622,13 +1647,13 @@ static void do_gc_peer_state_sync(GC_Chat *chat, GC_Connection *gconn, const uin * The packet contains sync information including peer's confirmed peer count, * shared state version and sanction credentials version. */ -static int handle_gc_ping(Messenger *m, int groupnumber, GC_Connection *gconn, const uint8_t *data, uint32_t length) +static int handle_gc_ping(Messenger *m, int group_number, GC_Connection *gconn, const uint8_t *data, uint32_t length) { if (length != GC_PING_PACKET_DATA_SIZE) { return -1; } - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(m->group_handler, group_number); if (chat == nullptr) { return -1; @@ -1639,7 +1664,7 @@ static int handle_gc_ping(Messenger *m, int groupnumber, GC_Connection *gconn, c } do_gc_peer_state_sync(chat, gconn, data, length); - gconn->last_rcvd_ping = mono_time_get(m->mono_time); + gconn->last_received_ping_time = mono_time_get(m->mono_time); return 0; } @@ -1647,14 +1672,14 @@ static int handle_gc_ping(Messenger *m, int groupnumber, GC_Connection *gconn, c /* Sets the caller's status * * Returns 0 on success. - * Returns -1 if the groupnumber is invalid. + * Returns -1 if the group_number is invalid. * Returns -2 if the status type is invalid. * Returns -3 if the packet failed to send. */ -int gc_set_self_status(Messenger *m, int groupnumber, uint8_t status) +int gc_set_self_status(Messenger *m, int group_number, uint8_t status) { GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; @@ -1664,10 +1689,6 @@ int gc_set_self_status(Messenger *m, int groupnumber, uint8_t status) return -2; } - if (c->status_change) { - (*c->status_change)(m, groupnumber, chat->group[0].peer_id, status, c->status_change_userdata); - } - chat->group[0].status = status; uint8_t data[1]; data[0] = chat->group[0].status; @@ -1679,14 +1700,14 @@ int gc_set_self_status(Messenger *m, int groupnumber, uint8_t status) return 0; } -static int handle_bc_status(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, uint32_t length) +static int handle_gc_status(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length) { if (length != sizeof(uint8_t)) { return -1; } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; @@ -1699,10 +1720,10 @@ static int handle_bc_status(Messenger *m, int groupnumber, uint32_t peernumber, } if (c->status_change) { - (*c->status_change)(m, groupnumber, chat->group[peernumber].peer_id, status, c->status_change_userdata); + (*c->status_change)(m, group_number, chat->group[peer_number].peer_id, status, c->status_change_userdata); } - chat->group[peernumber].status = status; + chat->group[peer_number].status = status; return 0; } @@ -1712,13 +1733,13 @@ static int handle_bc_status(Messenger *m, int groupnumber, uint32_t peernumber, */ uint8_t gc_get_status(const GC_Chat *chat, uint32_t peer_id) { - int peernumber = get_peernumber_of_peer_id(chat, peer_id); + int peer_number = get_peer_number_of_peer_id(chat, peer_id); - if (!peernumber_valid(chat, peernumber)) { + if (!peer_number_valid(chat, peer_number)) { return -1; } - return chat->group[peernumber].status; + return chat->group[peer_number].status; } /* Returns peer_id's group role. @@ -1726,13 +1747,13 @@ uint8_t gc_get_status(const GC_Chat *chat, uint32_t peer_id) */ uint8_t gc_get_role(const GC_Chat *chat, uint32_t peer_id) { - int peernumber = get_peernumber_of_peer_id(chat, peer_id); + int peer_number = get_peer_number_of_peer_id(chat, peer_id); - if (!peernumber_valid(chat, peernumber)) { + if (!peer_number_valid(chat, peer_number)) { return -1; } - return chat->group[peernumber].role; + return chat->group[peer_number].role; } /* Copies the chat_id to dest. */ @@ -1743,7 +1764,7 @@ void gc_get_chat_id(const GC_Chat *chat, uint8_t *dest) } } -/* Sends self peer info to peernumber. If the group is password protected the request +/* Sends self peer info to peer_number. If the group is password protected the request * will contain the group password, which the recipient will validate in the respective * group message handler. * @@ -1753,12 +1774,12 @@ void gc_get_chat_id(const GC_Chat *chat, uint8_t *dest) static int send_self_to_peer(const GC_Session *c, GC_Chat *chat, GC_Connection *gconn) { GC_GroupPeer self; - self_to_peer(c, chat, &self); + self_to_peer(chat, &self); uint8_t data[MAX_GC_PACKET_SIZE]; net_pack_u32(data, chat->self_public_key_hash); - memcpy(data + HASH_ID_BYTES, chat->shared_state.passwd, MAX_GC_PASSWD_SIZE); - uint32_t length = HASH_ID_BYTES + MAX_GC_PASSWD_SIZE; + memcpy(data + HASH_ID_BYTES, chat->shared_state.password, MAX_GC_PASSWORD_SIZE); + uint32_t length = HASH_ID_BYTES + MAX_GC_PASSWORD_SIZE; int packed_len = pack_gc_peer(data + length, sizeof(data) - length, &self); length += packed_len; @@ -1771,10 +1792,10 @@ static int send_self_to_peer(const GC_Session *c, GC_Chat *chat, GC_Connection * return send_lossless_group_packet(chat, gconn, data, length, GP_PEER_INFO_RESPONSE); } -static int handle_gc_peer_info_request(Messenger *m, int groupnumber, GC_Connection *gconn) +static int handle_gc_peer_info_request(Messenger *m, int group_number, GC_Connection *gconn) { GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; @@ -1808,33 +1829,96 @@ static int send_gc_peer_exchange(const GC_Session *c, GC_Chat *chat, GC_Connecti return (ret1 == -1 || ret2 == -1) ? -1 : 0; } +static int handle_gc_peer_announcement(Messenger *m, int group_number, const uint8_t *data, uint32_t length) +{ + if (length <= ENC_PUBLIC_KEY) { + return -1; + } + + // fprintf(stderr, "in handle_gc_peer_announcement\n"); + + GC_Chat *chat = gc_get_group(m->group_handler, group_number); + + if (!chat) { + return -1; + } + + GC_Announce new_peer_announce; + int unpack_result = unpack_announce((const uint8_t *)data, length, &new_peer_announce); + + if (unpack_result == -1) { + fprintf(stderr, "in handle_gc_peer_announcement unpack error\n"); + return -1; + } + + IP_Port *ip_port = new_peer_announce.ip_port_is_set ? &new_peer_announce.ip_port : nullptr; + + if (!ip_port && !new_peer_announce.tcp_relays_count) { + fprintf(stderr, "in handle_gc_peer_announcement invalid info\n"); + return -1; + } + + int peer_number = peer_add(m, group_number, ip_port, new_peer_announce.peer_public_key); + + if (peer_number == -2) { + return 0; + } else if (peer_number == -1) { + fprintf(stderr, "peer add failed\n"); + return -1; + } + + // char id_str[IDSTRING_LEN]; + // fprintf(stderr, "peer add %s\n", id_to_string(new_peer_announce.peer_public_key, id_str, IDSTRING_LEN)); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); + + if (!gconn) { + return -1; + } + + if (new_peer_announce.tcp_relays_count == 0) { + return 0; + } + + int i; + + for (i = 0; i < new_peer_announce.tcp_relays_count; ++i) { + add_tcp_relay_connection(chat->tcp_conn, gconn->tcp_connection_num, + new_peer_announce.tcp_relays[i].ip_port, + new_peer_announce.tcp_relays[i].public_key); + save_tcp_relay(gconn, &new_peer_announce.tcp_relays[i]); + } + + return 0; +} + /* Updates peer's info, validates their group role, and sets them as a confirmed peer. * If the group is password protected the password must first be validated. * * Returns 0 on success. * Returns -1 on failure. */ -static int handle_gc_peer_info_response(Messenger *m, int groupnumber, uint32_t peernumber, +static int handle_gc_peer_info_response(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length) { - if (length <= SIG_PUBLIC_KEY + MAX_GC_PASSWD_SIZE) { + if (length <= SIG_PUBLIC_KEY + MAX_GC_PASSWORD_SIZE) { return -1; } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return -1; } if (chat->connection_state != CS_CONNECTED) { + fprintf(stderr, "handle_gc_peer_info_response failed: state %d\n", chat->connection_state); return -1; } @@ -1842,11 +1926,11 @@ static int handle_gc_peer_info_response(Messenger *m, int groupnumber, uint32_t return -1; } - if (chat->shared_state.passwd_len > 0) { - uint8_t passwd[MAX_GC_PASSWD_SIZE]; - memcpy(passwd, data, sizeof(passwd)); + if (chat->shared_state.password_length > 0) { + uint8_t password[MAX_GC_PASSWORD_SIZE]; + memcpy(password, data, sizeof(password)); - if (memcmp(chat->shared_state.passwd, passwd, chat->shared_state.passwd_len) != 0) { + if (memcmp(chat->shared_state.password, password, chat->shared_state.password_length) != 0) { return -1; } } @@ -1854,24 +1938,24 @@ static int handle_gc_peer_info_response(Messenger *m, int groupnumber, uint32_t GC_GroupPeer peer; memset(&peer, 0, sizeof(GC_GroupPeer)); - if (unpack_gc_peer(&peer, data + MAX_GC_PASSWD_SIZE, length - MAX_GC_PASSWD_SIZE) == -1) { + if (unpack_gc_peer(&peer, data + MAX_GC_PASSWORD_SIZE, length - MAX_GC_PASSWORD_SIZE) == -1) { fprintf(stderr, "unpack_gc_peer failed in handle_gc_peer_info_request\n"); return -1; } - if (peer_update(m, groupnumber, &peer, peernumber) == -1) { + if (peer_update(m, group_number, &peer, peer_number) == -1) { fprintf(stderr, "peer_update() failed in handle_gc_peer_info_request\n"); return -1; } - if (validate_gc_peer_role(chat, peernumber) == -1) { - gc_peer_delete(m, groupnumber, peernumber, nullptr, 0); + if (validate_gc_peer_role(chat, peer_number) == -1) { + gc_peer_delete(m, group_number, peer_number, nullptr, 0, false); fprintf(stderr, "failed to validate peer role\n"); return -1; } if (c->peer_join && !gconn->confirmed) { - (*c->peer_join)(m, groupnumber, chat->group[peernumber].peer_id, c->peer_join_userdata); + (*c->peer_join)(m, group_number, chat->group[peer_number].peer_id, c->peer_join_userdata); } gconn->confirmed = true; @@ -1879,7 +1963,7 @@ static int handle_gc_peer_info_response(Messenger *m, int groupnumber, uint32_t return 0; } -/* Sends the group shared state and its signature to peernumber. +/* Sends the group shared state and its signature to peer_number. * * Returns a non-negative integer on success. * Returns -1 on failure. @@ -1924,47 +2008,38 @@ static int broadcast_gc_shared_state(GC_Chat *chat) * * The initial retrieval of the shared state on group join will be ignored by this function. */ -static void do_gc_shared_state_changes(GC_Session *c, const GC_Chat *chat, const GC_SharedState *old_shared_state) +static void do_gc_shared_state_changes(GC_Session *c, GC_Chat *chat, const GC_SharedState *old_shared_state) { - if (old_shared_state->version == 0) { - return; - } - /* Max peers changed */ if (chat->shared_state.maxpeers != old_shared_state->maxpeers) { if (c->peer_limit) { - (*c->peer_limit)(c->messenger, chat->groupnumber, chat->shared_state.maxpeers, c->peer_limit_userdata); + (*c->peer_limit)(c->messenger, chat->group_number, chat->shared_state.maxpeers, c->peer_limit_userdata); } - - return; } /* privacy state changed */ if (chat->shared_state.privacy_state != old_shared_state->privacy_state) { if (c->privacy_state) { - (*c->privacy_state)(c->messenger, chat->groupnumber, chat->shared_state.privacy_state, + (*c->privacy_state)(c->messenger, chat->group_number, chat->shared_state.privacy_state, c->privacy_state_userdata); } - if (chat->shared_state.privacy_state == GI_PUBLIC) { - group_announce_request(c, chat); + if (is_public_chat(chat)) { + m_add_group(c->messenger, chat); } else if (chat->shared_state.privacy_state == GI_PRIVATE) { - gca_cleanup(c->announce, get_chat_id(chat->chat_public_key)); + m_remove_group(c->messenger, chat); + cleanup_gca(c->announces_list, get_chat_id(chat->chat_public_key)); } - - return; } /* password changed */ - if (chat->shared_state.passwd_len != old_shared_state->passwd_len - || memcmp(chat->shared_state.passwd, old_shared_state->passwd, old_shared_state->passwd_len) != 0) { + if (chat->shared_state.password_length != old_shared_state->password_length + || memcmp(chat->shared_state.password, old_shared_state->password, old_shared_state->password_length) != 0) { if (c->password) { - (*c->password)(c->messenger, chat->groupnumber, chat->shared_state.passwd, - chat->shared_state.passwd_len, c->password_userdata); + (*c->password)(c->messenger, chat->group_number, chat->shared_state.password, + chat->shared_state.password_length, c->password_userdata); } - - return; } } @@ -1979,7 +2054,7 @@ static int validate_gc_shared_state(const GC_SharedState *state) return -1; } - if (state->passwd_len > MAX_GC_PASSWD_SIZE) { + if (state->password_length > MAX_GC_PASSWORD_SIZE) { return -1; } @@ -1990,12 +2065,12 @@ static int validate_gc_shared_state(const GC_SharedState *state) return 0; } -static int handle_gc_shared_state_error(Messenger *m, int groupnumber, - uint32_t peernumber, GC_Chat *chat) +static int handle_gc_shared_state_error(Messenger *m, int group_number, + uint32_t peer_number, GC_Chat *chat) { /* If we don't already have a valid shared state we will automatically try to get another invite. Otherwise we attempt to ask a different peer for a sync. */ - gc_peer_delete(m, groupnumber, peernumber, (const uint8_t *)"BAD SHARED STATE", 10); + gc_peer_delete(m, group_number, peer_number, (const uint8_t *)"BAD SHARED STATE", 10, false); if (chat->shared_state.version == 0) { chat->connection_state = CS_DISCONNECTED; @@ -2006,6 +2081,8 @@ static int handle_gc_shared_state_error(Messenger *m, int groupnumber, return -1; } + // fprintf(stderr, "Shared state error. Sending another syn request\n"); + return send_gc_sync_request(chat, &chat->gcc[1], 0); } @@ -2014,18 +2091,18 @@ static int handle_gc_shared_state_error(Messenger *m, int groupnumber, * Returns a non-negative value on success. * Returns -1 on failure. */ -static int handle_gc_shared_state(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, +static int handle_gc_shared_state(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length) { GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } if (length != GC_SHARED_STATE_ENC_PACKET_SIZE - HASH_ID_BYTES) { - return handle_gc_shared_state_error(m, groupnumber, peernumber, chat); + return handle_gc_shared_state_error(m, group_number, peer_number, chat); } uint8_t signature[SIGNATURE_SIZE]; @@ -2036,7 +2113,7 @@ static int handle_gc_shared_state(Messenger *m, int groupnumber, uint32_t peernu if (crypto_sign_verify_detached(signature, ss_data, GC_PACKED_SHARED_STATE_SIZE, get_sig_pk(chat->chat_public_key)) == -1) { - return handle_gc_shared_state_error(m, groupnumber, peernumber, chat); + return handle_gc_shared_state_error(m, group_number, peer_number, chat); } uint32_t version; @@ -2070,7 +2147,7 @@ static int handle_gc_shared_state(Messenger *m, int groupnumber, uint32_t peernu * Returns 0 on success. * Returns -1 on failure. */ -static int handle_gc_mod_list(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, +static int handle_gc_mod_list(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length) { if (length < sizeof(uint16_t)) { @@ -2078,7 +2155,7 @@ static int handle_gc_mod_list(Messenger *m, int groupnumber, uint32_t peernumber } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; @@ -2114,7 +2191,7 @@ static int handle_gc_mod_list(Messenger *m, int groupnumber, uint32_t peernumber return 0; ON_ERROR: - gc_peer_delete(m, groupnumber, peernumber, (const uint8_t *)"BAD MLIST", 9); + gc_peer_delete(m, group_number, peer_number, (const uint8_t *)"BAD MLIST", 9, false); if (chat->shared_state.version == 0) { chat->connection_state = CS_DISCONNECTED; @@ -2128,14 +2205,14 @@ static int handle_gc_mod_list(Messenger *m, int groupnumber, uint32_t peernumber return send_gc_sync_request(chat, &chat->gcc[1], 0); } -static int handle_gc_sanctions_list_error(Messenger *m, int groupnumber, - uint32_t peernumber, GC_Chat *chat) +static int handle_gc_sanctions_list_error(Messenger *m, int group_number, + uint32_t peer_number, GC_Chat *chat) { if (chat->moderation.sanctions_creds.version > 0) { return 0; } - gc_peer_delete(m, groupnumber, peernumber, (const uint8_t *)"BAD SCREDS", 10); + gc_peer_delete(m, group_number, peer_number, (const uint8_t *)"BAD SCREDS", 10, false); if (chat->shared_state.version == 0) { chat->connection_state = CS_DISCONNECTED; @@ -2149,7 +2226,7 @@ static int handle_gc_sanctions_list_error(Messenger *m, int groupnumber, return send_gc_sync_request(chat, &chat->gcc[1], 0); } -static int handle_gc_sanctions_list(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, +static int handle_gc_sanctions_list(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length) { if (length < sizeof(uint32_t)) { @@ -2157,7 +2234,7 @@ static int handle_gc_sanctions_list(Messenger *m, int groupnumber, uint32_t peer } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; @@ -2167,7 +2244,7 @@ static int handle_gc_sanctions_list(Messenger *m, int groupnumber, uint32_t peer net_unpack_u32(data, &num_sanctions); if (num_sanctions > MAX_GC_SANCTIONS) { - return handle_gc_sanctions_list_error(m, groupnumber, peernumber, chat); + return handle_gc_sanctions_list_error(m, group_number, peer_number, chat); } struct GC_Sanction_Creds creds; @@ -2184,13 +2261,13 @@ static int handle_gc_sanctions_list(Messenger *m, int groupnumber, uint32_t peer if (unpacked_num != num_sanctions) { fprintf(stderr, "sanctions_list_unpack failed in handle_gc_sanctions_list: %d\n", unpacked_num); free(sanctions); - return handle_gc_sanctions_list_error(m, groupnumber, peernumber, chat); + return handle_gc_sanctions_list_error(m, group_number, peer_number, chat); } if (sanctions_list_check_integrity(chat, &creds, sanctions, num_sanctions) == -1) { fprintf(stderr, "sanctions_list_check_integrity failed in handle_gc_sanctions_list\n"); free(sanctions); - return handle_gc_sanctions_list_error(m, groupnumber, peernumber, chat); + return handle_gc_sanctions_list_error(m, group_number, peer_number, chat); } sanctions_list_cleanup(chat); @@ -2373,30 +2450,31 @@ static int send_gc_self_exit(GC_Chat *chat, const uint8_t *partmessage, uint32_t return 0; } -static int handle_bc_peer_exit(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, +static int handle_gc_peer_exit(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length) { if (length > MAX_GC_PART_MESSAGE_SIZE) { length = MAX_GC_PART_MESSAGE_SIZE; } - return gc_peer_delete(m, groupnumber, peernumber, data, length); + // fprintf(stderr, "peer exit\n"); + return gc_peer_delete(m, group_number, peer_number, data, length, false); } /* * Sets your own nick. * * Returns 0 on success. - * Returns -1 if groupnumber is invalid. + * Returns -1 if group_number is invalid. * Returns -2 if the length is too long. * Returns -3 if the length is zero or nick is a NULL pointer. * Returns -4 if the nick is already taken. * Returns -5 if the packet fails to send. */ -int gc_set_self_nick(Messenger *m, int groupnumber, const uint8_t *nick, uint16_t length) +int gc_set_self_nick(Messenger *m, int group_number, const uint8_t *nick, uint16_t length) { GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; @@ -2410,16 +2488,12 @@ int gc_set_self_nick(Messenger *m, int groupnumber, const uint8_t *nick, uint16_ return -3; } - if (get_nick_peernumber(chat, nick, length) != -1) { + if (get_nick_peer_number(chat, nick, length) != -1) { return -4; } - if (c->nick_change) { - (*c->nick_change)(m, groupnumber, chat->group[0].peer_id, nick, length, c->nick_change_userdata); - } - memcpy(chat->group[0].nick, nick, length); - chat->group[0].nick_len = length; + chat->group[0].nick_length = length; if (send_gc_broadcast_message(chat, nick, length, GM_NICK) == -1) { return -5; @@ -2432,14 +2506,14 @@ int gc_set_self_nick(Messenger *m, int groupnumber, const uint8_t *nick, uint16_ void gc_get_self_nick(const GC_Chat *chat, uint8_t *nick) { if (nick) { - memcpy(nick, chat->group[0].nick, chat->group[0].nick_len); + memcpy(nick, chat->group[0].nick, chat->group[0].nick_length); } } /* Return your own nick length */ uint16_t gc_get_self_nick_size(const GC_Chat *chat) { - return chat->group[0].nick_len; + return chat->group[0].nick_length; } /* Return your own group role */ @@ -2475,14 +2549,14 @@ void gc_get_self_public_key(const GC_Chat *chat, uint8_t *public_key) */ int gc_get_peer_nick(const GC_Chat *chat, uint32_t peer_id, uint8_t *name) { - int peernumber = get_peernumber_of_peer_id(chat, peer_id); + int peer_number = get_peer_number_of_peer_id(chat, peer_id); - if (!peernumber_valid(chat, peernumber)) { + if (!peer_number_valid(chat, peer_number)) { return -1; } if (name) { - memcpy(name, chat->group[peernumber].nick, chat->group[peernumber].nick_len); + memcpy(name, chat->group[peer_number].nick, chat->group[peer_number].nick_length); } return 0; @@ -2493,59 +2567,83 @@ int gc_get_peer_nick(const GC_Chat *chat, uint32_t peer_id, uint8_t *name) */ int gc_get_peer_nick_size(const GC_Chat *chat, uint32_t peer_id) { - int peernumber = get_peernumber_of_peer_id(chat, peer_id); + int peer_number = get_peer_number_of_peer_id(chat, peer_id); - if (!peernumber_valid(chat, peernumber)) { + if (!peer_number_valid(chat, peer_number)) { return -1; } - return chat->group[peernumber].nick_len; + return chat->group[peer_number].nick_length; } -static int handle_bc_nick(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *nick, +static int handle_gc_nick(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *nick, uint32_t length) { GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } /* If this happens malicious behaviour is highly suspect */ - if (length == 0 || length > MAX_GC_NICK_SIZE || get_nick_peernumber(chat, nick, length) != -1) { - return gc_peer_delete(m, groupnumber, peernumber, nullptr, 0); + if (length == 0 || length > MAX_GC_NICK_SIZE || get_nick_peer_number(chat, nick, length) != -1) { + return gc_peer_delete(m, group_number, peer_number, nullptr, 0, false); } if (c->nick_change) { - (*c->nick_change)(m, groupnumber, chat->group[peernumber].peer_id, nick, length, c->nick_change_userdata); + (*c->nick_change)(m, group_number, chat->group[peer_number].peer_id, nick, length, c->nick_change_userdata); } - memcpy(chat->group[peernumber].nick, nick, length); - chat->group[peernumber].nick_len = length; + memcpy(chat->group[peer_number].nick, nick, length); + chat->group[peer_number].nick_length = length; return 0; } -/* Copies peer_id's public key to public_key. - * +/* Copies peer_number's public key to public_key. + * Returns 0 on success. - * Returns -1 if peer_id is invalid. + * Returns -1 if peer_number is invalid. + * Returns -2 if public_key is NULL */ -int gc_get_peer_public_key(const GC_Chat *chat, uint32_t peer_id, uint8_t *public_key) +int gc_get_peer_public_key(const GC_Chat *chat, uint32_t peer_number, uint8_t *public_key) { - int peernumber = get_peernumber_of_peer_id(chat, peer_id); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + if (!gconn) { + return -1; + } - if (gconn == nullptr) { + if (!public_key) { + return -2; + } + + memcpy(public_key, gconn->addr.public_key, ENC_PUBLIC_KEY); + + return 0; +} + +int gc_get_peer_public_key_by_peer_id(const GC_Chat *chat, uint32_t peer_id, uint8_t *public_key) +{ + int peer_number = get_peer_number_of_peer_id(chat, peer_id); + + if (peer_number < 0) { return -1; } - if (public_key) { - memcpy(public_key, gconn->addr.public_key, ENC_PUBLIC_KEY); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); + + if (!gconn) { + return -2; } + if (!public_key) { + return -3; + } + + memcpy(public_key, gconn->addr.public_key, ENC_PUBLIC_KEY); + return 0; } @@ -2706,7 +2804,7 @@ static int update_gc_topic(GC_Chat *chat, const uint8_t *public_sig_key) return 0; } -static int handle_gc_topic(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, +static int handle_gc_topic(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length) { if (length > SIGNATURE_SIZE + MAX_GC_TOPIC_SIZE + GC_MIN_PACKED_TOPIC_INFO_SIZE) { @@ -2718,7 +2816,7 @@ static int handle_gc_topic(Messenger *m, int groupnumber, uint32_t peernumber, c } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; @@ -2755,7 +2853,7 @@ static int handle_gc_topic(Messenger *m, int groupnumber, uint32_t peernumber, c memcpy(chat->topic_sig, signature, SIGNATURE_SIZE); if (!skip_callback && chat->connection_state == CS_CONNECTED && c->topic_change) { - (*c->topic_change)(m, groupnumber, chat->group[peernumber].peer_id, topic_info.topic, topic_info.length, + (*c->topic_change)(m, group_number, chat->group[peer_number].peer_id, topic_info.topic, topic_info.length, c->topic_change_userdata); } @@ -2763,10 +2861,10 @@ static int handle_gc_topic(Messenger *m, int groupnumber, uint32_t peernumber, c } /* Copies group name to groupname */ -void gc_get_group_name(const GC_Chat *chat, uint8_t *groupname) +void gc_get_group_name(const GC_Chat *chat, uint8_t *group_name) { - if (groupname) { - memcpy(groupname, chat->shared_state.group_name, chat->shared_state.group_name_len); + if (group_name) { + memcpy(group_name, chat->shared_state.group_name, chat->shared_state.group_name_len); } } @@ -2780,14 +2878,14 @@ uint16_t gc_get_group_name_size(const GC_Chat *chat) void gc_get_password(const GC_Chat *chat, uint8_t *password) { if (password) { - memcpy(password, chat->shared_state.passwd, chat->shared_state.passwd_len); + memcpy(password, chat->shared_state.password, chat->shared_state.password_length); } } /* Returns the group password length */ uint16_t gc_get_password_size(const GC_Chat *chat) { - return chat->shared_state.passwd_len; + return chat->shared_state.password_length; } /* Sets the group password and distributes the new shared state to the group. @@ -2799,17 +2897,17 @@ uint16_t gc_get_password_size(const GC_Chat *chat) * Returns -2 if the password is too long. * Returns -3 if the packet failed to send. */ -int gc_founder_set_password(GC_Chat *chat, const uint8_t *passwd, uint16_t passwd_len) +int gc_founder_set_password(GC_Chat *chat, const uint8_t *password, uint16_t password_length) { if (chat->group[0].role != GR_FOUNDER) { return -1; } - uint16_t oldlen = chat->shared_state.passwd_len; + uint16_t oldlen = chat->shared_state.password_length; uint8_t *const oldpasswd = (uint8_t *)malloc(oldlen); - memcpy(oldpasswd, chat->shared_state.passwd, oldlen); + memcpy(oldpasswd, chat->shared_state.password, oldlen); - if (set_gc_password_local(chat, passwd, passwd_len) == -1) { + if (set_gc_password_local(chat, password, password_length) == -1) { free(oldpasswd); return -2; } @@ -2829,7 +2927,7 @@ int gc_founder_set_password(GC_Chat *chat, const uint8_t *passwd, uint16_t passw return 0; } -static int handle_bc_set_mod(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, +static int handle_gc_set_mod(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length) { if (length < 1 + SIG_PUBLIC_KEY) { @@ -2837,19 +2935,19 @@ static int handle_bc_set_mod(Messenger *m, int groupnumber, uint32_t peernumber, } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } - if (chat->group[peernumber].role != GR_FOUNDER) { + if (chat->group[peer_number].role != GR_FOUNDER) { return -1; } bool add_mod = data[0] != 0; uint8_t mod_data[GC_MOD_LIST_ENTRY_SIZE]; - int target_peernum = -1; + int target_peernum; if (add_mod) { if (length < 1 + GC_MOD_LIST_ENTRY_SIZE) { @@ -2859,7 +2957,7 @@ static int handle_bc_set_mod(Messenger *m, int groupnumber, uint32_t peernumber, memcpy(mod_data, data + 1, GC_MODERATION_HASH_SIZE); target_peernum = get_peernum_of_sig_pk(chat, mod_data); - if (peernumber == target_peernum) { + if (peer_number == target_peernum) { return -1; } @@ -2870,7 +2968,7 @@ static int handle_bc_set_mod(Messenger *m, int groupnumber, uint32_t peernumber, memcpy(mod_data, data + 1, SIG_PUBLIC_KEY); target_peernum = get_peernum_of_sig_pk(chat, mod_data); - if (peernumber == target_peernum) { + if (peer_number == target_peernum) { return -1; } @@ -2879,14 +2977,14 @@ static int handle_bc_set_mod(Messenger *m, int groupnumber, uint32_t peernumber, } } - if (!peernumber_valid(chat, target_peernum)) { + if (!peer_number_valid(chat, target_peernum)) { return 0; } chat->group[target_peernum].role = add_mod ? GR_MODERATOR : GR_USER; if (c->moderation) { - (*c->moderation)(m, groupnumber, chat->group[peernumber].peer_id, chat->group[target_peernum].peer_id, + (*c->moderation)(m, group_number, chat->group[peer_number].peer_id, chat->group[target_peernum].peer_id, add_mod ? MV_MODERATOR : MV_USER, c->moderation_userdata); } @@ -2963,7 +3061,7 @@ static int founder_gc_set_moderator(GC_Chat *chat, GC_Connection *gconn, bool ad return 0; } -static int handle_bc_set_observer(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, +static int handle_gc_set_observer(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length) { if (length <= 1 + EXT_PUBLIC_KEY) { @@ -2971,13 +3069,13 @@ static int handle_bc_set_observer(Messenger *m, int groupnumber, uint32_t peernu } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } - if (chat->group[peernumber].role >= GR_USER) { + if (chat->group[peer_number].role >= GR_USER) { return -1; } @@ -2992,7 +3090,7 @@ static int handle_bc_set_observer(Messenger *m, int groupnumber, uint32_t peernu int target_peernum = get_peernum_of_enc_pk(chat, public_key); - if (target_peernum == peernumber) { + if (target_peernum == peer_number) { return -1; } @@ -3026,7 +3124,7 @@ static int handle_bc_set_observer(Messenger *m, int groupnumber, uint32_t peernu chat->group[target_peernum].role = add_obs ? GR_OBSERVER : GR_USER; if (c->moderation) { - (*c->moderation)(m, groupnumber, chat->group[peernumber].peer_id, chat->group[target_peernum].peer_id, + (*c->moderation)(m, group_number, chat->group[peer_number].peer_id, chat->group[target_peernum].peer_id, add_obs ? MV_OBSERVER : MV_USER, c->moderation_userdata); } } @@ -3055,15 +3153,15 @@ static int send_gc_set_observer(GC_Chat *chat, GC_Connection *gconn, const uint8 return 0; } -/* Adds or removes peernumber from the observer list if add_obs is true or false respectively. +/* Adds or removes peer_number from the observer list if add_obs is true or false respectively. * Broadcasts this change to the entire group. * * Returns 0 on success. * Returns -1 on failure. */ -static int mod_gc_set_observer(GC_Chat *chat, uint32_t peernumber, bool add_obs) +static int mod_gc_set_observer(GC_Chat *chat, uint32_t peer_number, bool add_obs) { - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return -1; @@ -3079,7 +3177,7 @@ static int mod_gc_set_observer(GC_Chat *chat, uint32_t peernumber, bool add_obs) if (add_obs) { struct GC_Sanction sanction; - if (sanctions_list_make_entry(chat, peernumber, &sanction, SA_OBSERVER) == -1) { + if (sanctions_list_make_entry(chat, peer_number, &sanction, SA_OBSERVER) == -1) { fprintf(stderr, "sanctions_list_make_entry failed in mod_gc_set_observer\n"); return -1; } @@ -3114,19 +3212,20 @@ static int mod_gc_set_observer(GC_Chat *chat, uint32_t peernumber, bool add_obs) return 0; } -/* Sets the role of peernumber. role must be one of: GR_MODERATOR, GR_USER, GR_OBSERVER +/* Sets the role of peer_number. role must be one of: GR_MODERATOR, GR_USER, GR_OBSERVER * * Returns 0 on success. - * Returns -1 if the groupnumber is invalid. + * Returns -1 if the group_number is invalid. * Returns -2 if the peer_id is invalid. * Returns -3 if caller does not have sufficient permissions for the action. * Returns -4 if the role assignment is invalid. * Returns -5 if the role failed to be set. + * Returns -6 if the caller attempted to set their own role. */ -int gc_set_peer_role(Messenger *m, int groupnumber, uint32_t peer_id, uint8_t role) +int gc_set_peer_role(Messenger *m, int group_number, uint32_t peer_id, uint8_t role) { GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; @@ -3136,14 +3235,18 @@ int gc_set_peer_role(Messenger *m, int groupnumber, uint32_t peer_id, uint8_t ro return -4; } - int peernumber = get_peernumber_of_peer_id(chat, peer_id); + int peer_number = get_peer_number_of_peer_id(chat, peer_id); - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); - if (peernumber == 0 || gconn == nullptr) { + if (gconn == nullptr) { return -2; } + if (peer_number == 0) { + return -6; + } + if (!gconn->confirmed) { return -2; } @@ -3152,33 +3255,29 @@ int gc_set_peer_role(Messenger *m, int groupnumber, uint32_t peer_id, uint8_t ro return -3; } - if (chat->group[peernumber].role == GR_FOUNDER) { + if (chat->group[peer_number].role == GR_FOUNDER) { return -3; } - if (chat->group[0].role != GR_FOUNDER && (role == GR_MODERATOR || chat->group[peernumber].role <= GR_MODERATOR)) { + if (chat->group[0].role != GR_FOUNDER && (role == GR_MODERATOR || chat->group[peer_number].role <= GR_MODERATOR)) { return -3; } - if (chat->group[peernumber].role == role) { + if (chat->group[peer_number].role == role) { return -4; } - uint8_t mod_event = MV_USER; - /* New role must be applied after the old role is removed */ - switch (chat->group[peernumber].role) { + switch (chat->group[peer_number].role) { case GR_MODERATOR: { if (founder_gc_set_moderator(chat, gconn, false) == -1) { return -5; } - chat->group[peernumber].role = GR_USER; + chat->group[peer_number].role = GR_USER; if (role == GR_OBSERVER) { - mod_event = MV_OBSERVER; - - if (mod_gc_set_observer(chat, peernumber, true) == -1) { + if (mod_gc_set_observer(chat, peer_number, true) == -1) { return -5; } } @@ -3187,15 +3286,13 @@ int gc_set_peer_role(Messenger *m, int groupnumber, uint32_t peer_id, uint8_t ro } case GR_OBSERVER: { - if (mod_gc_set_observer(chat, peernumber, false) == -1) { + if (mod_gc_set_observer(chat, peer_number, false) == -1) { return -5; } - chat->group[peernumber].role = GR_USER; + chat->group[peer_number].role = GR_USER; if (role == GR_MODERATOR) { - mod_event = MV_MODERATOR; - if (founder_gc_set_moderator(chat, gconn, true) == -1) { return -5; } @@ -3206,15 +3303,11 @@ int gc_set_peer_role(Messenger *m, int groupnumber, uint32_t peer_id, uint8_t ro case GR_USER: { if (role == GR_MODERATOR) { - mod_event = MV_MODERATOR; - if (founder_gc_set_moderator(chat, gconn, true) == -1) { return -5; } } else if (role == GR_OBSERVER) { - mod_event = MV_OBSERVER; - - if (mod_gc_set_observer(chat, peernumber, true) == -1) { + if (mod_gc_set_observer(chat, peer_number, true) == -1) { return -5; } } @@ -3227,12 +3320,7 @@ int gc_set_peer_role(Messenger *m, int groupnumber, uint32_t peer_id, uint8_t ro } } - if (c->moderation) { - (*c->moderation)(m, groupnumber, chat->group[0].peer_id, chat->group[peernumber].peer_id, mod_event, - c->moderation_userdata); - } - - chat->group[peernumber].role = role; + chat->group[peer_number].role = role; return 0; } @@ -3247,16 +3335,17 @@ uint8_t gc_get_privacy_state(const GC_Chat *chat) * This function requires that the shared state be re-signed and will only work for the group founder. * * Returns 0 on success. - * Returns -1 if groupnumber is invalid. + * Returns -1 if group_number is invalid. * Returns -2 if the privacy state is an invalid type. * Returns -3 if the caller does not have sufficient permissions for this action. - * Returns -4 if the privacy state could not be set. - * Returns -5 if the packet failed to send. + * Returns -4 if the group is disconnected. + * Returns -5 if the privacy state could not be set. + * Returns -6 if the packet failed to send. */ -int gc_founder_set_privacy_state(Messenger *m, int groupnumber, uint8_t new_privacy_state) +int gc_founder_set_privacy_state(Messenger *m, int group_number, uint8_t new_privacy_state) { GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; @@ -3270,6 +3359,10 @@ int gc_founder_set_privacy_state(Messenger *m, int groupnumber, uint8_t new_priv return -3; } + if (chat->connection_state == CS_MANUALLY_DISCONNECTED) { + return -4; + } + uint8_t old_privacy_state = chat->shared_state.privacy_state; if (new_privacy_state == old_privacy_state) { @@ -3280,17 +3373,18 @@ int gc_founder_set_privacy_state(Messenger *m, int groupnumber, uint8_t new_priv if (sign_gc_shared_state(chat) == -1) { chat->shared_state.privacy_state = old_privacy_state; - return -4; + return -5; } if (new_privacy_state == GI_PRIVATE) { - gca_cleanup(c->announce, get_chat_id(chat->chat_public_key)); + cleanup_gca(c->announces_list, get_chat_id(chat->chat_public_key)); + m_remove_group(c->messenger, chat); } else { - group_announce_request(c, chat); + m_add_group(c->messenger, chat); } if (broadcast_gc_shared_state(chat) == -1) { - return -5; + return -6; } return 0; @@ -3311,20 +3405,20 @@ uint32_t gc_get_max_peers(const GC_Chat *chat) * Returns -2 if the peer limit could not be set. * Returns -3 if the packet failed to send. */ -int gc_founder_set_max_peers(GC_Chat *chat, int groupnumber, uint32_t maxpeers) +int gc_founder_set_max_peers(GC_Chat *chat, uint32_t max_peers) { if (chat->group[0].role != GR_FOUNDER) { return -1; } - maxpeers = min_u32(maxpeers, MAX_GC_NUM_PEERS); + max_peers = min_u32(max_peers, MAX_GC_NUM_PEERS); uint32_t old_maxpeers = chat->shared_state.maxpeers; - if (maxpeers == chat->shared_state.maxpeers) { + if (max_peers == chat->shared_state.maxpeers) { return 0; } - chat->shared_state.maxpeers = maxpeers; + chat->shared_state.maxpeers = max_peers; if (sign_gc_shared_state(chat) == -1) { chat->shared_state.maxpeers = old_maxpeers; @@ -3374,7 +3468,7 @@ int gc_send_message(GC_Chat *chat, const uint8_t *message, uint16_t length, uint return 0; } -static int handle_bc_message(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, uint32_t length, +static int handle_gc_message(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length, uint8_t type) { if (!data || length > MAX_GC_MESSAGE_SIZE || length == 0) { @@ -3382,13 +3476,13 @@ static int handle_bc_message(Messenger *m, int groupnumber, uint32_t peernumber, } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } - if (chat->group[peernumber].ignore || chat->group[peernumber].role >= GR_OBSERVER) { + if (chat->group[peer_number].ignore || chat->group[peer_number].role >= GR_OBSERVER) { return 0; } @@ -3399,7 +3493,7 @@ static int handle_bc_message(Messenger *m, int groupnumber, uint32_t peernumber, unsigned int cb_type = (type == GM_PLAIN_MESSAGE) ? MESSAGE_NORMAL : MESSAGE_ACTION; if (c->message) { - (*c->message)(m, groupnumber, chat->group[peernumber].peer_id, cb_type, data, length, c->message_userdata); + (*c->message)(m, group_number, chat->group[peer_number].peer_id, cb_type, data, length, c->message_userdata); } return 0; @@ -3414,7 +3508,7 @@ static int handle_bc_message(Messenger *m, int groupnumber, uint32_t peernumber, * Returns -4 if the sender has the observer role. * Returns -5 if the packet fails to send. */ -int gc_send_private_message(GC_Chat *chat, uint32_t peer_id, const uint8_t *message, uint16_t length) +int gc_send_private_message(GC_Chat *chat, uint32_t peer_id, uint8_t type, const uint8_t *message, uint16_t length) { if (length > MAX_GC_MESSAGE_SIZE) { return -1; @@ -3424,48 +3518,63 @@ int gc_send_private_message(GC_Chat *chat, uint32_t peer_id, const uint8_t *mess return -2; } - int peernumber = get_peernumber_of_peer_id(chat, peer_id); + int peer_number = get_peer_number_of_peer_id(chat, peer_id); - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return -3; } - if (chat->group[0].role >= GR_OBSERVER) { + if (type > MESSAGE_ACTION) { return -4; } - VLA(uint8_t, packet, length + GC_BROADCAST_ENC_HEADER_SIZE); - uint32_t packet_len = make_gc_broadcast_header(chat, message, length, packet, GM_PRVT_MESSAGE); + if (chat->group[0].role >= GR_OBSERVER) { + return -5; + } + + VLA(uint8_t, message_with_type, length + 1); + message_with_type[0] = type; + memcpy(message_with_type + 1, message, length); + + VLA(uint8_t, packet, length + 1 + GC_BROADCAST_ENC_HEADER_SIZE); + uint32_t packet_len = make_gc_broadcast_header(chat, message_with_type, length + 1, packet, GM_PRIVATE_MESSAGE); if (send_lossless_group_packet(chat, gconn, packet, packet_len, GP_BROADCAST) == -1) { - return -5; + return -6; } return 0; } -static int handle_bc_private_message(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, +static int handle_gc_private_message(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length) { - if (!data || length > MAX_GC_MESSAGE_SIZE || length == 0) { + if (!data || length > MAX_GC_MESSAGE_SIZE || length <= 1) { return -1; } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } - if (chat->group[peernumber].ignore || chat->group[peernumber].role >= GR_OBSERVER) { + if (chat->group[peer_number].ignore || chat->group[peer_number].role >= GR_OBSERVER) { return 0; } + unsigned int message_type = data[0]; + + if (message_type > MESSAGE_ACTION) { + return -1; + } + if (c->private_message) { - (*c->private_message)(m, groupnumber, chat->group[peernumber].peer_id, data, length, c->private_message_userdata); + (*c->private_message)(m, group_number, chat->group[peer_number].peer_id, message_type, + data + 1, length - 1, c->private_message_userdata); } return 0; @@ -3506,7 +3615,7 @@ int gc_send_custom_packet(GC_Chat *chat, bool lossless, const uint8_t *data, uin * Returns 0 on success. * Returns -1 on failure. */ -static int handle_gc_custom_packet(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, +static int handle_gc_custom_packet(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint32_t length) { if (!data || length == 0 || length > MAX_GC_PACKET_SIZE) { @@ -3514,95 +3623,83 @@ static int handle_gc_custom_packet(Messenger *m, int groupnumber, uint32_t peern } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } - if (chat->group[peernumber].ignore || chat->group[peernumber].role >= GR_OBSERVER) { + if (chat->group[peer_number].ignore || chat->group[peer_number].role >= GR_OBSERVER) { return 0; } if (c->custom_packet) { - (*c->custom_packet)(m, groupnumber, chat->group[peernumber].peer_id, data, length, c->custom_packet_userdata); + (*c->custom_packet)(m, group_number, chat->group[peer_number].peer_id, data, length, c->custom_packet_userdata); } return 0; } -static int handle_bc_remove_peer(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, - uint32_t length) +static int handle_gc_kick_peer(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, + uint32_t length) { if (length < 1 + ENC_PUBLIC_KEY) { return -1; } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(m->group_handler, group_number); if (chat == nullptr) { return -1; } - if (chat->group[peernumber].role >= GR_USER) { + if (chat->group[peer_number].role >= GR_USER) { return -1; } uint8_t mod_event = data[0]; - if (mod_event != MV_KICK && mod_event != MV_BAN) { + if (mod_event != MV_KICK) { return -1; } uint8_t target_pk[ENC_PUBLIC_KEY]; memcpy(target_pk, data + 1, ENC_PUBLIC_KEY); - int target_peernum = get_peernum_of_enc_pk(chat, target_pk); + int target_peer_number = get_peernum_of_enc_pk(chat, target_pk); - if (peernumber_valid(chat, target_peernum)) { - /* Even if they're offline or this guard is removed a ban on a mod or founder won't work */ - if (chat->group[target_peernum].role != GR_USER) { + if (peer_number_valid(chat, target_peer_number)) { + if (chat->group[target_peer_number].role != GR_USER) { return -1; } } - if (target_peernum == 0) { + if (target_peer_number == 0) { if (c->moderation) { - (*c->moderation)(m, groupnumber, chat->group[peernumber].peer_id, chat->group[target_peernum].peer_id, - mod_event, c->moderation_userdata); + (*c->moderation)(m, group_number, chat->group[peer_number].peer_id, + chat->group[target_peer_number].peer_id, mod_event, c->moderation_userdata); } - group_delete(c, chat); - return 0; - } - - struct GC_Sanction_Creds creds; - - if (mod_event == MV_BAN) { - struct GC_Sanction sanction; - - if (sanctions_list_unpack(&sanction, &creds, 1, data + 1 + ENC_PUBLIC_KEY, - length - 1 - ENC_PUBLIC_KEY, nullptr) != 1) { - return -1; + while (chat->numpeers > 1) { + gc_peer_delete(m, group_number, 1, nullptr, 0, false); } - if (sanctions_list_add_entry(chat, &sanction, &creds) == -1) { - fprintf(stderr, "sanctions_list_add_entry failed in remove peer\n"); - return -1; - } + chat->connection_state = CS_FAILED; + + return 0; } - if (target_peernum == -1) { /* we don't need to/can't kick a peer that isn't in our peerlist */ + if (target_peer_number == -1) { /* we don't need to/can't kick a peer that isn't in our peerlist */ return 0; } if (c->moderation) { - (*c->moderation)(m, groupnumber, chat->group[peernumber].peer_id, chat->group[target_peernum].peer_id, + (*c->moderation)(m, group_number, chat->group[peer_number].peer_id, chat->group[target_peer_number].peer_id, mod_event, c->moderation_userdata); } - if (gc_peer_delete(m, groupnumber, target_peernum, nullptr, 0) == -1) { + if (gc_peer_delete(m, group_number, target_peer_number, nullptr, 0, false) == -1) { return -1; } @@ -3610,58 +3707,46 @@ static int handle_bc_remove_peer(Messenger *m, int groupnumber, uint32_t peernum } /* Sends a packet to instruct all peers to remove gconn from their peerlist. - * - * If mod_event is MV_BAN an updated sanctions list along with new credentials will be added to - * the ban list. * * Returns 0 on success. * Returns -1 on failure. */ -static int send_gc_remove_peer(GC_Chat *chat, GC_Connection *gconn, struct GC_Sanction *sanction, - uint8_t mod_event, bool send_new_creds) +static int send_gc_kick_peer(GC_Chat *chat, GC_Connection *gconn) { uint32_t length = 1 + ENC_PUBLIC_KEY; uint8_t packet[MAX_GC_PACKET_SIZE]; - packet[0] = mod_event; + packet[0] = MV_KICK; memcpy(packet + 1, gconn->addr.public_key, ENC_PUBLIC_KEY); - if (mod_event == MV_BAN) { - int packed_len = sanctions_list_pack(packet + length, sizeof(packet) - length, sanction, - &chat->moderation.sanctions_creds, 1); - - if (packed_len < 0) { - fprintf(stderr, "sanctions_list_pack failed in send_gc_remove_peer\n"); - return -1; - } - - length += packed_len; - } - - return send_gc_broadcast_message(chat, packet, length, GM_REMOVE_PEER); + return send_gc_broadcast_message(chat, packet, length, GM_KICK_PEER); } /* Instructs all peers to remove peer_id from their peerlist. - * If set_ban is true peer will be added to the ban list. * * Returns 0 on success. - * Returns -1 if the groupnumber is invalid. + * Returns -1 if the group_number is invalid. * Returns -2 if the peer_id is invalid. * Returns -3 if the caller does not have sufficient permissions for this action. * Returns -4 if the action failed. * Returns -5 if the packet failed to send. + * Returns -6 if the caller attempted to kick himself. */ -int gc_remove_peer(Messenger *m, int groupnumber, uint32_t peer_id, bool set_ban) +int gc_kick_peer(Messenger *m, int group_number, uint32_t peer_id) { GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } - int peernumber = get_peernumber_of_peer_id(chat, peer_id); + int peer_number = get_peer_number_of_peer_id(chat, peer_id); + + if (peer_number == 0) { + return -6; + } - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return -2; @@ -3671,135 +3756,36 @@ int gc_remove_peer(Messenger *m, int groupnumber, uint32_t peer_id, bool set_ban return -2; } - if (chat->group[0].role >= GR_USER || chat->group[peernumber].role == GR_FOUNDER) { + if (chat->group[0].role >= GR_USER || chat->group[peer_number].role == GR_FOUNDER) { return -3; } - if (chat->group[0].role != GR_FOUNDER && chat->group[peernumber].role == GR_MODERATOR) { + if (chat->group[0].role != GR_FOUNDER && chat->group[peer_number].role == GR_MODERATOR) { return -3; } - if (peernumber == 0) { + if (peer_number == 0) { return -2; } - if (chat->group[peernumber].role == GR_MODERATOR || chat->group[peernumber].role == GR_OBSERVER) { + if (chat->group[peer_number].role == GR_MODERATOR || chat->group[peer_number].role == GR_OBSERVER) { /* this first removes peer from any lists they're on and broadcasts new lists to group */ - if (gc_set_peer_role(m, groupnumber, peer_id, GR_USER) < 0) { - return -4; - } - } - - uint8_t mod_event = set_ban ? MV_BAN : MV_KICK; - struct GC_Sanction sanction; - - if (set_ban) { - if (sanctions_list_make_entry(chat, peernumber, &sanction, SA_BAN) == -1) { - fprintf(stderr, "sanctions_list_make_entry failed\n"); + if (gc_set_peer_role(m, group_number, peer_id, GR_USER) < 0) { return -4; } } - bool send_new_creds = !set_ban && chat->group[peernumber].role == GR_OBSERVER; - - if (send_gc_remove_peer(chat, gconn, &sanction, mod_event, send_new_creds) == -1) { + if (send_gc_kick_peer(chat, gconn) == -1) { return -5; } - if (c->moderation) { - (*c->moderation)(m, groupnumber, chat->group[0].peer_id, chat->group[peernumber].peer_id, mod_event, - c->moderation_userdata); - } - - if (gc_peer_delete(m, groupnumber, peernumber, nullptr, 0) == -1) { + if (gc_peer_delete(m, group_number, peer_number, nullptr, 0, true) == -1) { return -4; } return 0; } -static int handle_bc_remove_ban(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, - uint32_t length) -{ - if (length < sizeof(uint32_t)) { - return -1; - } - - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); - - if (chat == nullptr) { - return -1; - } - - if (chat->group[peernumber].role >= GR_USER) { - return -1; - } - - uint32_t ban_id; - net_unpack_u32(data, &ban_id); - - struct GC_Sanction_Creds creds; - uint16_t unpacked_len = sanctions_creds_unpack(&creds, data + sizeof(uint32_t), length - sizeof(uint32_t)); - - if (unpacked_len != GC_SANCTIONS_CREDENTIALS_SIZE) { - return -1; - } - - if (sanctions_list_remove_ban(chat, ban_id, &creds) == -1) { - fprintf(stderr, "sanctions_list_remove_ban failed in handle_bc_remove_ban\n"); - } - - return 0; -} - -/* Sends a packet instructing all peers to remove a ban entry from the sanctions list. - * Additionally sends updated sanctions credentials. - * - * Returns 0 on success. - * Returns -1 on failure. - */ -static int send_gc_remove_ban(GC_Chat *chat, uint32_t ban_id) -{ - uint8_t packet[sizeof(uint32_t) + GC_SANCTIONS_CREDENTIALS_SIZE]; - net_pack_u32(packet, ban_id); - uint32_t length = sizeof(uint32_t); - - uint16_t packed_len = sanctions_creds_pack(&chat->moderation.sanctions_creds, packet + length, - sizeof(packet) - length); - - if (packed_len != GC_SANCTIONS_CREDENTIALS_SIZE) { - return -1; - } - - length += packed_len; - - return send_gc_broadcast_message(chat, packet, length, GM_REMOVE_BAN); -} - -/* Instructs all peers to remove ban_id from their ban list. - * - * Returns 0 on success. - * Returns -1 if the caller does not have sufficient permissions for this action. - * Returns -2 if the entry could not be removed. - * Returns -3 if the packet failed to send. - */ -int gc_remove_ban(GC_Chat *chat, uint32_t ban_id) -{ - if (chat->group[0].role >= GR_USER) { - return -1; - } - - if (sanctions_list_remove_ban(chat, ban_id, nullptr) == -1) { - return -2; - } - - if (send_gc_remove_ban(chat, ban_id) == -1) { - return -3; - } - - return 0; -} - static bool valid_gc_message_ack(uint64_t a, uint64_t b) { return a == 0 || b == 0; @@ -3848,13 +3834,13 @@ static int handle_gc_message_ack(GC_Chat *chat, GC_Connection *gconn, const uint } uint64_t tm = mono_time_get(chat->mono_time); - uint16_t idx = get_ary_index(request_id); + uint16_t idx = get_array_index(request_id); /* re-send requested packet */ - if (gconn->send_ary[idx].message_id == request_id - && (gconn->send_ary[idx].last_send_try != tm || gconn->send_ary[idx].time_added == tm)) { - gconn->send_ary[idx].last_send_try = tm; - return sendpacket(chat->net, gconn->addr.ip_port, gconn->send_ary[idx].data, gconn->send_ary[idx].data_length); + if (gconn->send_array[idx].message_id == request_id + && (gconn->send_array[idx].last_send_try != tm || gconn->send_array[idx].time_added == tm)) { + gconn->send_array[idx].last_send_try = tm; + return sendpacket(chat->net, gconn->addr.ip_port, gconn->send_array[idx].data, gconn->send_array[idx].data_length); } return -1; @@ -3879,19 +3865,26 @@ static int gc_send_hs_response_ack(GC_Chat *chat, GC_Connection *gconn) * Returns 0 on success. * Returns -1 on failure. */ -static int handle_gc_hs_response_ack(Messenger *m, int groupnumber, GC_Connection *gconn, const uint8_t *data, - uint32_t length) +static int handle_gc_hs_response_ack(Messenger *m, int group_number, GC_Connection *gconn) { - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(m->group_handler, group_number); - if (chat == nullptr) { + if (!chat) { return -1; } gconn->handshaked = true; + gconn->pending_handshake = 0; + // fprintf(stderr, "handshaked - ack\n"); - if (gcc_handle_ack(gconn, 1) == -1) { - return -1; + if (gconn->friend_shared_state_version > gconn->self_sent_shared_state_version + || (gconn->friend_shared_state_version == gconn->self_sent_shared_state_version + && id_cmp(chat->self_public_key, gconn->addr.public_key) > 0)) { + int ret = send_gc_invite_request(chat, gconn); + + if (ret == -1) { + return -1; + } } return 0; @@ -3901,16 +3894,21 @@ static int handle_gc_hs_response_ack(Messenger *m, int groupnumber, GC_Connectio * * Returns 0 on success. * Returns -1 if the peer_id is invalid. + * Returns -2 if the caller attempted to ignore himself. */ int gc_toggle_ignore(GC_Chat *chat, uint32_t peer_id, bool ignore) { - int peernumber = get_peernumber_of_peer_id(chat, peer_id); + int peer_number = get_peer_number_of_peer_id(chat, peer_id); - if (!peernumber_valid(chat, peernumber)) { + if (!peer_number_valid(chat, peer_number)) { return -1; } - chat->group[peernumber].ignore = ignore; + if (peer_number == 0) { + return -2; + } + + chat->group[peer_number].ignore = ignore; return 0; } @@ -3919,26 +3917,28 @@ int gc_toggle_ignore(GC_Chat *chat, uint32_t peer_id, bool ignore) * Returns 0 on success. * Returns -1 on failure. */ -static int handle_gc_broadcast(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, uint32_t length) +static int handle_gc_broadcast(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, + uint32_t length) { if (length < 1 + TIME_STAMP_SIZE) { return -1; } GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return -1; } if (chat->connection_state != CS_CONNECTED) { + fprintf(stderr, "handle_gc_broadcast() failed. State: %d\n", chat->connection_state); return -1; } @@ -3955,32 +3955,29 @@ static int handle_gc_broadcast(Messenger *m, int groupnumber, uint32_t peernumbe switch (broadcast_type) { case GM_STATUS: - return handle_bc_status(m, groupnumber, peernumber, message, m_len); + return handle_gc_status(m, group_number, peer_number, message, m_len); case GM_NICK: - return handle_bc_nick(m, groupnumber, peernumber, message, m_len); + return handle_gc_nick(m, group_number, peer_number, message, m_len); case GM_ACTION_MESSAGE: // intentional fallthrough case GM_PLAIN_MESSAGE: - return handle_bc_message(m, groupnumber, peernumber, message, m_len, broadcast_type); + return handle_gc_message(m, group_number, peer_number, message, m_len, broadcast_type); - case GM_PRVT_MESSAGE: - return handle_bc_private_message(m, groupnumber, peernumber, message, m_len); + case GM_PRIVATE_MESSAGE: + return handle_gc_private_message(m, group_number, peer_number, message, m_len); case GM_PEER_EXIT: - return handle_bc_peer_exit(m, groupnumber, peernumber, message, m_len); - - case GM_REMOVE_PEER: - return handle_bc_remove_peer(m, groupnumber, peernumber, message, m_len); + return handle_gc_peer_exit(m, group_number, peer_number, message, m_len); - case GM_REMOVE_BAN: - return handle_bc_remove_ban(m, groupnumber, peernumber, message, m_len); + case GM_KICK_PEER: + return handle_gc_kick_peer(m, group_number, peer_number, message, m_len); case GM_SET_MOD: - return handle_bc_set_mod(m, groupnumber, peernumber, message, m_len); + return handle_gc_set_mod(m, group_number, peer_number, message, m_len); case GM_SET_OBSERVER: - return handle_bc_set_observer(m, groupnumber, peernumber, message, m_len); + return handle_gc_set_observer(m, group_number, peer_number, message, m_len); default: fprintf(stderr, "Warning: handle_gc_broadcast received an invalid broadcast type %u\n", broadcast_type); @@ -4028,7 +4025,7 @@ static int wrap_group_handshake_packet(const uint8_t *self_pk, const uint8_t *se uint8_t *packet, uint32_t packet_size, const uint8_t *data, uint16_t length, uint32_t chat_id_hash) { - if (packet_size != GC_ENCRYPTED_HS_PACKET_SIZE) { + if (packet_size < GC_ENCRYPTED_HS_PACKET_SIZE + sizeof(Node_format)) { return -1; } @@ -4055,24 +4052,24 @@ static int wrap_group_handshake_packet(const uint8_t *self_pk, const uint8_t *se /* Makes, wraps and encrypts a group handshake packet (both request and response are the same format). * * Packet contains the handshake header, the handshake type, self pk hash, session pk, self public signature key, - * the request type (GROUP_HANDSHAKE_REQUEST_TYPE), the join type (GROUP_HANDSHAKE_JOIN_TYPE), + * the request type (Group_Handshake_Request_Type), the join type (GROUP_HANDSHAKE_JOIN_TYPE), * and a list of tcp relay nodes we share with this peer. * * Returns length of encrypted packet on success. * Returns -1 on failure. */ -static int make_gc_handshake_packet(GC_Chat *chat, const GC_Connection *gconn, uint8_t handshake_type, - uint8_t request_type, uint8_t join_type, uint8_t *packet, size_t packet_size) +static int make_gc_handshake_packet(GC_Chat *chat, GC_Connection *gconn, uint8_t handshake_type, uint8_t request_type, + uint8_t join_type, uint8_t *packet, size_t packet_size, Node_format *node) { - if (packet_size != GC_ENCRYPTED_HS_PACKET_SIZE) { + if (packet_size < GC_ENCRYPTED_HS_PACKET_SIZE + sizeof(Node_format)) { return -1; } - if (!chat || gconn == nullptr) { + if (!chat || !gconn || !node) { return -1; } - uint8_t data[GC_PLAIN_HS_PACKET_SIZE]; + uint8_t data[GC_PLAIN_HS_PACKET_SIZE + sizeof(Node_format)]; data[0] = handshake_type; uint16_t length = sizeof(uint8_t); @@ -4087,11 +4084,27 @@ static int make_gc_handshake_packet(GC_Chat *chat, const GC_Connection *gconn, u memcpy(data + length, &join_type, sizeof(uint8_t)); length += sizeof(uint8_t); + uint32_t state = + gconn->self_sent_shared_state_version != UINT32_MAX ? gconn->self_sent_shared_state_version : + chat->connection_state == CS_CONNECTED ? chat->shared_state.version : 0; + gconn->self_sent_shared_state_version = state; + net_pack_u32(data + length, state); + length += sizeof(uint32_t); + + int nodes_size = pack_nodes(data + length, sizeof(Node_format), node, MAX_SENT_GC_NODES); + + if (nodes_size != -1) { + length += nodes_size; + } else { + nodes_size = 0; + } + int enc_len = wrap_group_handshake_packet(chat->self_public_key, chat->self_secret_key, gconn->addr.public_key, packet, packet_size, data, length, chat->chat_id_hash); - if (enc_len != GC_ENCRYPTED_HS_PACKET_SIZE) { + if (enc_len != GC_ENCRYPTED_HS_PACKET_SIZE + nodes_size) { + fprintf(stderr, "enc len\n"); return -1; } @@ -4100,30 +4113,31 @@ static int make_gc_handshake_packet(GC_Chat *chat, const GC_Connection *gconn, u /* Sends a handshake packet where handshake_type is GH_REQUEST or GH_RESPONSE. * - * Returns size of packet sent on success. + * Returns 0 on success. * Returns -1 on failure. */ -static int send_gc_handshake_packet(GC_Chat *chat, uint32_t peernumber, uint8_t handshake_type, +static int send_gc_handshake_packet(GC_Chat *chat, uint32_t peer_number, uint8_t handshake_type, uint8_t request_type, uint8_t join_type) { - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); - if (gconn == nullptr) { + if (!gconn) { return -1; } - uint8_t packet[GC_ENCRYPTED_HS_PACKET_SIZE]; - int length = make_gc_handshake_packet(chat, gconn, handshake_type, request_type, join_type, packet, sizeof(packet)); + uint8_t packet[GC_ENCRYPTED_HS_PACKET_SIZE + sizeof(Node_format)]; + Node_format node[MAX_ANNOUNCED_TCP_RELAYS]; + gcc_copy_tcp_relay(gconn, node); - if (length != sizeof(packet)) { - return -1; - } + int length = make_gc_handshake_packet(chat, gconn, handshake_type, request_type, join_type, packet, + sizeof(packet), node); - if (gcc_add_send_ary(chat->mono_time, gconn, packet, length, -1) == -1) { + if (length == -1) { + fprintf(stderr, "length error\n"); return -1; } - int ret1 = -1, ret2 = -1; + int ret1 = -1, ret2; if (!net_family_is_unspec(gconn->addr.ip_port.ip.family)) { ret1 = sendpacket(chat->net, gconn->addr.ip_port, packet, length); @@ -4135,73 +4149,66 @@ static int send_gc_handshake_packet(GC_Chat *chat, uint32_t peernumber, uint8_t return -1; } + // fprintf(stderr, "send_gc_handshake_packet success\n"); + return 0; } -/* Initiates a handshake request with a peer. - * request_type should be one of GROUP_HANDSHAKE_REQUEST_TYPE. - * join_type should be HJ_PUBLIC if we found the group via DHT, otherwise HJ_PRIVATE. - * - * Returns peernumber of newly added peer on success. - * Returns -1 on failure. - */ -static int send_gc_handshake_request(Messenger *m, int groupnumber, IP_Port ipp, const uint8_t *public_key, - uint8_t request_type, uint8_t join_type) +static int send_gc_oob_handshake_packet(GC_Chat *chat, uint32_t peer_number, uint8_t handshake_type, + uint8_t request_type, uint8_t join_type) { - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); - if (chat == nullptr) { + if (!gconn) { return -1; } - if (id_equal(chat->self_public_key, public_key)) { - return -1; - } + Node_format node[1]; + gcc_copy_tcp_relay(gconn, node); - int peernumber = peer_add(m, groupnumber, &ipp, public_key); + uint8_t packet[GC_ENCRYPTED_HS_PACKET_SIZE + sizeof(Node_format)]; + int length = make_gc_handshake_packet(chat, gconn, handshake_type, request_type, join_type, packet, + sizeof(packet), node); - if (peernumber == -1) { + if (length == -1) { return -1; } - if (peernumber == -2) { - peernumber = get_peernum_of_enc_pk(chat, public_key); - - if (peernumber == -1) { - return -1; - } - } - - if (send_gc_handshake_packet(chat, peernumber, GH_REQUEST, request_type, join_type) == -1) { - return -1; - } + int ret = tcp_send_oob_packet_using_relay(chat->tcp_conn, gconn->oob_relay_pk, + gconn->addr.public_key, packet, length); - return peernumber; + return ret; } /* Handles a handshake response packet and takes appropriate action depending on the value of request_type. * - * Returns peernumber of new connected peer on success. + * Returns peer_number of new connected peer on success. * Returns -1 on failure. */ -static int handle_gc_handshake_response(Messenger *m, int groupnumber, const uint8_t *sender_pk, +static int handle_gc_handshake_response(Messenger *m, int group_number, const uint8_t *sender_pk, const uint8_t *data, uint16_t length) { - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + // fprintf(stderr, "handle gc handshake resp\n"); - if (chat == nullptr) { + if (length < ENC_PUBLIC_KEY + SIG_PUBLIC_KEY + 1) { return -1; } - int peernumber = get_peernum_of_enc_pk(chat, sender_pk); + GC_Chat *chat = gc_get_group(m->group_handler, group_number); - if (peernumber == -1) { + if (!chat) { return -1; } - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + int peer_number = get_peernum_of_enc_pk(chat, sender_pk); - if (gconn == nullptr) { + if (peer_number == -1) { + return -1; + } + + GC_Connection *gconn = gcc_get_connection(chat, peer_number); + + if (!gconn) { return -1; } @@ -4213,16 +4220,26 @@ static int handle_gc_handshake_response(Messenger *m, int groupnumber, const uin uint8_t request_type = data[ENC_PUBLIC_KEY + SIG_PUBLIC_KEY]; /* This packet is an implied handshake request acknowledgement */ - gcc_handle_ack(gconn, 1); - ++gconn->recv_message_id; + ++gconn->received_message_id; gconn->handshaked = true; + gconn->pending_handshake = 0; gc_send_hs_response_ack(chat, gconn); - int ret = -1; + int ret; switch (request_type) { case HS_INVITE_REQUEST: + net_unpack_u32(data + ENC_PUBLIC_KEY + SIG_PUBLIC_KEY + 2, &gconn->friend_shared_state_version); + + // client with shared version < than friend has will send invite request + // if shared versions are the same compare public keys of peers + if (gconn->friend_shared_state_version < gconn->self_sent_shared_state_version + || (gconn->friend_shared_state_version == gconn->self_sent_shared_state_version + && id_cmp(chat->self_public_key, gconn->addr.public_key) > 0)) { + return peer_number; + } + ret = send_gc_invite_request(chat, gconn); break; @@ -4239,50 +4256,58 @@ static int handle_gc_handshake_response(Messenger *m, int groupnumber, const uin return -1; } - return peernumber; + return peer_number; } -static int send_gc_handshake_response(GC_Chat *chat, uint32_t peernumber, uint8_t request_type) +static int send_gc_handshake_response(GC_Chat *chat, uint32_t peer_number, uint8_t request_type) { - if (send_gc_handshake_packet(chat, peernumber, GH_RESPONSE, request_type, 0) == -1) { + if (send_gc_handshake_packet(chat, peer_number, GH_RESPONSE, request_type, 0) == -1) { return -1; } return 0; } +static int peer_reconnect(Messenger *m, const GC_Chat *chat, const uint8_t *peer_pk) +{ + int peer_number = get_peernum_of_enc_pk(chat, peer_pk); + + if (peer_number < 0) { + return -1; + } + + gc_peer_delete(m, chat->group_number, peer_number, nullptr, 0, false); + + return peer_add(m, chat->group_number, nullptr, peer_pk); +} + /* Handles handshake request packets. * Peer is added to peerlist and a lossless connection is established. * - * Return new peer's peernumber on success. + * Return new peer's peer_number on success. * Return -1 on failure. */ -#define GC_NEW_PEER_CONNECTION_LIMIT 5 -static int handle_gc_handshake_request(Messenger *m, int groupnumber, IP_Port *ipp, const uint8_t *sender_pk, +#define GC_NEW_PEER_CONNECTION_LIMIT 10 +static int handle_gc_handshake_request(Messenger *m, int group_number, IP_Port *ipp, const uint8_t *sender_pk, const uint8_t *data, uint32_t length) { - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); - - if (chat == nullptr) { + if (length < ENC_PUBLIC_KEY + SIG_PUBLIC_KEY + 6) { return -1; } - if (chat->connection_state == CS_FAILED) { + GC_Chat *chat = gc_get_group(m->group_handler, group_number); + + if (!chat) { return -1; } - if (chat->shared_state.version == 0) { + if (chat->connection_state == CS_FAILED) { return -1; } uint8_t public_sig_key[SIG_PUBLIC_KEY]; memcpy(public_sig_key, data + ENC_PUBLIC_KEY, SIG_PUBLIC_KEY); - /* Check if IP is banned and make sure they aren't a moderator or founder */ - if (sanctions_list_ip_banned(chat, ipp) && !mod_list_verify_sig_pk(chat, public_sig_key)) { - return -1; - } - if (chat->connection_O_metre >= GC_NEW_PEER_CONNECTION_LIMIT) { chat->block_handshakes = true; return -1; @@ -4290,25 +4315,77 @@ static int handle_gc_handshake_request(Messenger *m, int groupnumber, IP_Port *i ++chat->connection_O_metre; - int peer_exists = get_peernum_of_enc_pk(chat, sender_pk); + int peer_number = get_peernum_of_enc_pk(chat, sender_pk); + bool is_new_peer = false; + + if (peer_number < 0) { + if (!is_public_chat(chat) && !is_peer_confirmed(chat, sender_pk)) { + return -1; // peer is not allowed to join this chat + } + + peer_number = peer_add(m, chat->group_number, ipp, sender_pk); + + if (peer_number < 0) { + return -1; + } + + is_new_peer = true; + } else { + GC_Connection *gconn = gcc_get_connection(chat, peer_number); + + if (!gconn) { + return -1; + } + + if (gconn->handshaked) { + peer_number = peer_reconnect(m, chat, sender_pk); + + if (peer_number < 0) { + return -1; + } - if (peer_exists != -1) { - gc_peer_delete(m, groupnumber, peer_exists, nullptr, 0); + is_new_peer = true; + } } - int peernumber = peer_add(m, groupnumber, ipp, sender_pk); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); - if (peernumber < 0) { - fprintf(stderr, "peer_add failed in handle_gc_handshake_request\n"); + if (!gconn) { return -1; } - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + if (ipp) { + memcpy(&gconn->addr.ip_port, ipp, sizeof(IP_Port)); + } + + Node_format node[MAX_ANNOUNCED_TCP_RELAYS]; + int processed = ENC_PUBLIC_KEY + SIG_PUBLIC_KEY + 6; + int nodes_count = unpack_nodes(node, MAX_ANNOUNCED_TCP_RELAYS, nullptr, data + processed, length - processed, 1); + + if (nodes_count <= 0 && !ipp) { + if (is_new_peer) { + fprintf(stderr, "broken tcp relay for new peer\n"); + gc_peer_delete(m, chat->group_number, peer_number, nullptr, 0, false); + } - if (gconn == nullptr) { return -1; } + if (nodes_count > 0) { + int add_tcp_result = add_tcp_relay_connection(chat->tcp_conn, gconn->tcp_connection_num, + node->ip_port, node->public_key); + + if (add_tcp_result < 0 && is_new_peer && !ipp) { + fprintf(stderr, "broken tcp relay for new peer\n"); + gc_peer_delete(m, group_number, peer_number, nullptr, 0, false); + return -1; + } + + if (add_tcp_result >= 0) { + save_tcp_relay(gconn, node); + } + } + uint8_t sender_session_pk[ENC_PUBLIC_KEY]; memcpy(sender_session_pk, data, ENC_PUBLIC_KEY); @@ -4319,36 +4396,52 @@ static int handle_gc_handshake_request(Messenger *m, int groupnumber, IP_Port *i uint8_t request_type = data[ENC_PUBLIC_KEY + SIG_PUBLIC_KEY]; uint8_t join_type = data[ENC_PUBLIC_KEY + SIG_PUBLIC_KEY + 1]; - if (join_type == HJ_PUBLIC && chat->shared_state.privacy_state != GI_PUBLIC) { - gc_peer_delete(m, groupnumber, peernumber, nullptr, 0); + net_unpack_u32(data + ENC_PUBLIC_KEY + SIG_PUBLIC_KEY + 2, &gconn->friend_shared_state_version); + + if (join_type == HJ_PUBLIC && !is_public_chat(chat)) { + gc_peer_delete(m, group_number, peer_number, (const uint8_t *)"Join priv", 9, false); return -1; } - ++gconn->recv_message_id; + ++gconn->received_message_id; - if (send_gc_handshake_response(chat, peernumber, request_type) == -1) { - return -1; + if (!ipp) { + gconn->pending_handshake_type = request_type; + gconn->is_oob_handshake = false; + gconn->is_pending_handshake_response = true; + const uint64_t timeout = mono_time_get(chat->mono_time) + HANDSHAKE_SENDING_TIMEOUT; + gconn->last_received_ping_time = timeout; + gconn->pending_handshake = timeout; + } else { + send_gc_handshake_response(chat, peer_number, request_type); + ++gconn->send_message_id; + gconn->pending_handshake = 0; + // fprintf(stderr, "in handle_gc_handshake_request gconn->send_message_id %ld\n", gconn->send_message_id); } - return peernumber; + + // fprintf(stderr, "in handle_gc_handshake_request success\n"); + + return peer_number; } /* Handles handshake request and handshake response packets. * - * Returns peernumber of connecting peer on success. + * Returns peer_number of connecting peer on success. * Returns -1 on failure. */ static int handle_gc_handshake_packet(Messenger *m, GC_Chat *chat, IP_Port *ipp, const uint8_t *packet, uint16_t length, bool direct_conn) { - if (length != GC_ENCRYPTED_HS_PACKET_SIZE) { + if (length < GC_ENCRYPTED_HS_PACKET_SIZE) { return -1; } uint8_t sender_pk[ENC_PUBLIC_KEY]; VLA(uint8_t, data, length - 1 - HASH_ID_BYTES - ENC_PUBLIC_KEY - CRYPTO_NONCE_SIZE - CRYPTO_MAC_SIZE); - int plain_len = uwrap_group_handshake_packet(chat->self_secret_key, sender_pk, data, SIZEOF_VLA(data), packet, length); + int plain_len = uwrap_group_handshake_packet(chat->self_secret_key, sender_pk, data, SIZEOF_VLA(data), + packet, length); if (plain_len != SIZEOF_VLA(data)) { return -1; @@ -4364,47 +4457,42 @@ static int handle_gc_handshake_packet(Messenger *m, GC_Chat *chat, IP_Port *ipp, } const uint8_t *real_data = data + (sizeof(uint8_t) + HASH_ID_BYTES); - uint16_t real_len = plain_len - (sizeof(uint8_t) - HASH_ID_BYTES); + uint16_t real_len = plain_len - sizeof(uint8_t) - HASH_ID_BYTES; - int peernumber = -1; + int peer_number; if (handshake_type == GH_REQUEST) { - if (ipp == nullptr) { - return -1; - } - - peernumber = handle_gc_handshake_request(m, chat->groupnumber, ipp, sender_pk, real_data, real_len); + peer_number = handle_gc_handshake_request(m, chat->group_number, ipp, sender_pk, real_data, real_len); } else if (handshake_type == GH_RESPONSE) { - peernumber = handle_gc_handshake_response(m, chat->groupnumber, sender_pk, real_data, real_len); + peer_number = handle_gc_handshake_response(m, chat->group_number, sender_pk, real_data, real_len); } else { return -1; - } - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return -1; } - if (peernumber > 0 && direct_conn) { - gconn->last_recv_direct_time = mono_time_get(chat->mono_time); + if (peer_number > 0 && direct_conn) { + gconn->last_received_direct_time = mono_time_get(chat->mono_time); } - return peernumber; + return peer_number; } -int handle_gc_lossless_helper(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, +int handle_gc_lossless_helper(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint16_t length, uint64_t message_id, uint8_t packet_type) { GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return -1; @@ -4412,43 +4500,46 @@ int handle_gc_lossless_helper(Messenger *m, int groupnumber, uint32_t peernumber switch (packet_type) { case GP_BROADCAST: - return handle_gc_broadcast(m, groupnumber, peernumber, data, length); + return handle_gc_broadcast(m, group_number, peer_number, data, length); - case GP_PEER_INFO_RESPONSE: - return handle_gc_peer_info_response(m, groupnumber, peernumber, data, length); + case GP_PEER_ANNOUNCE: + return handle_gc_peer_announcement(m, group_number, data, length); case GP_PEER_INFO_REQUEST: - return handle_gc_peer_info_request(m, groupnumber, gconn); + return handle_gc_peer_info_request(m, group_number, gconn); + + case GP_PEER_INFO_RESPONSE: + return handle_gc_peer_info_response(m, group_number, peer_number, data, length); case GP_SYNC_REQUEST: - return handle_gc_sync_request(m, groupnumber, gconn, data, length); + return handle_gc_sync_request(m, group_number, peer_number, gconn, data, length); case GP_SYNC_RESPONSE: - return handle_gc_sync_response(m, groupnumber, peernumber, gconn, data, length); + return handle_gc_sync_response(m, group_number, peer_number, gconn, data, length); case GP_INVITE_REQUEST: - return handle_gc_invite_request(m, groupnumber, peernumber, data, length); + return handle_gc_invite_request(m, group_number, peer_number, data, length); case GP_INVITE_RESPONSE: - return handle_gc_invite_response(m, groupnumber, gconn, data, length); + return handle_gc_invite_response(m, group_number, gconn, data, length); case GP_TOPIC: - return handle_gc_topic(m, groupnumber, peernumber, data, length); + return handle_gc_topic(m, group_number, peer_number, data, length); case GP_SHARED_STATE: - return handle_gc_shared_state(m, groupnumber, peernumber, data, length); + return handle_gc_shared_state(m, group_number, peer_number, data, length); case GP_MOD_LIST: - return handle_gc_mod_list(m, groupnumber, peernumber, data, length); + return handle_gc_mod_list(m, group_number, peer_number, data, length); case GP_SANCTIONS_LIST: - return handle_gc_sanctions_list(m, groupnumber, peernumber, data, length); + return handle_gc_sanctions_list(m, group_number, peer_number, data, length); case GP_HS_RESPONSE_ACK: - return handle_gc_hs_response_ack(m, groupnumber, gconn, data, length); + return handle_gc_hs_response_ack(m, group_number, gconn); case GP_CUSTOM_PACKET: - return handle_gc_custom_packet(m, groupnumber, peernumber, data, length); + return handle_gc_custom_packet(m, group_number, peer_number, data, length); default: fprintf(stderr, "Warning: handling invalid lossless group packet type %u\n", packet_type); @@ -4471,11 +4562,11 @@ static int handle_gc_lossless_message(Messenger *m, GC_Chat *chat, const uint8_t uint8_t sender_pk[ENC_PUBLIC_KEY]; memcpy(sender_pk, packet + 1 + HASH_ID_BYTES, ENC_PUBLIC_KEY); - int peernumber = get_peernum_of_enc_pk(chat, sender_pk); + int peer_number = get_peernum_of_enc_pk(chat, sender_pk); - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); - if (gconn == nullptr) { + if (!gconn) { return -1; } @@ -4486,13 +4577,18 @@ static int handle_gc_lossless_message(Messenger *m, GC_Chat *chat, const uint8_t int len = unwrap_group_packet(gconn->shared_key, data, &message_id, &packet_type, packet, length); if (len <= 0) { + char id_str[IDSTRING_LEN]; + fprintf(stderr, "%s\n", id_to_string(sender_pk, id_str, IDSTRING_LEN)); return -1; } - if (packet_type != GP_HS_RESPONSE_ACK && !gconn->handshaked) { + if (packet_type != GP_HS_RESPONSE_ACK && packet_type != GP_INVITE_REQUEST && !gconn->handshaked) { + fprintf(stderr, "not ack %d\n", packet_type); return -1; } + // fprintf(stderr, "handle_gc_lossless_message %d\n", packet_type); + uint32_t sender_pk_hash; net_unpack_u32(data, &sender_pk_hash); @@ -4503,7 +4599,21 @@ static int handle_gc_lossless_message(Messenger *m, GC_Chat *chat, const uint8_t const uint8_t *real_data = data + HASH_ID_BYTES; uint16_t real_len = len - HASH_ID_BYTES; - int lossless_ret = gcc_handle_recv_message(chat, peernumber, real_data, real_len, packet_type, message_id); + bool is_invite_packet = packet_type == GP_INVITE_REQUEST || packet_type == GP_INVITE_RESPONSE + || packet_type == GP_INVITE_RESPONSE_REJECT; + + if (message_id == 3 && is_invite_packet && gconn->received_message_id == 1) { + // probably we missed initial handshake packet. update received packets index + // TODO: FIXME + ++gconn->received_message_id; + } + + int lossless_ret = gcc_handle_received_message(chat, peer_number, real_data, real_len, packet_type, message_id); + + if (packet_type == GP_INVITE_REQUEST && !gconn->handshaked) { // race condition + fprintf(stderr, "race condition %d\n", packet_type); + return 0; + } if (lossless_ret == -1) { fprintf(stderr, "failed to handle packet %llu (type %u)\n", (unsigned long long)message_id, packet_type); @@ -4518,29 +4628,27 @@ static int handle_gc_lossless_message(Messenger *m, GC_Chat *chat, const uint8_t /* request missing packet */ if (lossless_ret == 1) { - fprintf(stderr, "recieved out of order packet. expected %lu, got %lu\n", gconn->recv_message_id + 1, message_id); - return gc_send_message_ack(chat, gconn, 0, gconn->recv_message_id + 1); + fprintf(stderr, "received out of order packet. expected %lu, got %lu\n", gconn->received_message_id + 1, message_id); + return gc_send_message_ack(chat, gconn, 0, gconn->received_message_id + 1); } - int ret = handle_gc_lossless_helper(m, chat->groupnumber, peernumber, real_data, real_len, message_id, packet_type); + int ret = handle_gc_lossless_helper(m, chat->group_number, peer_number, real_data, real_len, message_id, packet_type); if (ret == -1) { fprintf(stderr, "lossless handler failed (type %u)\n", packet_type); return -1; } - /* we need to get the peernumber again because it may have changed */ - peernumber = get_peernum_of_enc_pk(chat, sender_pk); - // TODO(iphydf): This is fixing one symptom of a problem: objects - // pointed at by gconn can easily go out of scope because of realloc. - gconn = gcc_get_connection(chat, peernumber); + /* we need to get the peer_number and gconn again because it may have changed */ + peer_number = get_peernum_of_enc_pk(chat, sender_pk); + gconn = gcc_get_connection(chat, peer_number); - if (lossless_ret == 2 && peernumber != -1) { + if (lossless_ret == 2 && peer_number != -1) { gc_send_message_ack(chat, gconn, message_id, 0); - gcc_check_recv_ary(m, chat->groupnumber, peernumber); + gcc_check_received_array(m, chat->group_number, peer_number); if (direct_conn) { - gconn->last_recv_direct_time = mono_time_get(chat->mono_time); + gconn->last_received_direct_time = mono_time_get(chat->mono_time); } } @@ -4562,9 +4670,9 @@ static int handle_gc_lossy_message(Messenger *m, GC_Chat *chat, const uint8_t *p uint8_t sender_pk[ENC_PUBLIC_KEY]; memcpy(sender_pk, packet + 1 + HASH_ID_BYTES, ENC_PUBLIC_KEY); - int peernumber = get_peernum_of_enc_pk(chat, sender_pk); + int peer_number = get_peernum_of_enc_pk(chat, sender_pk); - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return -1; @@ -4593,7 +4701,7 @@ static int handle_gc_lossy_message(Messenger *m, GC_Chat *chat, const uint8_t *p return -1; } - int ret = -1; + int ret; switch (packet_type) { case GP_MESSAGE_ACK: @@ -4601,19 +4709,19 @@ static int handle_gc_lossy_message(Messenger *m, GC_Chat *chat, const uint8_t *p break; case GP_PING: - ret = handle_gc_ping(m, chat->groupnumber, gconn, real_data, len); + ret = handle_gc_ping(m, chat->group_number, gconn, real_data, len); break; case GP_INVITE_RESPONSE_REJECT: - ret = handle_gc_invite_response_reject(m, chat->groupnumber, real_data, len); + ret = handle_gc_invite_response_reject(m, chat->group_number, real_data, len); break; case GP_TCP_RELAYS: - ret = handle_gc_tcp_relays(m, chat->groupnumber, gconn, real_data, len); + ret = handle_gc_tcp_relays(m, chat->group_number, gconn, real_data, len); break; case GP_CUSTOM_PACKET: - ret = handle_gc_custom_packet(m, chat->groupnumber, peernumber, real_data, len); + ret = handle_gc_custom_packet(m, chat->group_number, peer_number, real_data, len); break; default: @@ -4622,12 +4730,17 @@ static int handle_gc_lossy_message(Messenger *m, GC_Chat *chat, const uint8_t *p } if (ret != -1 && direct_conn) { - gconn->last_recv_direct_time = mono_time_get(m->mono_time); + gconn->last_received_direct_time = mono_time_get(m->mono_time); } return ret; } +static bool group_can_handle_packets(GC_Chat *chat) +{ + return chat->connection_state != CS_FAILED && chat->connection_state != CS_MANUALLY_DISCONNECTED; +} + /* Sends a group packet to appropriate handler function. * * Returns non-negative value on success. @@ -4650,7 +4763,7 @@ static int handle_gc_tcp_packet(void *object, int id, const uint8_t *packet, uin return -1; } - if (chat->connection_state == CS_FAILED) { + if (!group_can_handle_packets(chat)) { return -1; } @@ -4683,7 +4796,7 @@ static int handle_gc_tcp_oob_packet(void *object, const uint8_t *public_key, uns return -1; } - if (chat->connection_state == CS_FAILED) { + if (!group_can_handle_packets(chat)) { return -1; } @@ -4691,12 +4804,9 @@ static int handle_gc_tcp_oob_packet(void *object, const uint8_t *public_key, uns return -1; } - IP_Port ipp; - ipp.port = 0; - ipp.ip.family = net_family_tcp_family; - ipp.ip.ip.v6.uint32[0] = tcp_connections_number; + fprintf(stderr, "handle gc tcp oob handshake packet\n"); - if (handle_gc_handshake_packet(m, chat, &ipp, packet, length, false) == -1) { + if (handle_gc_handshake_packet(m, chat, nullptr, packet, length, false) == -1) { return -1; } @@ -4720,7 +4830,7 @@ static int handle_gc_udp_packet(void *object, IP_Port ipp, const uint8_t *packet return -1; } - if (chat->connection_state == CS_FAILED) { + if (!group_can_handle_packets(chat)) { return -1; } @@ -4836,41 +4946,55 @@ void gc_callback_rejected(Messenger *m, gc_rejected_cb *function, void *userdata c->rejected_userdata = userdata; } -/* Deletets peernumber from group. +/* + * Deletes peer_number from group. `no_callback` should be set to true if the `peer_exit` callback should not be triggered. * * Return 0 on success. * Return -1 on failure. */ -int gc_peer_delete(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, uint16_t length) +int gc_peer_delete(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint16_t length, + bool no_callback) { GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); - if (chat == nullptr) { + if (!chat) { return -1; } - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + if ((chat->connection_state == CS_DISCONNECTED + || chat->connection_state == CS_CONNECTING + || chat->connection_state == CS_MANUALLY_DISCONNECTED) + && !is_public_chat(chat)) { + return -1; + } - if (gconn == nullptr) { + GC_Connection *gconn = gcc_get_connection(chat, peer_number); + + if (!gconn) { return -1; } - if (c->peer_exit && gconn->confirmed) { - (*c->peer_exit)(m, groupnumber, chat->group[peernumber].peer_id, chat->group[peernumber].nick, - chat->group[peernumber].nick_len, data, length, c->peer_exit_userdata); + if (gconn->handshaked && !is_peer_confirmed(chat, gconn->addr.public_key)) { + memcpy(chat->confirmed_peers[chat->confirmed_peers_index], gconn->addr.public_key, ENC_PUBLIC_KEY); + chat->confirmed_peers_index = (chat->confirmed_peers_index + 1) % MAX_GC_CONFIRMED_PEERS; + } + + /* Needs to occur before peer is removed*/ + if (!no_callback && c->peer_exit && gconn->confirmed) { + (*c->peer_exit)(m, group_number, chat->group[peer_number].peer_id, chat->group[peer_number].nick, + chat->group[peer_number].nick_length, data, length, c->peer_exit_userdata); } kill_tcp_connection_to(chat->tcp_conn, gconn->tcp_connection_num); - gca_peer_cleanup(m->group_handler->announce, get_chat_id(chat->chat_public_key), gconn->addr.public_key); gcc_peer_cleanup(gconn); --chat->numpeers; - if (chat->numpeers != peernumber) { - memcpy(&chat->group[peernumber], &chat->group[chat->numpeers], sizeof(GC_GroupPeer)); - memcpy(&chat->gcc[peernumber], &chat->gcc[chat->numpeers], sizeof(GC_Connection)); + if (chat->numpeers != peer_number) { + memcpy(&chat->group[peer_number], &chat->group[chat->numpeers], sizeof(GC_GroupPeer)); + memcpy(&chat->gcc[peer_number], &chat->gcc[chat->numpeers], sizeof(GC_Connection)); } memset(&chat->group[chat->numpeers], 0, sizeof(GC_GroupPeer)); @@ -4895,55 +5019,57 @@ int gc_peer_delete(Messenger *m, int groupnumber, uint32_t peernumber, const uin return 0; } -/* Updates peernumber with info from `peer`. +/* Updates peer_number with info from `peer`. * - * Returns peernumber on success. + * Returns peer_number on success. * Returns -1 on failure. */ -static int peer_update(Messenger *m, int groupnumber, GC_GroupPeer *peer, uint32_t peernumber) +static int peer_update(Messenger *m, int group_number, GC_GroupPeer *peer, uint32_t peer_number) { GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; } - if (peer->nick_len == 0) { + if (peer->nick_length == 0) { return -1; } - int nick_num = get_nick_peernumber(chat, peer->nick, peer->nick_len); + int nick_num = get_nick_peer_number(chat, peer->nick, peer->nick_length); - if (nick_num != -1 && nick_num != peernumber) { /* duplicate nick */ + if ((nick_num != -1 && nick_num != peer_number)) { /* duplicate nick */ if (c->peer_exit) { - (*c->peer_exit)(m, groupnumber, chat->group[peernumber].peer_id, chat->group[peernumber].nick, - chat->group[peernumber].nick_len, nullptr, 0, c->peer_exit_userdata); + (*c->peer_exit)(m, group_number, chat->group[peer_number].peer_id, chat->group[peer_number].nick, + chat->group[peer_number].nick_length, nullptr, 0, c->peer_exit_userdata); } - gc_peer_delete(m, groupnumber, peernumber, nullptr, 0); + gc_peer_delete(m, group_number, peer_number, (const uint8_t *)"dup nick", 8, false); return -1; } - peer->peer_id = chat->group[peernumber].peer_id; - memcpy(&chat->group[peernumber], peer, sizeof(GC_GroupPeer)); - chat->group[peernumber].ignore = false; + GC_GroupPeer *curr_peer = &chat->group[peer_number]; + curr_peer->role = peer->role; + curr_peer->status = peer->status; + curr_peer->nick_length = peer->nick_length; + memcpy(curr_peer->nick, peer->nick, MAX_GC_NICK_SIZE); - return peernumber; + return peer_number; } -/* Adds a new peer to groupnumber's peer list. +/* Adds a new peer to group_number's peer list. * - * Return peernumber if success. + * Return peer_number if success. * Return -1 on failure. * Returns -2 if a peer with public_key is already in our peerlist. */ -static int peer_add(Messenger *m, int groupnumber, IP_Port *ipp, const uint8_t *public_key) +static int peer_add(Messenger *m, int group_number, IP_Port *ipp, const uint8_t *public_key) { GC_Session *c = m->group_handler; - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); - if (chat == nullptr) { + if (!chat) { return -1; } @@ -4961,7 +5087,7 @@ static int peer_add(Messenger *m, int groupnumber, IP_Port *ipp, const uint8_t * } } - int peernumber = chat->numpeers; + int peer_number = chat->numpeers; GC_Connection *tmp_gcc = (GC_Connection *)realloc(chat->gcc, sizeof(GC_Connection) * (chat->numpeers + 1)); @@ -4970,7 +5096,7 @@ static int peer_add(Messenger *m, int groupnumber, IP_Port *ipp, const uint8_t * return -1; } - memset(&tmp_gcc[peernumber], 0, sizeof(GC_Connection)); + memset(&tmp_gcc[peer_number], 0, sizeof(GC_Connection)); chat->gcc = tmp_gcc; GC_GroupPeer *tmp_group = (GC_GroupPeer *)realloc(chat->group, sizeof(GC_GroupPeer) * (chat->numpeers + 1)); @@ -4981,39 +5107,40 @@ static int peer_add(Messenger *m, int groupnumber, IP_Port *ipp, const uint8_t * } ++chat->numpeers; - memset(&tmp_group[peernumber], 0, sizeof(GC_GroupPeer)); + memset(&tmp_group[peer_number], 0, sizeof(GC_GroupPeer)); chat->group = tmp_group; - GC_Connection *gconn = &chat->gcc[peernumber]; + GC_Connection *gconn = &chat->gcc[peer_number]; + gconn->friend_shared_state_version = UINT32_MAX; + gconn->self_sent_shared_state_version = UINT32_MAX; if (ipp) { ipport_copy(&gconn->addr.ip_port, ipp); } - chat->group[peernumber].role = GR_INVALID; - chat->group[peernumber].peer_id = get_new_peer_id(chat); - chat->group[peernumber].ignore = false; + chat->group[peer_number].role = GR_INVALID; + chat->group[peer_number].peer_id = get_new_peer_id(chat); + chat->group[peer_number].ignore = false; crypto_box_keypair(gconn->session_public_key, gconn->session_secret_key); memcpy(gconn->addr.public_key, public_key, ENC_PUBLIC_KEY); /* we get the sig key in the handshake */ gconn->public_key_hash = get_peer_key_hash(public_key); - gconn->last_rcvd_ping = mono_time_get(chat->mono_time) + (rand() % GC_PING_INTERVAL); - gconn->time_added = mono_time_get(chat->mono_time); + gconn->last_received_ping_time = mono_time_get(chat->mono_time) + (rand() % GC_PING_INTERVAL); gconn->send_message_id = 1; - gconn->send_ary_start = 1; - gconn->recv_message_id = 0; + gconn->send_array_start = 1; + gconn->received_message_id = 0; gconn->tcp_connection_num = tcp_connection_num; - return peernumber; + return peer_number; } /* Copies own peer data to peer */ -static void self_to_peer(const GC_Session *c, const GC_Chat *chat, GC_GroupPeer *peer) +static void self_to_peer(const GC_Chat *chat, GC_GroupPeer *peer) { memset(peer, 0, sizeof(GC_GroupPeer)); - memcpy(peer->nick, chat->group[0].nick, chat->group[0].nick_len); - peer->nick_len = chat->group[0].nick_len; + memcpy(peer->nick, chat->group[0].nick, chat->group[0].nick_length); + peer->nick_length = chat->group[0].nick_length; peer->status = chat->group[0].status; peer->role = chat->group[0].role; } @@ -5023,14 +5150,14 @@ static void self_to_peer(const GC_Session *c, const GC_Chat *chat, GC_GroupPeer */ static bool peer_timed_out(const Mono_Time *mono_time, const GC_Chat *chat, GC_Connection *gconn) { - return mono_time_is_timeout(mono_time, gconn->last_rcvd_ping, gconn->confirmed + return mono_time_is_timeout(mono_time, gconn->last_received_ping_time, gconn->confirmed ? GC_CONFIRMED_PEER_TIMEOUT - : GC_UNCONFRIMED_PEER_TIMEOUT); + : GC_UNCONFIRMED_PEER_TIMEOUT); } -static void do_peer_connections(Messenger *m, int groupnumber) +static void do_peer_connections(Messenger *m, int group_number) { - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(m->group_handler, group_number); if (chat == nullptr) { return; @@ -5046,7 +5173,7 @@ static void do_peer_connections(Messenger *m, int groupnumber) } if (peer_timed_out(m->mono_time, chat, &chat->gcc[i])) { - gc_peer_delete(m, groupnumber, i, (const uint8_t *)"Timed out", 9); + gc_peer_delete(m, group_number, i, (const uint8_t *)"Timed out", 9, false); } else { gcc_resend_packets(m, chat, i); // This function may delete the peer } @@ -5087,25 +5214,6 @@ static void ping_group(GC_Chat *chat) chat->last_sent_ping_time = mono_time_get(chat->mono_time); } -/* Searches the DHT for nodes belonging to the group periodically in case of a split group. - * The search frequency is relative to the number of peers in the group. - */ -#define GROUP_SEARCH_ANNOUNCE_INTERVAL 180 -static void search_gc_announce(GC_Session *c, GC_Chat *chat) -{ - if (!mono_time_is_timeout(c->messenger->mono_time, chat->announce_search_timer, GROUP_SEARCH_ANNOUNCE_INTERVAL)) { - return; - } - - chat->announce_search_timer = mono_time_get(c->messenger->mono_time); - uint32_t cnumpeers = get_gc_confirmed_numpeers(chat); - - if (random_int_range(cnumpeers) == 0) { - /* DHT response/sync procedure is handled in gc_update_addresses_cb() */ - group_get_nodes_request(c, chat); - } -} - static void do_new_connection_cooldown(GC_Chat *chat) { if (chat->connection_O_metre == 0) { @@ -5124,9 +5232,65 @@ static void do_new_connection_cooldown(GC_Chat *chat) } } +#define PENDING_HANDSHAKE_SENDING_MAX_INTERVAL 15 +static int send_pending_handshake(GC_Chat *chat, GC_Connection *gconn, uint32_t peer_number) +{ + if (!chat || !gconn || !peer_number) { + return 1; + } + + uint64_t time = mono_time_get(chat->mono_time); + + if (gconn->pending_handshake && chat->should_start_sending_handshakes) { + gconn->pending_handshake = time; + } + + if (!gconn->pending_handshake || time < gconn->pending_handshake) { + return 0; + } + + if (gconn->handshaked) { + gconn->pending_handshake = 0; + return 0; + } + + int result; + + if (gconn->is_pending_handshake_response) { + result = send_gc_handshake_response(chat, peer_number, gconn->pending_handshake_type); + } else if (gconn->is_oob_handshake) { + // fprintf(stderr, "in send pending gc oob handshake\n"); + result = send_gc_oob_handshake_packet(chat, peer_number, GH_REQUEST, + gconn->pending_handshake_type, chat->join_type); + } else { + // fprintf(stderr, "in send pending gc handshake\n"); + result = send_gc_handshake_packet(chat, peer_number, GH_REQUEST, + gconn->pending_handshake_type, chat->join_type); + } + + // fprintf(stderr, "in send pending handshake result %d\n", result); + + if (!result || time > gconn->pending_handshake + PENDING_HANDSHAKE_SENDING_MAX_INTERVAL) { + gconn->pending_handshake = 0; + } + + if (!result) { + // fprintf(stderr, "in send pending handshake increment %ld\n", gconn->send_message_id); + + if (!gconn->is_pending_handshake_response) { + gconn->send_message_id = 2; + } else { + ++gconn->send_message_id; + } + + } + + return 0; +} + static void do_group_tcp(GC_Chat *chat, void *userdata) { - if (!chat->tcp_conn) { + if (!chat->tcp_conn || chat->connection_state <= CS_MANUALLY_DISCONNECTED) { return; } @@ -5136,19 +5300,22 @@ static void do_group_tcp(GC_Chat *chat, void *userdata) for (i = 1; i < chat->numpeers; ++i) { GC_Connection *gconn = &chat->gcc[i]; - bool tcp_set = gcc_connection_is_direct(chat->mono_time, gconn) ? false : true; + bool tcp_set = !gcc_connection_is_direct(chat->mono_time, gconn); set_tcp_connection_to_status(chat->tcp_conn, gconn->tcp_connection_num, tcp_set); + + send_pending_handshake(chat, gconn, i); } + + chat->should_start_sending_handshakes = false; } -#define GROUP_JOIN_ATTEMPT_INTERVAL 3 -#define GROUP_GET_NEW_NODES_INTERVAL 15 -#define GROUP_MAX_GET_NODES_ATTEMPTS 3 +#define GROUP_JOIN_ATTEMPT_INTERVAL 20 +#define TCP_RELAYS_TEST_COUNT 1 /* CS_CONNECTED: Peers are pinged, unsent packets are resent, and timeouts are checked. * CS_CONNECTING: Look for new DHT nodes after an interval. * CS_DISCONNECTED: Send an invite request using a random node if our timeout GROUP_JOIN_ATTEMPT_INTERVAL has expired. - * CS_FAILED: Do nothing. This occurrs if we cannot connect to a group or our invite request is rejected. + * CS_FAILED: Do nothing. This occurs if we cannot connect to a group or our invite request is rejected. */ void do_gc(GC_Session *c, void *userdata) { @@ -5157,6 +5324,7 @@ void do_gc(GC_Session *c, void *userdata) } uint32_t i; + int j; for (i = 0; i < c->num_chats; ++i) { GC_Chat *chat = &c->chats[i]; @@ -5167,57 +5335,45 @@ void do_gc(GC_Session *c, void *userdata) ping_group(chat); do_peer_connections(c->messenger, i); do_new_connection_cooldown(chat); - search_gc_announce(c, chat); break; } case CS_CONNECTING: { - if (chat->get_nodes_attempts > GROUP_MAX_GET_NODES_ATTEMPTS) { - self_gc_connected(c->messenger->mono_time, chat); + if (mono_time_is_timeout(chat->mono_time, chat->last_join_attempt, GROUP_JOIN_ATTEMPT_INTERVAL)) { + chat->connection_state = CS_DISCONNECTED; + } - /* If we can't get an invite we assume the group is empty */ - if (chat->shared_state.version == 0 || group_announce_request(c, chat) == -1) { - if (c->rejected) { - (*c->rejected)(c->messenger, i, GJ_INVITE_FAILED, c->rejected_userdata); - } + break; + } - chat->connection_state = CS_FAILED; - } + case CS_DISCONNECTED: { + Node_format tcp_relays[TCP_RELAYS_TEST_COUNT]; + int tcp_num = tcp_copy_connected_relays(chat->tcp_conn, tcp_relays, TCP_RELAYS_TEST_COUNT); - if (chat->group[0].role == GR_FOUNDER) { - if (sign_gc_shared_state(chat) == -1) { - chat->connection_state = CS_FAILED; - } - } + if (tcp_num != TCP_RELAYS_TEST_COUNT) { + add_tcp_relays_to_chat(c->messenger, chat); + } + if (chat->numpeers <= 1 + || !mono_time_is_timeout(c->messenger->mono_time, chat->last_join_attempt, GROUP_JOIN_ATTEMPT_INTERVAL)) { break; } - if (mono_time_is_timeout(c->messenger->mono_time, chat->last_get_nodes_attempt, GROUP_GET_NEW_NODES_INTERVAL)) { - ++chat->get_nodes_attempts; - chat->last_get_nodes_attempt = mono_time_get(c->messenger->mono_time); - group_get_nodes_request(c, chat); - } + chat->last_join_attempt = mono_time_get(c->messenger->mono_time); + chat->connection_state = CS_CONNECTING; - chat->connection_state = CS_DISCONNECTED; - break; - } + for (j = 1; j < chat->numpeers; ++j) { + GC_Connection *gconn = &chat->gcc[j]; - case CS_DISCONNECTED: { - if (chat->num_addrs - && mono_time_is_timeout(c->messenger->mono_time, chat->last_join_attempt, GROUP_JOIN_ATTEMPT_INTERVAL)) { - send_gc_handshake_request(c->messenger, i, chat->addr_list[chat->addrs_idx].ip_port, - chat->addr_list[chat->addrs_idx].public_key, HS_INVITE_REQUEST, - chat->join_type); - - chat->last_join_attempt = mono_time_get(c->messenger->mono_time); - chat->addrs_idx = (chat->addrs_idx + 1) % chat->num_addrs; + if (!gconn->handshaked && !gconn->pending_handshake) { + gconn->pending_handshake = mono_time_get(c->messenger->mono_time) + HANDSHAKE_SENDING_TIMEOUT; + } } - chat->connection_state = CS_CONNECTING; break; } + case CS_MANUALLY_DISCONNECTED: case CS_FAILED: { break; } @@ -5268,25 +5424,57 @@ static int get_new_group_index(GC_Session *c) int new_index = c->num_chats; memset(&(c->chats[new_index]), 0, sizeof(GC_Chat)); + memset(&(c->chats[new_index].saved_invites), -1, MAX_GC_SAVED_INVITES * sizeof(uint32_t)); ++c->num_chats; return new_index; } -static int init_gc_tcp_connection(Messenger *m, GC_Chat *chat) +static GC_Chat *get_chat_by_tcp_connections(GC_Session *group_handler, TCP_Connections *tcp_c) { - chat->tcp_conn = new_tcp_connections(m->mono_time, chat->self_secret_key, &m->options.proxy_info); + uint32_t i; - if (chat->tcp_conn == nullptr) { - return -1; + for (i = 0; i < group_handler->num_chats; ++i) { + GC_Chat *chat = &group_handler->chats[i]; + + if (chat->tcp_conn == tcp_c) { + return chat; + } + } + + return nullptr; +} + +static void handle_connection_status_updated_callback(void *object, TCP_Connections *tcp_c, int status) +{ + Messenger *m = (Messenger *)object; + + GC_Chat *chat = get_chat_by_tcp_connections(m->group_handler, tcp_c); + + if (!chat) { + return; + } + + // fprintf(stderr, "updated gc data\n"); + + if (status != TCP_CONNECTIONS_STATUS_REGISTERED) { + // fprintf(stderr, "update self ann\n"); + chat->should_update_self_announces = true; + } + + if (status == TCP_CONNECTIONS_STATUS_ONLINE) { + // fprintf(stderr, "try send handshakes\n"); + chat->should_start_sending_handshakes = true; } +} +static void add_tcp_relays_to_chat(Messenger *m, GC_Chat *chat) +{ uint16_t num_relays = tcp_connections_count(nc_get_tcp_c(m->net_crypto)); if (num_relays == 0) { - // TODO(iphydf): This should be an error, but for now TCP isn't working. - return 0; + return; } VLA(Node_format, tcp_relays, num_relays); @@ -5295,22 +5483,43 @@ static int init_gc_tcp_connection(Messenger *m, GC_Chat *chat) for (i = 0; i < num; ++i) { add_tcp_relay_global(chat->tcp_conn, tcp_relays[i].ip_port, tcp_relays[i].public_key); } +} + +static int init_gc_tcp_connection(Messenger *m, GC_Chat *chat) +{ + chat->tcp_conn = new_tcp_connections(m->mono_time, chat->self_secret_key, &m->options.proxy_info); + + if (chat->tcp_conn == nullptr) { + return -1; + } + + add_tcp_relays_to_chat(m, chat); set_packet_tcp_connection_callback(chat->tcp_conn, &handle_gc_tcp_packet, m); set_oob_packet_tcp_connection_callback(chat->tcp_conn, &handle_gc_tcp_oob_packet, m); + set_connection_status_updated_callback(chat->tcp_conn, &handle_connection_status_updated_callback, m); + return 0; } -static int create_new_group(GC_Session *c, bool founder) +static int create_new_group(GC_Session *c, const uint8_t *nick, size_t nick_length, bool founder) { - int groupnumber = get_new_group_index(c); + if (nick == nullptr || nick_length == 0) { + return -1; + } + + if (nick_length > MAX_GC_NICK_SIZE) { + return -1; + } - if (groupnumber == -1) { + int group_number = get_new_group_index(c); + + if (group_number == -1) { return -1; } Messenger *m = c->messenger; - GC_Chat *chat = &c->chats[groupnumber]; + GC_Chat *chat = &c->chats[group_number]; create_extended_keypair(chat->self_public_key, chat->self_secret_key); @@ -5319,29 +5528,27 @@ static int create_new_group(GC_Session *c, bool founder) return -1; } - chat->groupnumber = groupnumber; + chat->group_number = group_number; chat->numpeers = 0; chat->connection_state = CS_DISCONNECTED; chat->net = m->net; chat->mono_time = m->mono_time; - chat->last_get_nodes_attempt = mono_time_get(m->mono_time); chat->last_sent_ping_time = mono_time_get(m->mono_time); - chat->announce_search_timer = mono_time_get(m->mono_time); - if (peer_add(m, groupnumber, nullptr, chat->self_public_key) != 0) { /* you are always peernumber/index 0 */ + if (peer_add(m, group_number, nullptr, chat->self_public_key) != 0) { /* you are always peer_number/index 0 */ group_delete(c, chat); return -1; } - memcpy(chat->group[0].nick, m->name, m->name_length); - chat->group[0].nick_len = m->name_length; - chat->group[0].status = m->userstatus; + memcpy(chat->group[0].nick, nick, nick_length); + chat->group[0].nick_length = nick_length; + chat->group[0].status = GS_NONE; chat->group[0].role = founder ? GR_FOUNDER : GR_USER; chat->gcc[0].confirmed = true; chat->self_public_key_hash = chat->gcc[0].public_key_hash; memcpy(chat->gcc[0].addr.public_key, chat->self_public_key, EXT_PUBLIC_KEY); - return groupnumber; + return group_number; } /* Initializes group shared state and creates a signature for it using the chat secret key. @@ -5375,50 +5582,92 @@ static int init_gc_sanctions_creds(GC_Chat *chat) return 0; } +static void gc_load_peers(Messenger *m, GC_Chat *chat, GC_SavedPeerInfo *addrs, uint16_t num_addrs) +{ + int i; + + for (i = 0; i < num_addrs && i < MAX_GC_PEER_ADDRS; ++i) { + bool ip_port_is_set = ipport_isset(&addrs[i].ip_port); + IP_Port *ip_port = ip_port_is_set ? &addrs[i].ip_port : nullptr; + int peer_number = peer_add(m, chat->group_number, ip_port, addrs[i].public_key); + + if (peer_number < 0) { + continue; + } + + GC_Connection *gconn = gcc_get_connection(chat, peer_number); + + if (!gconn) { + continue; + } + + add_tcp_relay_global(chat->tcp_conn, addrs[i].tcp_relay.ip_port, addrs[i].tcp_relay.public_key); + + int add_tcp_result = add_tcp_relay_connection(chat->tcp_conn, gconn->tcp_connection_num, + addrs[i].tcp_relay.ip_port, + addrs[i].tcp_relay.public_key); + + if (add_tcp_result < 0 && !ip_port_is_set) { + // fprintf(stderr, "error adding relay\n"); + continue; + } + + int save_tcp_result = save_tcp_relay(gconn, &addrs[i].tcp_relay); + + if (save_tcp_result < 0 && !ip_port_is_set) { + continue; + } + + memcpy(gconn->oob_relay_pk, addrs[i].tcp_relay.public_key, ENC_PUBLIC_KEY); + gconn->is_oob_handshake = add_tcp_result == 0; + gconn->is_pending_handshake_response = false; + gconn->pending_handshake_type = HS_INVITE_REQUEST; + const uint64_t timeout = mono_time_get(chat->mono_time) + HANDSHAKE_SENDING_TIMEOUT; + gconn->pending_handshake = timeout; + gconn->last_received_ping_time = timeout; + } +} + /* Loads a previously saved group and attempts to connect to it. * - * Returns groupnumber on success. + * Returns group number on success. * Returns -1 on failure. */ -int gc_group_load(GC_Session *c, struct Saved_Group *save) +int gc_group_load(GC_Session *c, Saved_Group *save, int group_number) { - int groupnumber = get_new_group_index(c); + group_number = group_number == -1 ? get_new_group_index(c) : group_number; - if (groupnumber == -1) { + if (group_number == -1) { return -1; } uint64_t tm = mono_time_get(c->messenger->mono_time); Messenger *m = c->messenger; - GC_Chat *chat = &c->chats[groupnumber]; - - if (init_gc_tcp_connection(m, chat) == -1) { - return -1; - } + GC_Chat *chat = &c->chats[group_number]; + bool is_active_chat = save->group_connection_state != SGCS_DISCONNECTED; - chat->groupnumber = groupnumber; + chat->group_number = group_number; chat->numpeers = 0; - chat->connection_state = CS_DISCONNECTED; + chat->connection_state = is_active_chat ? CS_CONNECTING : CS_MANUALLY_DISCONNECTED; chat->join_type = HJ_PRIVATE; + chat->last_join_attempt = tm; chat->net = m->net; chat->mono_time = m->mono_time; - chat->last_get_nodes_attempt = tm; chat->last_sent_ping_time = tm; - chat->announce_search_timer = tm; memcpy(chat->shared_state.founder_public_key, save->founder_public_key, EXT_PUBLIC_KEY); - chat->shared_state.group_name_len = net_ntohs(save->group_name_len); + chat->shared_state.group_name_len = net_ntohs(save->group_name_length); memcpy(chat->shared_state.group_name, save->group_name, MAX_GC_GROUP_NAME_SIZE); chat->shared_state.privacy_state = save->privacy_state; chat->shared_state.maxpeers = net_ntohs(save->maxpeers); - chat->shared_state.passwd_len = net_ntohs(save->passwd_len); - memcpy(chat->shared_state.passwd, save->passwd, MAX_GC_PASSWD_SIZE); + chat->shared_state.password_length = net_ntohs(save->password_length); + memcpy(chat->shared_state.password, save->password, MAX_GC_PASSWORD_SIZE); memcpy(chat->shared_state.mod_list_hash, save->mod_list_hash, GC_MODERATION_HASH_SIZE); - chat->shared_state.version = net_ntohl(save->sstate_version); - memcpy(chat->shared_state_sig, save->sstate_signature, SIGNATURE_SIZE); + chat->shared_state.version = net_ntohl(save->shared_state_version); + memcpy(chat->shared_state_sig, save->shared_state_signature, SIGNATURE_SIZE); - chat->topic_info.length = net_ntohs(save->topic_len); + chat->topic_info.length = net_ntohs(save->topic_length); memcpy(chat->topic_info.topic, save->topic, MAX_GC_TOPIC_SIZE); memcpy(chat->topic_info.public_sig_key, save->topic_public_sig_key, SIG_PUBLIC_KEY); chat->topic_info.version = net_ntohl(save->topic_version); @@ -5428,6 +5677,7 @@ int gc_group_load(GC_Session *c, struct Saved_Group *save) memcpy(chat->chat_secret_key, save->chat_secret_key, EXT_SECRET_KEY); uint16_t num_mods = net_ntohs(save->num_mods); + // fprintf(stderr, "num mods %d\n", num_mods); if (mod_list_unpack(chat, save->mod_list, num_mods * GC_MOD_LIST_ENTRY_SIZE, num_mods) == -1) { return -1; @@ -5438,16 +5688,16 @@ int gc_group_load(GC_Session *c, struct Saved_Group *save) chat->chat_id_hash = get_chat_id_hash(get_chat_id(chat->chat_public_key)); chat->self_public_key_hash = get_peer_key_hash(chat->self_public_key); - if (peer_add(m, groupnumber, nullptr, save->self_public_key) != 0) { + if (peer_add(m, group_number, nullptr, save->self_public_key) != 0) { return -1; } + memcpy(chat->gcc[0].addr.public_key, chat->self_public_key, EXT_PUBLIC_KEY); memcpy(chat->group[0].nick, save->self_nick, MAX_GC_NICK_SIZE); - chat->group[0].nick_len = net_ntohs(save->self_nick_len); + chat->group[0].nick_length = net_ntohs(save->self_nick_length); chat->group[0].role = save->self_role; chat->group[0].status = save->self_status; chat->gcc[0].confirmed = true; - memcpy(chat->gcc[0].addr.public_key, chat->self_public_key, EXT_PUBLIC_KEY); if (save->self_role == GR_FOUNDER) { if (init_gc_sanctions_creds(chat) == -1) { @@ -5455,37 +5705,57 @@ int gc_group_load(GC_Session *c, struct Saved_Group *save) } } - uint16_t i, num = 0, num_addrs = net_ntohs(save->num_addrs); + if (!is_active_chat) { + chat->save = (Saved_Group *)malloc(sizeof(Saved_Group)); - for (i = 0; i < num_addrs && i < MAX_GC_PEER_ADDRS; ++i) { - if (ipport_isset(&save->addrs[i].ip_port)) { - chat->addr_list[num] = save->addrs[i]; - ++num; + if (!chat->save) { + return -1; } - } - chat->num_addrs = num; + memcpy(chat->save, save, sizeof(Saved_Group)); - return groupnumber; + return group_number; + } + + if (init_gc_tcp_connection(m, chat) == -1) { + return -1; + } + + if (is_public_chat(chat)) { + m_add_group(m, chat); + } + + uint16_t num_addrs = net_ntohs(save->num_addrs); + gc_load_peers(m, chat, save->addrs, num_addrs); + + return group_number; } /* Creates a new group. * - * Return groupnumber on success. - * Return -1 if the group name is too long. - * Return -2 if the group name is empty. + * Return -1 if the nick or group name is too long. + * Return -2 if the nick or group name is empty. * Return -3 if the privacy state is an invalid type. * Return -4 if the the group object fails to initialize. * Return -5 if the group state fails to initialize. - * Return -6 if the group fails to announce to the DHT. - */ -int gc_group_add(GC_Session *c, uint8_t privacy_state, const uint8_t *group_name, uint16_t length) + * Return -6 if the announce was unsuccessful. +*/ +int gc_group_add(GC_Session *c, uint8_t privacy_state, const uint8_t *group_name, uint16_t group_name_length, + const uint8_t *nick, size_t nick_length) { - if (length > MAX_GC_GROUP_NAME_SIZE) { + if (group_name_length > MAX_GC_GROUP_NAME_SIZE) { return -1; } - if (length == 0 || group_name == nullptr) { + if (nick_length > MAX_GC_NICK_SIZE) { + return -1; + } + + if (group_name_length == 0 || group_name == nullptr) { + return -2; + } + + if (nick_length == 0 || nick == nullptr) { return -2; } @@ -5493,13 +5763,13 @@ int gc_group_add(GC_Session *c, uint8_t privacy_state, const uint8_t *group_name return -3; } - int groupnumber = create_new_group(c, true); + int group_number = create_new_group(c, nick, nick_length, true); - if (groupnumber == -1) { + if (group_number == -1) { return -4; } - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -4; @@ -5507,7 +5777,7 @@ int gc_group_add(GC_Session *c, uint8_t privacy_state, const uint8_t *group_name create_extended_keypair(chat->chat_public_key, chat->chat_secret_key); - if (init_gc_shared_state(chat, privacy_state, group_name, length) == -1) { + if (init_gc_shared_state(chat, privacy_state, group_name, group_name_length) == -1) { group_delete(c, chat); return -5; } @@ -5526,36 +5796,52 @@ int gc_group_add(GC_Session *c, uint8_t privacy_state, const uint8_t *group_name chat->join_type = HJ_PRIVATE; self_gc_connected(c->messenger->mono_time, chat); - if (group_announce_request(c, chat) == -1) { - group_delete(c, chat); - return -6; + if (is_public_chat(chat)) { + int friend_number = m_add_group(c->messenger, chat); + + if (friend_number < 0) { + group_delete(c, chat); + return -6; + } } - return groupnumber; + return group_number; } /* Sends an invite request to a public group using the chat_id. * - * If the group is not password protected passwd should be set to NULL and passwd_len should be 0. + * If the group is not password protected password should be set to NULL and password_length should be 0. * - * Return groupnumber on success. - * Reutrn -1 if the group object fails to initialize. + * Return group_number on success. + * Return -1 if the group object fails to initialize. * Return -2 if chat_id is NULL or a group with chat_id already exists in the chats array. - * Return -3 if there is an error setting the group password. + * Return -3 if nick is too long. + * Return -4 if nick is empty or nick length is zero. + * Return -5 if there is an error setting the group password. + * Return -6 if there is an error adding a friend. */ -int gc_group_join(GC_Session *c, const uint8_t *chat_id, const uint8_t *passwd, uint16_t passwd_len) +int gc_group_join(GC_Session *c, const uint8_t *chat_id, const uint8_t *nick, size_t nick_length, const uint8_t *passwd, + uint16_t passwd_len) { - if (chat_id == nullptr || group_exists(c, chat_id)) { + if (chat_id == nullptr || group_exists(c, chat_id) || getfriend_id(c->messenger, chat_id) != -1) { return -2; } - int groupnumber = create_new_group(c, false); + if (nick_length > MAX_GC_NICK_SIZE) { + return -3; + } + + if (nick == nullptr || nick_length == 0) { + return -4; + } + + int group_number = create_new_group(c, nick, nick_length, false); - if (groupnumber == -1) { + if (group_number == -1) { return -1; } - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { return -1; @@ -5564,142 +5850,461 @@ int gc_group_join(GC_Session *c, const uint8_t *chat_id, const uint8_t *passwd, expand_chat_id(chat->chat_public_key, chat_id); chat->chat_id_hash = get_chat_id_hash(get_chat_id(chat->chat_public_key)); chat->join_type = HJ_PUBLIC; + chat->last_join_attempt = mono_time_get(chat->mono_time); + chat->connection_state = CS_CONNECTING; if (passwd != nullptr && passwd_len > 0) { if (set_gc_password_local(chat, passwd, passwd_len) == -1) { - return -3; + return -5; } } - if (chat->num_addrs == 0) { - group_get_nodes_request(c, chat); + int friend_number = m_add_group(c->messenger, chat); + + if (friend_number < 0) { + return -6; } - return groupnumber; + return group_number; } -/* Resets chat saving all self state and attempts to reconnect to group */ -void gc_rejoin_group(GC_Session *c, GC_Chat *chat) +bool gc_disconnect_from_group(GC_Session *c, GC_Chat *chat) { - send_gc_self_exit(chat, nullptr, 0); + if (!c || !chat) { + return false; + } - clear_gc_addrs_list(chat); - chat->num_addrs = gc_copy_peer_addrs(chat, chat->addr_list, MAX_GC_PEER_ADDRS); + uint8_t previous_state = chat->connection_state; + chat->connection_state = CS_MANUALLY_DISCONNECTED; - uint32_t i; + chat->save = (Saved_Group *)malloc(sizeof(Saved_Group)); + + if (!chat->save) { + chat->connection_state = previous_state; + + return false; + } + + send_gc_broadcast_message(chat, nullptr, 0, GM_PEER_EXIT); + + // save info about group + pack_group_info(chat, chat->save, false); + + // cleanup gc data + group_cleanup(c, chat); + + return true; +} + +static bool gc_rejoin_disconnected_group(GC_Session *c, GC_Chat *chat) +{ + chat->save->group_connection_state = SGCS_CONNECTED; + + int group_loading_result = gc_group_load(c, chat->save, chat->group_number); + bool rejoin_successful = group_loading_result != -1; + + if (rejoin_successful) { + free(chat->save); + chat->save = nullptr; + } + + return rejoin_successful; +} + +static bool gc_rejoin_connected_group(GC_Session *c, GC_Chat *chat) +{ + GC_SavedPeerInfo peers[GROUP_SAVE_MAX_PEERS]; + uint16_t i, num_addrs = gc_copy_peer_addrs(chat, peers, GROUP_SAVE_MAX_PEERS); + chat->connection_state = CS_CONNECTED; /* Remove all peers except self. Numpeers decrements with each call to gc_peer_delete */ for (i = 1; chat->numpeers > 1;) { - if (gc_peer_delete(c->messenger, chat->groupnumber, i, nullptr, 0) == -1) { - break; - } + gc_peer_delete(c->messenger, chat->group_number, i, nullptr, 0, true); } - chat->connection_state = CS_DISCONNECTED; - chat->last_get_nodes_attempt = chat->num_addrs > 0 ? mono_time_get(c->messenger->mono_time) : - 0; /* Reconnect using saved peers or DHT */ - chat->last_sent_ping_time = mono_time_get(c->messenger->mono_time); - chat->last_join_attempt = mono_time_get(c->messenger->mono_time); - chat->announce_search_timer = mono_time_get(c->messenger->mono_time); - chat->get_nodes_attempts = 0; + if (is_public_chat(chat)) { + m_remove_group(c->messenger, chat); + m_add_group(c->messenger, chat); + } + + gc_load_peers(c->messenger, chat, peers, num_addrs); + chat->connection_state = CS_CONNECTING; + + return true; +} + +/* Resets chat saving all self state and attempts to reconnect to group */ +bool gc_rejoin_group(GC_Session *c, GC_Chat *chat) +{ + if (!c || !chat) { + return false; + } + + if (chat->connection_state == CS_MANUALLY_DISCONNECTED) { + return gc_rejoin_disconnected_group(c, chat); + } + + return gc_rejoin_connected_group(c, chat); +} + + +bool check_group_invite(GC_Session *c, const uint8_t *data, uint32_t length) +{ + if (length <= CHAT_ID_SIZE) { + return false; + } + + return !gc_get_group_by_public_key(c, data); } /* Invites friendnumber to chat. Packet includes: Type, chat_id, node * * Return 0 on success. * Return -1 if friendnumber does not exist. - * Return -2 on failure to create the invite data. - * Return -3 if the packet fails to send. + * Return -2 if the packet fails to send. */ -int gc_invite_friend(GC_Session *c, GC_Chat *chat, int32_t friendnumber, +int gc_invite_friend(GC_Session *c, GC_Chat *chat, int32_t friend_number, gc_send_group_invite_packet_cb *send_group_invite_packet) { - if (!friend_is_valid(c->messenger, friendnumber)) { + if (!friend_is_valid(c->messenger, friend_number)) { + return -1; + } + + uint8_t packet[MAX_GC_PACKET_SIZE]; + packet[0] = GP_FRIEND_INVITE; + packet[1] = GROUP_INVITE; + + memcpy(packet + 2, get_chat_id(chat->chat_public_key), CHAT_ID_SIZE); + + uint16_t length = 2 + CHAT_ID_SIZE; + + memcpy(packet + length, chat->self_public_key, ENC_PUBLIC_KEY); + + length += ENC_PUBLIC_KEY; + size_t group_name_length = chat->shared_state.group_name_len; + memcpy(packet + length, chat->shared_state.group_name, group_name_length); + length += group_name_length; + + if (send_group_invite_packet(c->messenger, friend_number, packet, length) == -1) { + return -2; + } + + chat->saved_invites[chat->saved_invites_index] = friend_number; + chat->saved_invites_index = (chat->saved_invites_index + 1) % MAX_GC_SAVED_INVITES; + + return 0; +} + +static int send_gc_invite_accepted_packet(Messenger *m, GC_Chat *chat, uint32_t friend_number) +{ + if (!friend_is_valid(m, friend_number)) { return -1; } + if (!chat) { + return -2; + } + uint8_t packet[MAX_GC_PACKET_SIZE]; packet[0] = GP_FRIEND_INVITE; + packet[1] = GROUP_INVITE_ACCEPTED; + + memcpy(packet + 2, get_chat_id(chat->chat_public_key), CHAT_ID_SIZE); + + uint16_t length = 2 + CHAT_ID_SIZE; + + memcpy(packet + length, chat->self_public_key, ENC_PUBLIC_KEY); + + length += ENC_PUBLIC_KEY; + + // fprintf(stderr, "send_gc_invite_accepted_packet\n"); + + if (send_group_invite_packet(m, friend_number, packet, length) == -1) { + fprintf(stderr, "send_gc_invite_accepted_packet error\n"); + return -3; + } + + return 0; +} + +static int send_gc_invite_confirmed_packet(Messenger *m, GC_Chat *chat, uint32_t friend_number, + const uint8_t *data, uint16_t length) +{ + if (!friend_is_valid(m, friend_number)) { + return -1; + } + + if (!chat) { + return -2; + } + + uint8_t packet[MAX_GC_PACKET_SIZE]; + packet[0] = GP_FRIEND_INVITE; + packet[1] = GROUP_INVITE_CONFIRMATION; + + memcpy(packet + 2, data, length); + +// fprintf(stderr, "send_gc_invite_confirmed_packet\n"); + + if (send_group_invite_packet(m, friend_number, packet, length + 2) == -1) { + return -3; + } + + return 0; +} + +static bool copy_ip_port_to_gconn(Messenger *m, int friend_number, GC_Connection *gconn) +{ + Friend *f = &m->friendlist[friend_number]; + int friend_connection_id = f->friendcon_id; + Friend_Conn *connection = &m->fr_c->conns[friend_connection_id]; + IP_Port *friend_ip_port = &connection->dht_ip_port; - memcpy(packet + 1, get_chat_id(chat->chat_public_key), CHAT_ID_SIZE); + if (!ipport_isset(friend_ip_port)) { + return false; + } + + memcpy(&gconn->addr.ip_port, friend_ip_port, sizeof(IP_Port)); - GC_Announce_Node self_node; + return true; +} - if (make_self_gca_node(c->messenger->dht, &self_node, chat->self_public_key) == -1) { +int handle_gc_invite_confirmed_packet(GC_Session *c, int friend_number, const uint8_t *data, + uint32_t length) +{ + // fprintf(stderr, "handle_gc_invite_confirmed_packet\n"); + + if (length < GC_JOIN_DATA_LENGTH) { return -1; } - int node_len = pack_gca_nodes(packet + 1 + CHAT_ID_SIZE, sizeof(GC_Announce_Node), &self_node, 1); + if (!friend_is_valid(c->messenger, friend_number)) { + return -4; + } + +// fprintf(stderr, "handle_gc_invite_confirmed_packet1\n"); + + uint8_t chat_id[CHAT_ID_SIZE]; + uint8_t invite_chat_pk[ENC_PUBLIC_KEY]; + + memcpy(chat_id, data, CHAT_ID_SIZE); + memcpy(invite_chat_pk, data + CHAT_ID_SIZE, ENC_PUBLIC_KEY); + + GC_Chat *chat = gc_get_group_by_public_key(c, chat_id); + + if (chat == nullptr) { + return -2; + } + + int peer_number = get_peernum_of_enc_pk(chat, invite_chat_pk); + + if (peer_number < 0) { + return -3; + } + + GC_Connection *gconn = gcc_get_connection(chat, peer_number); + + Node_format tcp_relays[GCC_MAX_TCP_SHARED_RELAYS]; + int num_nodes = unpack_nodes(tcp_relays, GCC_MAX_TCP_SHARED_RELAYS, + nullptr, data + ENC_PUBLIC_KEY + CHAT_ID_SIZE, + length - GC_JOIN_DATA_LENGTH, 1); + + bool copy_ip_port_result = copy_ip_port_to_gconn(c->messenger, friend_number, gconn); + + if (num_nodes <= 0 && !copy_ip_port_result) { + return -1; + } + + int i; + + for (i = 0; i < num_nodes; ++i) { + add_tcp_relay_connection(chat->tcp_conn, gconn->tcp_connection_num, tcp_relays[i].ip_port, + tcp_relays[i].public_key); + save_tcp_relay(gconn, &tcp_relays[i]); + } + + if (copy_ip_port_result) { + // fprintf(stderr, "sending handshake to hell\n"); + send_gc_handshake_packet(chat, peer_number, GH_REQUEST, HS_INVITE_REQUEST, chat->join_type); + gconn->send_message_id = 2; + } else { + gconn->pending_handshake_type = HS_INVITE_REQUEST; + gconn->is_oob_handshake = false; + gconn->is_pending_handshake_response = false; + gconn->pending_handshake = mono_time_get(chat->mono_time) + HANDSHAKE_SENDING_TIMEOUT; + } + + return 0; +} + +static bool friend_was_invited(GC_Chat *chat, int friend_number) +{ + int i; + + for (i = 0; i < MAX_GC_SAVED_INVITES; ++i) { + if (chat->saved_invites[i] == friend_number) { + chat->saved_invites[i] = -1; + + return true; + } + } - if (node_len <= 0) { - fprintf(stderr, "pack_gca_nodes failed in gc_invite_friend (%d)\n", node_len); + return false; +} + +int handle_gc_invite_accepted_packet(GC_Session *c, int friend_number, const uint8_t *data, + uint32_t length) +{ + // fprintf(stderr, "handle_gc_invite_accepted_packet1\n"); + + if (length < GC_JOIN_DATA_LENGTH) { return -1; } - uint16_t length = 1 + CHAT_ID_SIZE + node_len; + Messenger *m = c->messenger; + + if (!friend_is_valid(m, friend_number)) { + return -4; + } - if (send_group_invite_packet(c->messenger, friendnumber, packet, length) == -1) { + // fprintf(stderr, "handle_gc_invite_accepted_packet2\n"); + + uint8_t chat_id[CHAT_ID_SIZE]; + uint8_t invite_chat_pk[ENC_PUBLIC_KEY]; + + memcpy(chat_id, data, CHAT_ID_SIZE); + memcpy(invite_chat_pk, data + CHAT_ID_SIZE, ENC_PUBLIC_KEY); + + GC_Chat *chat = gc_get_group_by_public_key(c, chat_id); + + if (!chat || chat->connection_state <= CS_DISCONNECTED) { return -2; } + if (!friend_was_invited(chat, friend_number)) { + return -2; + } + + int peer_number = peer_add(m, chat->group_number, nullptr, invite_chat_pk); + + if (peer_number < 0) { + return -3; + } + + GC_Connection *gconn = gcc_get_connection(chat, peer_number); + + Node_format tcp_relays[GCC_MAX_TCP_SHARED_RELAYS]; + unsigned int i, num = tcp_copy_connected_relays(chat->tcp_conn, tcp_relays, GCC_MAX_TCP_SHARED_RELAYS); + + bool copy_ip_port_result = copy_ip_port_to_gconn(m, friend_number, gconn); + + if (num <= 0 && !copy_ip_port_result) { + return -1; + } + + uint32_t len = GC_JOIN_DATA_LENGTH; + uint8_t send_data[MAX_GC_PACKET_SIZE]; + memcpy(send_data, chat_id, CHAT_ID_SIZE); + memcpy(send_data + CHAT_ID_SIZE, chat->self_public_key, ENC_PUBLIC_KEY); + + if (num > 0) { + for (i = 0; i < num; ++i) { + add_tcp_relay_connection(chat->tcp_conn, gconn->tcp_connection_num, tcp_relays[i].ip_port, + tcp_relays[i].public_key); + save_tcp_relay(gconn, &tcp_relays[i]); + } + + int nodes_len = pack_nodes(send_data + len, sizeof(send_data) - len, tcp_relays, num); + + if (nodes_len <= 0 && !copy_ip_port_result) { + return -1; + } + + len += nodes_len; + } + + // fprintf(stderr, "send_gc_invite_confirmed_packet\n"); + + if (send_gc_invite_confirmed_packet(m, chat, friend_number, send_data, len)) { + return -4; + } + return 0; } /* Joins a group using the invite data received in a friend's group invite. * - * Return groupnumber on success. + * Return group_number on success. * Return -1 if the invite data is malformed. * Return -2 if the group object fails to initialize. - * Return -3 if there is an error setting the password. + * Return -3 if nick is too long. + * Return -4 if nick is empty or nick length is zero. + * Return -5 if there is an error setting the password. + * Return -6 if friend doesn't exist. + * Return -7 if sending packet failed. */ -int gc_accept_invite(GC_Session *c, const uint8_t *data, uint16_t length, const uint8_t *passwd, uint16_t passwd_len) +int gc_accept_invite(GC_Session *c, int32_t friend_number, const uint8_t *data, uint16_t length, const uint8_t *nick, + size_t nick_length, const uint8_t *passwd, uint16_t passwd_len) { - uint8_t chat_id[CHAT_ID_SIZE]; - memcpy(chat_id, data, CHAT_ID_SIZE); + if (length < CHAT_ID_SIZE + ENC_PUBLIC_KEY) { + return -1; + } - GC_Announce_Node node; + if (nick_length > MAX_GC_NICK_SIZE) { + return -3; + } - if (unpack_gca_nodes(&node, 1, nullptr, data + CHAT_ID_SIZE, length - CHAT_ID_SIZE, 0) != 1) { - return -1; + if (nick == nullptr || nick_length == 0) { + return -4; } - int err = -2; - int groupnumber = create_new_group(c, false); + if (!friend_is_valid(c->messenger, friend_number)) { + return -6; + } + + uint8_t chat_id[CHAT_ID_SIZE]; + uint8_t invite_chat_pk[ENC_PUBLIC_KEY]; + + memcpy(chat_id, data, CHAT_ID_SIZE); + memcpy(invite_chat_pk, data + CHAT_ID_SIZE, ENC_PUBLIC_KEY); + + int group_number = create_new_group(c, nick, nick_length, false); - if (groupnumber == -1) { - return err; + if (group_number == -1) { + return -2; } - GC_Chat *chat = gc_get_group(c, groupnumber); + GC_Chat *chat = gc_get_group(c, group_number); if (chat == nullptr) { - goto ON_ERROR; + group_delete(c, chat); + return -2; } expand_chat_id(chat->chat_public_key, chat_id); chat->chat_id_hash = get_chat_id_hash(get_chat_id(chat->chat_public_key)); chat->join_type = HJ_PRIVATE; + chat->shared_state.privacy_state = GI_PRIVATE; chat->last_join_attempt = mono_time_get(c->messenger->mono_time); if (passwd != nullptr && passwd_len > 0) { - err = -3; - if (set_gc_password_local(chat, passwd, passwd_len) == -1) { - goto ON_ERROR; + group_delete(c, chat); + return -5; } } - memcpy(&chat->addr_list[0].ip_port, &node.ip_port, sizeof(IP_Port)); - memcpy(&chat->addr_list[0].public_key, node.public_key, ENC_PUBLIC_KEY); - chat->num_addrs = 1; + int peer_id = peer_add(c->messenger, group_number, nullptr, invite_chat_pk); + + if (peer_id < 0) { + return -2; + } - send_gc_handshake_request(c->messenger, groupnumber, node.ip_port, node.public_key, HS_INVITE_REQUEST, - chat->join_type); - return groupnumber; + if (send_gc_invite_accepted_packet(c->messenger, chat, friend_number)) { + return -7; + } -ON_ERROR: - group_delete(c, chat); - return err; + return group_number; } GC_Session *new_dht_groupchats(Messenger *m) @@ -5711,16 +6316,33 @@ GC_Session *new_dht_groupchats(Messenger *m) } c->messenger = m; - c->announce = m->group_announce; + c->announces_list = m->group_announce; networking_registerhandler(m->net, NET_PACKET_GC_LOSSLESS, &handle_gc_udp_packet, m); networking_registerhandler(m->net, NET_PACKET_GC_LOSSY, &handle_gc_udp_packet, m); networking_registerhandler(m->net, NET_PACKET_GC_HANDSHAKE, &handle_gc_udp_packet, m); - group_callback_update_addresses(c->announce, handle_update_gc_addresses, c); return c; } +static void group_cleanup(GC_Session *c, GC_Chat *chat) +{ + m_remove_group(c->messenger, chat); + mod_list_cleanup(chat); + sanctions_list_cleanup(chat); + + if (chat->tcp_conn) { + kill_tcp_connections(chat->tcp_conn); + } + + gcc_cleanup(chat); + + if (chat->group) { + free(chat->group); + chat->group = nullptr; + } +} + /* Deletes chat from group chat array and cleans up. * * Return 0 on success. @@ -5728,21 +6350,13 @@ GC_Session *new_dht_groupchats(Messenger *m) */ static int group_delete(GC_Session *c, GC_Chat *chat) { - if (c == nullptr) { + if (!c || !chat) { return -1; } - mod_list_cleanup(chat); - sanctions_list_cleanup(chat); - kill_tcp_connections(chat->tcp_conn); - gca_cleanup(c->announce, get_chat_id(chat->chat_public_key)); - gcc_cleanup(chat); - - if (chat->group) { - free(chat->group); - } + group_cleanup(c, chat); - memset(&(c->chats[chat->groupnumber]), 0, sizeof(GC_Chat)); + memset(&(c->chats[chat->group_number]), 0, sizeof(GC_Chat)); uint32_t i; @@ -5772,11 +6386,10 @@ static int group_delete(GC_Session *c, GC_Chat *chat) */ int gc_group_exit(GC_Session *c, GC_Chat *chat, const uint8_t *message, uint16_t length) { - int ret = send_gc_self_exit(chat, message, length); + bool is_chat_active = chat->connection_state != CS_MANUALLY_DISCONNECTED; + int ret = is_chat_active ? send_gc_self_exit(chat, message, length) : 0; - if (group_delete(c, chat) == -1) { - ret = -3; - } + group_delete(c, chat); return ret; } @@ -5786,34 +6399,36 @@ void kill_dht_groupchats(GC_Session *c) uint32_t i; for (i = 0; i < c->num_chats; ++i) { - if (c->chats[i].connection_state != CS_NONE) { - GC_Chat *chat = &c->chats[i]; + GC_Chat *chat = &c->chats[i]; + + if (chat->connection_state != CS_NONE && chat->connection_state != CS_MANUALLY_DISCONNECTED) { send_gc_self_exit(chat, nullptr, 0); - kill_tcp_connections(chat->tcp_conn); + group_cleanup(c, chat); } } networking_registerhandler(c->messenger->net, NET_PACKET_GC_LOSSY, nullptr, nullptr); networking_registerhandler(c->messenger->net, NET_PACKET_GC_LOSSLESS, nullptr, nullptr); networking_registerhandler(c->messenger->net, NET_PACKET_GC_HANDSHAKE, nullptr, nullptr); - group_callback_update_addresses(c->announce, nullptr, nullptr); + + free(c->chats); free(c); } -/* Return 1 if groupnumber is a valid group chat index +/* Return 1 if group_number is a valid group chat index * Return 0 otherwise */ -static int groupnumber_valid(const GC_Session *c, int groupnumber) +static bool group_number_valid(const GC_Session *c, int group_number) { - if (groupnumber < 0 || groupnumber >= c->num_chats) { - return 0; + if (group_number < 0 || group_number >= c->num_chats) { + return false; } if (c->chats == nullptr) { - return 0; + return false; } - return c->chats[groupnumber].connection_state != CS_NONE; + return c->chats[group_number].connection_state != CS_NONE; } /* Count number of active groups. @@ -5833,22 +6448,47 @@ uint32_t gc_count_groups(const GC_Session *c) return count; } -/* Return groupnumber's GC_Chat pointer on success +void gc_copy_groups_numbers(const GC_Session *c, uint32_t *list) +{ + uint32_t i, count = 0; + + for (i = 0; i < c->num_chats; ++i) { + if (c->chats[i].connection_state > CS_NONE && c->chats[i].connection_state < CS_INVALID) { + list[count] = i; + ++count; + } + } +} + +/* Return group_number's GC_Chat pointer on success * Return NULL on failure */ -GC_Chat *gc_get_group(const GC_Session *c, int groupnumber) +GC_Chat *gc_get_group(const GC_Session *c, int group_number) { - if (!groupnumber_valid(c, groupnumber)) { + if (!group_number_valid(c, group_number)) { return nullptr; } - return &c->chats[groupnumber]; + return &c->chats[group_number]; +} + +GC_Chat *gc_get_group_by_public_key(const GC_Session *c, const uint8_t *public_key) +{ + int i; + + for (i = 0; i < c->num_chats; ++i) { + if (!memcmp(public_key, get_chat_id(c->chats[i].chat_public_key), CHAT_ID_SIZE)) { + return &c->chats[i]; + } + } + + return nullptr; } -/* Return peernumber of peer with nick if nick is taken. +/* Return peer_number of peer with nick if nick is taken. * Return -1 if nick is not taken. */ -static int get_nick_peernumber(const GC_Chat *chat, const uint8_t *nick, uint16_t length) +static int get_nick_peer_number(const GC_Chat *chat, const uint8_t *nick, uint16_t length) { if (length == 0) { return -1; @@ -5857,7 +6497,7 @@ static int get_nick_peernumber(const GC_Chat *chat, const uint8_t *nick, uint16_ uint32_t i; for (i = 0; i < chat->numpeers; ++i) { - if (chat->group[i].nick_len == length && memcmp(chat->group[i].nick, nick, length) == 0) { + if (chat->group[i].nick_length == length && memcmp(chat->group[i].nick, nick, length) == 0) { return i; } } @@ -5879,4 +6519,78 @@ static bool group_exists(const GC_Session *c, const uint8_t *chat_id) return false; } -#endif /* VANILLA_NACL */ +int add_peers_from_announces(const GC_Session *gc_session, GC_Chat *chat, GC_Announce *announces, + uint8_t gc_announces_count) +{ + if (!chat || !announces || !gc_session) { + return -1; + } + + int i, j, added_peers = 0; + + for (i = 0; i < gc_announces_count; ++i) { + GC_Announce *curr_announce = &announces[i]; + + if (!is_valid_announce(curr_announce)) { + // fprintf(stderr, "invalid ann\n"); + continue; + } + + bool ip_port_set = curr_announce->ip_port_is_set; + IP_Port *ip_port = ip_port_set ? &curr_announce->ip_port : nullptr; + int peer_number = peer_add(gc_session->messenger, chat->group_number, + ip_port, curr_announce->peer_public_key); + + if (peer_number < 0) { + continue; + } + + GC_Connection *gconn = gcc_get_connection(chat, peer_number); + + if (!gconn) { + continue; + } + + uint8_t tcp_relays_count = curr_announce->tcp_relays_count; + + for (j = 0; j < tcp_relays_count; ++j) { + int add_tcp_result = add_tcp_relay_connection(chat->tcp_conn, gconn->tcp_connection_num, + curr_announce->tcp_relays[j].ip_port, + curr_announce->tcp_relays[j].public_key); + + if (add_tcp_result < 0) { + continue; + } + + int save_tcp_result = save_tcp_relay(gconn, &curr_announce->tcp_relays[j]); + + if (save_tcp_result) { + continue; + } + + memcpy(gconn->oob_relay_pk, curr_announce->tcp_relays[j].public_key, ENC_PUBLIC_KEY); + } + + if (ip_port_set && !tcp_relays_count) { + // fprintf(stderr, "ip_port_set && !curr_announce->base_announce.tcp_relays_count\n"); + send_gc_handshake_packet(chat, peer_number, GH_REQUEST, HS_INVITE_REQUEST, chat->join_type); + gconn->send_message_id = 2; + } else { + // fprintf(stderr, "send oob %d\n", curr_announce->tcp_relays_count); + gconn->is_oob_handshake = true; + gconn->is_pending_handshake_response = false; + gconn->pending_handshake_type = HS_INVITE_REQUEST; + const uint64_t timeout = mono_time_get(chat->mono_time) + HANDSHAKE_SENDING_TIMEOUT; + gconn->last_received_ping_time = timeout; + gconn->pending_handshake = timeout; + } + + ++added_peers; + // char id_str[IDSTRING_LEN]; + // fprintf(stderr, "Added peers %s\n", id_to_string(curr_announce->peer_public_key, id_str, IDSTRING_LEN)); + } + + return added_peers; +} + +#endif diff --git a/toxcore/group_chats.h b/toxcore/group_chats.h index d8686b36e9..869fe39fe4 100644 --- a/toxcore/group_chats.h +++ b/toxcore/group_chats.h @@ -12,6 +12,7 @@ #include #include "TCP_connection.h" +#include "group_announce.h" #define TIME_STAMP_SIZE (sizeof(uint64_t)) #define HASH_ID_BYTES (sizeof(uint32_t)) @@ -22,14 +23,19 @@ #define MAX_GC_MESSAGE_SIZE 1372 #define MAX_GC_PART_MESSAGE_SIZE 128 #define MAX_GC_PEER_ADDRS 30 -#define MAX_GC_PASSWD_SIZE 32 +#define MAX_GC_PASSWORD_SIZE 32 #define MAX_GC_MODERATORS 128 +#define MAX_GC_SAVED_INVITES 50 #define GC_MOD_LIST_ENTRY_SIZE SIG_PUBLIC_KEY #define GC_MODERATION_HASH_SIZE CRYPTO_SHA256_SIZE #define GC_PING_INTERVAL 12 #define GC_CONFIRMED_PEER_TIMEOUT (GC_PING_INTERVAL * 4 + 10) -#define GC_UNCONFRIMED_PEER_TIMEOUT GC_PING_INTERVAL +#define GC_UNCONFIRMED_PEER_TIMEOUT (GC_PING_INTERVAL * 2) +#define MAX_GC_CONFIRMED_PEERS 20 + +#define GC_JOIN_DATA_LENGTH (ENC_PUBLIC_KEY + CHAT_ID_SIZE) + typedef enum Group_Privacy_State { GI_PUBLIC, @@ -39,19 +45,25 @@ typedef enum Group_Privacy_State { typedef enum Group_Moderation_Event { MV_KICK, - MV_BAN, MV_OBSERVER, MV_USER, MV_MODERATOR, MV_INVALID, } Group_Moderation_Event; +typedef enum Group_Invite_Message_Type { + GROUP_INVITE, + GROUP_INVITE_ACCEPTED, + GROUP_INVITE_CONFIRMATION, +} Group_Invite_Message_Type; + /* Group roles are hierarchical where each role has a set of privileges plus * all the privileges of the roles below it. * - * - FOUNDER is all-powerful. Cannot be demoted or banned. - * - OP may issue bans, promotions and demotions to all roles below founder. - * - USER may talk, stream A/V, and change the group topic. + * - FOUNDER is all-powerful. Cannot be demoted or kicked. + * - MODERATOR may promote or demote peers below them to any role below them. + * May also kick peers below them and set the topic. + * - USER may interact normally with the group. * - OBSERVER cannot interact with the group but may observe. */ typedef enum Group_Role { @@ -62,22 +74,28 @@ typedef enum Group_Role { GR_INVALID, } Group_Role; -typedef enum Group_Status { +typedef enum Group_Peer_Status { GS_NONE, GS_AWAY, GS_BUSY, GS_INVALID, -} Group_Status; +} Group_Peer_Status; typedef enum Group_Connection_State { CS_NONE, CS_FAILED, + CS_MANUALLY_DISCONNECTED, CS_DISCONNECTED, CS_CONNECTING, CS_CONNECTED, CS_INVALID, } Group_Connection_State; +typedef enum Saved_Group_Connection_State { + SGCS_DISCONNECTED, + SGCS_CONNECTED, +} Saved_Group_Connection_State; + typedef enum Group_Join_Rejected { GJ_NICK_TAKEN, GJ_GROUP_FULL, @@ -91,10 +109,9 @@ typedef enum Group_Broadcast_Type { GM_NICK, GM_PLAIN_MESSAGE, GM_ACTION_MESSAGE, - GM_PRVT_MESSAGE, + GM_PRIVATE_MESSAGE, GM_PEER_EXIT, - GM_REMOVE_PEER, - GM_REMOVE_BAN, + GM_KICK_PEER, GM_SET_MOD, GM_SET_OBSERVER, } Group_Broadcast_Type; @@ -107,7 +124,8 @@ typedef enum Group_Packet_Type { GP_TCP_RELAYS = 4, /* lossless packets */ - GP_CUSTOM_PACKET = 242, + GP_CUSTOM_PACKET = 241, + GP_PEER_ANNOUNCE = 242, GP_BROADCAST = 243, GP_PEER_INFO_REQUEST = 244, GP_PEER_INFO_RESPONSE = 245, @@ -154,10 +172,16 @@ typedef struct GC_PeerAddress { IP_Port ip_port; } GC_PeerAddress; +typedef struct GC_SavedPeerInfo { + uint8_t public_key[EXT_PUBLIC_KEY]; + Node_format tcp_relay; + IP_Port ip_port; +} GC_SavedPeerInfo; + typedef struct GC_GroupPeer { uint8_t role; uint8_t nick[MAX_GC_NICK_SIZE]; - uint16_t nick_len; + uint16_t nick_length; uint8_t status; /* Below variables are not sent to other peers */ @@ -171,8 +195,8 @@ typedef struct GC_SharedState { uint16_t group_name_len; uint8_t group_name[MAX_GC_GROUP_NAME_SIZE]; uint8_t privacy_state; /* GI_PUBLIC (uses DHT) or GI_PRIVATE (invite only) */ - uint16_t passwd_len; - uint8_t passwd[MAX_GC_PASSWD_SIZE]; + uint16_t password_length; + uint8_t password[MAX_GC_PASSWORD_SIZE]; uint8_t mod_list_hash[GC_MODERATION_HASH_SIZE]; uint32_t version; } GC_SharedState; @@ -186,8 +210,55 @@ typedef struct GC_TopicInfo { typedef struct GC_Connection GC_Connection; + +#define GROUP_SAVE_MAX_PEERS MAX_GC_PEER_ADDRS + +struct Saved_Group { + /* Group shared state */ + uint8_t founder_public_key[EXT_PUBLIC_KEY]; + uint16_t maxpeers; + uint16_t group_name_length; + uint8_t group_name[MAX_GC_GROUP_NAME_SIZE]; + uint8_t privacy_state; + uint16_t password_length; + uint8_t password[MAX_GC_PASSWORD_SIZE]; + uint8_t mod_list_hash[GC_MODERATION_HASH_SIZE]; + uint32_t shared_state_version; + uint8_t shared_state_signature[SIGNATURE_SIZE]; + + /* Topic info */ + uint16_t topic_length; + uint8_t topic[MAX_GC_TOPIC_SIZE]; + uint8_t topic_public_sig_key[SIG_PUBLIC_KEY]; + uint32_t topic_version; + uint8_t topic_signature[SIGNATURE_SIZE]; + + /* Other group info */ + uint8_t chat_public_key[EXT_PUBLIC_KEY]; + uint8_t chat_secret_key[EXT_SECRET_KEY]; + uint16_t num_addrs; + GC_SavedPeerInfo addrs[GROUP_SAVE_MAX_PEERS]; + uint16_t num_mods; + uint8_t mod_list[GC_MOD_LIST_ENTRY_SIZE * MAX_GC_MODERATORS]; + uint8_t group_connection_state; + + /* self info */ + uint8_t self_public_key[EXT_PUBLIC_KEY]; + uint8_t self_secret_key[EXT_SECRET_KEY]; + uint8_t self_nick[MAX_GC_NICK_SIZE]; + uint16_t self_nick_length; + uint8_t self_role; + uint8_t self_status; +}; + +typedef struct Saved_Group Saved_Group; + typedef struct GC_Chat { const Mono_Time *mono_time; + uint8_t confirmed_peers[MAX_GC_CONFIRMED_PEERS][ENC_PUBLIC_KEY]; + uint8_t confirmed_peers_index; + Node_format announced_node; + Networking_Core *net; TCP_Connections *tcp_conn; @@ -202,7 +273,7 @@ typedef struct GC_Chat { uint8_t topic_sig[SIGNATURE_SIZE]; /* Signed by a moderator or the founder */ uint32_t numpeers; - int groupnumber; + int group_number; uint8_t chat_public_key[EXT_PUBLIC_KEY]; /* the chat_id is the sig portion */ uint8_t chat_secret_key[EXT_SECRET_KEY]; /* only used by the founder */ @@ -214,10 +285,7 @@ typedef struct GC_Chat { uint8_t connection_state; uint64_t last_join_attempt; - uint8_t get_nodes_attempts; - uint64_t last_get_nodes_attempt; uint64_t last_sent_ping_time; - uint64_t announce_search_timer; uint8_t join_type; /* How we joined the group (invite or DHT) */ /* keeps track of frequency of new inbound connections */ @@ -225,10 +293,14 @@ typedef struct GC_Chat { uint64_t connection_cooldown_timer; bool block_handshakes; - /* Holder for IP/keys received from announcement requests and loaded from saved groups */ - GC_PeerAddress addr_list[MAX_GC_PEER_ADDRS]; - uint16_t num_addrs; - uint16_t addrs_idx; + int32_t saved_invites[MAX_GC_SAVED_INVITES]; + uint8_t saved_invites_index; + + uint8_t m_group_public_key[CRYPTO_PUBLIC_KEY_SIZE]; /* Identifier for group's messenger friend connection */ + bool should_update_self_announces; + bool should_start_sending_handshakes; + + Saved_Group *save; } GC_Chat; #ifndef MESSENGER_DEFINED @@ -238,7 +310,7 @@ typedef struct Messenger Messenger; typedef void gc_message_cb(Messenger *m, uint32_t group_number, uint32_t peer_id, unsigned int type, const uint8_t *data, size_t length, void *user_data); -typedef void gc_private_message_cb(Messenger *m, uint32_t group_number, uint32_t peer_id, +typedef void gc_private_message_cb(Messenger *m, uint32_t group_number, uint32_t peer_id, unsigned int type, const uint8_t *data, size_t length, void *user_data); typedef void gc_custom_packet_cb(Messenger *m, uint32_t group_number, uint32_t peer_id, const uint8_t *data, size_t length, void *user_data); @@ -260,9 +332,9 @@ typedef void gc_self_join_cb(Messenger *m, uint32_t group_number, void *user_dat typedef void gc_rejected_cb(Messenger *m, uint32_t group_number, unsigned int type, void *user_data); typedef struct GC_Session { - Messenger *messenger; - GC_Chat *chats; - struct GC_Announce *announce; + Messenger *messenger; + GC_Chat *chats; + struct GC_Announces_List *announces_list; uint32_t num_chats; @@ -296,44 +368,9 @@ typedef struct GC_Session { void *rejected_userdata; } GC_Session; -#define GROUP_SAVE_MAX_PEERS MAX_GC_PEER_ADDRS +void pack_group_info(const GC_Chat *chat, Saved_Group *temp, bool can_use_cached_value); -struct Saved_Group { - /* Group shared state */ - uint8_t founder_public_key[EXT_PUBLIC_KEY]; - uint16_t maxpeers; - uint16_t group_name_len; - uint8_t group_name[MAX_GC_GROUP_NAME_SIZE]; - uint8_t privacy_state; - uint16_t passwd_len; - uint8_t passwd[MAX_GC_PASSWD_SIZE]; - uint8_t mod_list_hash[GC_MODERATION_HASH_SIZE]; - uint32_t sstate_version; - uint8_t sstate_signature[SIGNATURE_SIZE]; - - /* Topic info */ - uint16_t topic_len; - uint8_t topic[MAX_GC_TOPIC_SIZE]; - uint8_t topic_public_sig_key[SIG_PUBLIC_KEY]; - uint32_t topic_version; - uint8_t topic_signature[SIGNATURE_SIZE]; - - /* Other group info */ - uint8_t chat_public_key[EXT_PUBLIC_KEY]; - uint8_t chat_secret_key[EXT_SECRET_KEY]; - uint16_t num_addrs; - GC_PeerAddress addrs[GROUP_SAVE_MAX_PEERS]; - uint16_t num_mods; - uint8_t mod_list[GC_MOD_LIST_ENTRY_SIZE * MAX_GC_MODERATORS]; - - /* self info */ - uint8_t self_public_key[EXT_PUBLIC_KEY]; - uint8_t self_secret_key[EXT_SECRET_KEY]; - uint8_t self_nick[MAX_GC_NICK_SIZE]; - uint16_t self_nick_len; - uint8_t self_role; - uint8_t self_status; -}; +bool is_public_chat(const GC_Chat *chat); /* Sends a plain message or an action, depending on type. * @@ -355,7 +392,7 @@ int gc_send_message(GC_Chat *chat, const uint8_t *message, uint16_t length, uint * Returns -4 if the sender has the observer role. * Returns -5 if the packet fails to send. */ -int gc_send_private_message(GC_Chat *chat, uint32_t peer_id, const uint8_t *message, uint16_t length); +int gc_send_private_message(GC_Chat *chat, uint32_t peer_id, uint8_t type, const uint8_t *message, uint16_t length); /* Sends a custom packet to the group. If lossless is true, the packet will be lossless. * @@ -370,6 +407,7 @@ int gc_send_custom_packet(GC_Chat *chat, bool lossless, const uint8_t *data, uin * * Returns 0 on success. * Returns -1 if the peer_id is invalid. + * Returns -2 if the caller attempted to ignore himself. */ int gc_toggle_ignore(GC_Chat *chat, uint32_t peer_id, bool ignore); @@ -390,7 +428,7 @@ void gc_get_topic(const GC_Chat *chat, uint8_t *topic); uint16_t gc_get_topic_size(const GC_Chat *chat); /* Copies group name to groupname. */ -void gc_get_group_name(const GC_Chat *chat, uint8_t *groupname); +void gc_get_group_name(const GC_Chat *chat, uint8_t *group_name); /* Returns group name length */ uint16_t gc_get_group_name_size(const GC_Chat *chat); @@ -411,13 +449,13 @@ uint32_t gc_get_max_peers(const GC_Chat *chat); * Sets your own nick. * * Returns 0 on success. - * Returns -1 if groupnumber is invalid. + * Returns -1 if group_number is invalid. * Returns -2 if the length is too long. * Returns -3 if the length is zero or nick is a NULL pointer. * Returns -4 if the nick is already taken. * Returns -5 if the packet fails to send. */ -int gc_set_self_nick(Messenger *m, int groupnumber, const uint8_t *nick, uint16_t length); +int gc_set_self_nick(Messenger *m, int group_number, const uint8_t *nick, uint16_t length); /* Copies your own nick to nick */ void gc_get_self_nick(const GC_Chat *chat, uint8_t *nick); @@ -456,14 +494,16 @@ int gc_get_peer_nick_size(const GC_Chat *chat, uint32_t peer_id); */ int gc_get_peer_public_key(const GC_Chat *chat, uint32_t peer_id, uint8_t *public_key); +int gc_get_peer_public_key_by_peer_id(const GC_Chat *chat, uint32_t peer_id, uint8_t *public_key); + /* Sets the caller's status to status * * Returns 0 on success. - * Returns -1 if the groupnumber is invalid. + * Returns -1 if the group_number is invalid. * Returns -2 if the status type is invalid. * Returns -3 if the packet failed to send. */ -int gc_set_self_status(Messenger *m, int groupnumber, uint8_t status); +int gc_set_self_status(Messenger *m, int group_number, uint8_t status); /* Returns peer_id's status. * Returns (uint8_t) -1 on failure. @@ -478,13 +518,14 @@ uint8_t gc_get_role(const GC_Chat *chat, uint32_t peer_id); /* Sets the role of peer_id. role must be one of: GR_MODERATOR, GR_USER, GR_OBSERVER * * Returns 0 on success. - * Returns -1 if the groupnumber is invalid. + * Returns -1 if the group_number is invalid. * Returns -2 if the peer_id is invalid. * Returns -3 if caller does not have sufficient permissions for the action. * Returns -4 if the role assignment is invalid. * Returns -5 if the role failed to be set. + * Returns -6 if the caller attempted to kick himself. */ -int gc_set_peer_role(Messenger *m, int groupnumber, uint32_t peer_id, uint8_t role); +int gc_set_peer_role(Messenger *m, int group_number, uint32_t peer_id, uint8_t role); /* Sets the group password and distributes the new shared state to the group. * @@ -495,20 +536,20 @@ int gc_set_peer_role(Messenger *m, int groupnumber, uint32_t peer_id, uint8_t ro * Returns -2 if the password is too long. * Returns -3 if the packet failed to send. */ -int gc_founder_set_password(GC_Chat *chat, const uint8_t *passwd, uint16_t passwd_len); +int gc_founder_set_password(GC_Chat *chat, const uint8_t *password, uint16_t password_length); /* Sets the group privacy state and distributes the new shared state to the group. * * This function requires that the shared state be re-signed and will only work for the group founder. * * Returns 0 on success. - * Returns -1 if groupnumber is invalid. + * Returns -1 if group_number is invalid. * Returns -2 if the privacy state is an invalid type. * Returns -3 if the caller does not have sufficient permissions for this action. * Returns -4 if the privacy state fails to set. * Returns -5 if the packet fails to send. */ -int gc_founder_set_privacy_state(Messenger *m, int groupnumber, uint8_t new_privacy_state); +int gc_founder_set_privacy_state(Messenger *m, int group_number, uint8_t new_privacy_state); /* Sets the peer limit to maxpeers and distributes the new shared state to the group. * @@ -519,28 +560,19 @@ int gc_founder_set_privacy_state(Messenger *m, int groupnumber, uint8_t new_priv * Returns -2 if the peer limit could not be set. * Returns -3 if the packet failed to send. */ -int gc_founder_set_max_peers(GC_Chat *chat, int groupnumber, uint32_t maxpeers); +int gc_founder_set_max_peers(GC_Chat *chat, uint32_t max_peers); /* Instructs all peers to remove peer_id from their peerlist. - * If set_ban is true peer will be added to the ban list. * * Returns 0 on success. - * Returns -1 if the groupnumber is invalid. + * Returns -1 if the group_number is invalid. * Returns -2 if the peer_id is invalid. * Returns -3 if the caller does not have sufficient permissions for this action. * Returns -4 if the action failed. * Returns -5 if the packet failed to send. + * Returns -6 if the caller attempted to kick himself. */ -int gc_remove_peer(Messenger *m, int groupnumber, uint32_t peer_id, bool set_ban); - -/* Instructs all peers to remove ban_id from their ban list. - * - * Returns 0 on success. - * Returns -1 if the caller does not have sufficient permissions for this action. - * Returns -2 if the entry could not be removed. - * Returns -3 if the packet failed to send. - */ -int gc_remove_ban(GC_Chat *chat, uint32_t ban_id); +int gc_kick_peer(Messenger *m, int group_number, uint32_t peer_id); /* Copies the chat_id to dest */ void gc_get_chat_id(const GC_Chat *chat, uint8_t *dest); @@ -575,45 +607,56 @@ void kill_dht_groupchats(GC_Session *c); /* Loads a previously saved group and attempts to join it. * - * Returns groupnumber on success. + * Returns group_number on success. * Returns -1 on failure. */ -int gc_group_load(GC_Session *c, struct Saved_Group *save); +int gc_group_load(GC_Session *c, Saved_Group *save, int group_number); /* Creates a new group. * - * Return groupnumber on success. - * Return -1 if the group name is too long. - * Return -2 if the group name is empty. + * Return -1 if the nick or group name is too long. + * Return -2 if the nick or group name is empty. * Return -3 if the privacy state is an invalid type. * Return -4 if the the group object fails to initialize. * Return -5 if the group state fails to initialize. - * Return -6 if the group fails to announce to the DHT. - */ -int gc_group_add(GC_Session *c, uint8_t privacy_state, const uint8_t *group_name, uint16_t length); + * Return -6 if the announce was unsuccessful. +*/ +int gc_group_add(GC_Session *c, uint8_t privacy_state, const uint8_t *group_name, uint16_t group_name_length, + const uint8_t *nick, size_t nick_length); /* Sends an invite request to a public group using the chat_id. * - * If the group is not password protected passwd should be set to NULL and passwd_len should be 0. + * If the group is not password protected password should be set to NULL and password_length should be 0. * - * Return groupnumber on success. - * Reutrn -1 if the group object fails to initialize. - * Return -2 if chat_id is NULL or a group with chat_id already exists in the chats arr - * Return -3 if there is an error setting the group password. + * Return group_number on success. + * Return -1 if the group object fails to initialize. + * Return -2 if chat_id is NULL or a group with chat_id already exists in the chats array. + * Return -3 if nick is too long. + * Return -4 if nick is empty or nick length is zero. + * Return -5 if there is an error setting the group password. + * Return -6 if there is an error adding a friend. */ -int gc_group_join(GC_Session *c, const uint8_t *chat_id, const uint8_t *passwd, uint16_t passwd_len); +int gc_group_join(GC_Session *c, const uint8_t *chat_id, const uint8_t *nick, size_t nick_length, const uint8_t *passwd, + uint16_t passwd_len); + +bool gc_disconnect_from_group(GC_Session *c, GC_Chat *chat); /* Resets chat saving all self state and attempts to reconnect to group */ -void gc_rejoin_group(GC_Session *c, GC_Chat *chat); +bool gc_rejoin_group(GC_Session *c, GC_Chat *chat); /* Joins a group using the invite data received in a friend's group invite. * - * Return groupnumber on success. + * Return group_number on success. * Return -1 if the invite data is malformed. * Return -2 if the group object fails to initialize. - * Return -3 if there is an error setting the password. + * Return -3 if nick is too long. + * Return -4 if nick is empty or nick length is zero. + * Return -5 if there is an error setting the password. + * Return -6 if friend doesn't exist. + * Return -7 if sending packet failed. */ -int gc_accept_invite(GC_Session *c, const uint8_t *data, uint16_t length, const uint8_t *passwd, uint16_t passwd_len); +int gc_accept_invite(GC_Session *c, int32_t friend_number, const uint8_t *data, uint16_t length, const uint8_t *nick, + size_t nick_length, const uint8_t *passwd, uint16_t passwd_len); typedef int gc_send_group_invite_packet_cb(const Messenger *m, uint32_t friendnumber, const uint8_t *packet, size_t length); @@ -643,38 +686,37 @@ int gc_group_exit(GC_Session *c, GC_Chat *chat, const uint8_t *message, uint16_t */ uint32_t gc_count_groups(const GC_Session *c); -/* Returns true if peernumber exists */ -bool peernumber_valid(const GC_Chat *chat, int peernumber); +void gc_copy_groups_numbers(const GC_Session *c, uint32_t *list); + +/* Returns true if peer_number exists */ +bool peer_number_valid(const GC_Chat *chat, int peer_number); -/* Return groupnumber's GC_Chat pointer on success +/* Return group_number's GC_Chat pointer on success * Return NULL on failure */ -GC_Chat *gc_get_group(const GC_Session *c, int groupnumber); +GC_Chat *gc_get_group(const GC_Session *c, int group_number); -/* Deletets peernumber from group. +/* + * Deletes peer_number from group. `no_callback` should be set to true if the `peer_exit` callback should not be triggered. * * Return 0 on success. * Return -1 on failure. */ -int gc_peer_delete(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, uint16_t length); - -/* Packs mod_list into data. - * data must have room for `num_mods * SIG_PUBLIC_KEY` bytes. - */ -void pack_gc_mod_list(const GC_Chat *chat, uint8_t *data); +int gc_peer_delete(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint16_t length, + bool no_callback); /* Copies up to max_addrs peer addresses from chat into addrs. * * Returns number of addresses copied. */ -uint16_t gc_copy_peer_addrs(const GC_Chat *chat, GC_PeerAddress *addrs, size_t max_addrs); +uint16_t gc_copy_peer_addrs(const GC_Chat *chat, GC_SavedPeerInfo *addrs, size_t max_addrs); /* If read_id is non-zero sends a read-receipt for ack_id's packet. * If request_id is non-zero sends a request for the respective id's packet. */ int gc_send_message_ack(const GC_Chat *chat, GC_Connection *gconn, uint64_t read_id, uint64_t request_id); -int handle_gc_lossless_helper(Messenger *m, int groupnumber, uint32_t peernumber, const uint8_t *data, +int handle_gc_lossless_helper(Messenger *m, int group_number, uint32_t peer_number, const uint8_t *data, uint16_t length, uint64_t message_id, uint8_t packet_type); /* Sends the sanctions list to all peers in group. @@ -684,4 +726,18 @@ int handle_gc_lossless_helper(Messenger *m, int groupnumber, uint32_t peernumber */ int broadcast_gc_sanctions_list(GC_Chat *chat); + +int handle_gc_invite_accepted_packet(GC_Session *c, int friend_number, const uint8_t *data, + uint32_t length); + +bool check_group_invite(GC_Session *c, const uint8_t *data, uint32_t length); + +int handle_gc_invite_confirmed_packet(GC_Session *c, int friend_number, const uint8_t *data, + uint32_t length); + +GC_Chat *gc_get_group_by_public_key(const GC_Session *c, const uint8_t *public_key); + +int add_peers_from_announces(const GC_Session *gc_session, GC_Chat *chat, GC_Announce *announces, + uint8_t gc_announces_count); + #endif /* GROUP_CHATS_H */ diff --git a/toxcore/group_connection.c b/toxcore/group_connection.c index e33496fb3e..df067bdb42 100644 --- a/toxcore/group_connection.c +++ b/toxcore/group_connection.c @@ -13,6 +13,7 @@ #include #include +#include #include "DHT.h" #include "mono_time.h" @@ -24,22 +25,22 @@ #ifndef VANILLA_NACL -/* Returns group connection object for peernumber. - * Returns NULL if peernumber is invalid. +/* Returns group connection object for peer_number. + * Returns NULL if peer_number is invalid. */ -GC_Connection *gcc_get_connection(const GC_Chat *chat, int peernumber) +GC_Connection *gcc_get_connection(const GC_Chat *chat, int peer_number) { - if (!peernumber_valid(chat, peernumber)) { + if (!peer_number_valid(chat, peer_number)) { return nullptr; } - return &chat->gcc[peernumber]; + return &chat->gcc[peer_number]; } /* Returns true if ary entry does not contain an active packet. */ -static bool ary_entry_is_empty(struct GC_Message_Ary_Entry *ary_entry) +static bool array_entry_is_empty(struct GC_Message_Array_Entry *array_entry) { - return ary_entry->time_added == 0; + return array_entry->time_added == 0; } /* Clears an ary entry. @@ -47,17 +48,17 @@ static bool ary_entry_is_empty(struct GC_Message_Ary_Entry *ary_entry) * Return 0 on success. * Return -1 on failure. */ -static void clear_ary_entry(struct GC_Message_Ary_Entry *ary_entry) +static void clear_array_entry(struct GC_Message_Array_Entry *array_entry) { - if (ary_entry->data) { - free(ary_entry->data); + if (array_entry->data) { + free(array_entry->data); } - memset(ary_entry, 0, sizeof(struct GC_Message_Ary_Entry)); + memset(array_entry, 0, sizeof(struct GC_Message_Array_Entry)); } /* Returns ary index for message_id */ -uint16_t get_ary_index(uint64_t message_id) +uint16_t get_array_index(uint64_t message_id) { return message_id % GCC_BUFFER_SIZE; } @@ -67,49 +68,50 @@ uint16_t get_ary_index(uint64_t message_id) * Return 0 on success. * Return -1 on failure. */ -static int create_ary_entry(const Mono_Time *mono_time, struct GC_Message_Ary_Entry *ary_entry, const uint8_t *data, - uint32_t length, uint8_t packet_type, uint64_t message_id) +static int create_array_entry(const Mono_Time *mono_time, struct GC_Message_Array_Entry *array_entry, + const uint8_t *data, uint32_t length, + uint8_t packet_type, uint64_t message_id) { if (length) { - ary_entry->data = (uint8_t *)malloc(sizeof(uint8_t) * length); + array_entry->data = (uint8_t *)malloc(sizeof(uint8_t) * length); - if (ary_entry->data == nullptr) { + if (array_entry->data == nullptr) { return -1; } - memcpy(ary_entry->data, data, length); + memcpy(array_entry->data, data, length); } - ary_entry->data_length = length; - ary_entry->packet_type = packet_type; - ary_entry->message_id = message_id; - ary_entry->time_added = mono_time_get(mono_time); - ary_entry->last_send_try = mono_time_get(mono_time); + array_entry->data_length = length; + array_entry->packet_type = packet_type; + array_entry->message_id = message_id; + array_entry->time_added = mono_time_get(mono_time); + array_entry->last_send_try = mono_time_get(mono_time); return 0; } -/* Adds data of length to gconn's send_ary. +/* Adds data of length to gconn's send_array. * * Returns 0 on success and increments gconn's send_message_id. * Returns -1 on failure. */ -int gcc_add_send_ary(const Mono_Time *mono_time, GC_Connection *gconn, const uint8_t *data, uint32_t length, - uint8_t packet_type) +int gcc_add_to_send_array(const Mono_Time *mono_time, GC_Connection *gconn, const uint8_t *data, uint32_t length, + uint8_t packet_type) { - /* check if send_ary is full */ - if ((gconn->send_message_id % GCC_BUFFER_SIZE) == (uint16_t)(gconn->send_ary_start - 1)) { + /* check if send_array is full */ + if ((gconn->send_message_id % GCC_BUFFER_SIZE) == (uint16_t)(gconn->send_array_start - 1)) { return -1; } - uint16_t idx = get_ary_index(gconn->send_message_id); - struct GC_Message_Ary_Entry *ary_entry = &gconn->send_ary[idx]; + uint16_t idx = get_array_index(gconn->send_message_id); + struct GC_Message_Array_Entry *array_entry = &gconn->send_array[idx]; - if (!ary_entry_is_empty(ary_entry)) { + if (!array_entry_is_empty(array_entry)) { return -1; } - if (create_ary_entry(mono_time, ary_entry, data, length, packet_type, gconn->send_message_id) == -1) { + if (create_array_entry(mono_time, array_entry, data, length, packet_type, gconn->send_message_id) == -1) { return -1; } @@ -118,32 +120,32 @@ int gcc_add_send_ary(const Mono_Time *mono_time, GC_Connection *gconn, const uin return 0; } -/* Removes send_ary item with message_id. +/* Removes send_array item with message_id. * * Returns 0 if success. * Returns -1 on failure. */ int gcc_handle_ack(GC_Connection *gconn, uint64_t message_id) { - uint16_t idx = get_ary_index(message_id); - struct GC_Message_Ary_Entry *ary_entry = &gconn->send_ary[idx]; + uint16_t idx = get_array_index(message_id); + struct GC_Message_Array_Entry *array_entry = &gconn->send_array[idx]; - if (ary_entry_is_empty(ary_entry)) { + if (array_entry_is_empty(array_entry)) { return -1; } - if (ary_entry->message_id != message_id) { // wrap-around indicates a connection problem + if (array_entry->message_id != message_id) { // wrap-around indicates a connection problem return -1; } - clear_ary_entry(ary_entry); + clear_array_entry(array_entry); - /* Put send_ary_start in proper position */ - if (idx == gconn->send_ary_start) { + /* Put send_array_start in proper position */ + if (idx == gconn->send_array_start) { uint16_t end = gconn->send_message_id % GCC_BUFFER_SIZE; - while (ary_entry_is_empty(ary_entry) && gconn->send_ary_start != end) { - gconn->send_ary_start = (gconn->send_ary_start + 1) % GCC_BUFFER_SIZE; + while (array_entry_is_empty(array_entry) && gconn->send_array_start != end) { + gconn->send_array_start = (gconn->send_array_start + 1) % GCC_BUFFER_SIZE; idx = (idx + 1) % GCC_BUFFER_SIZE; } } @@ -151,145 +153,150 @@ int gcc_handle_ack(GC_Connection *gconn, uint64_t message_id) return 0; } -/* Decides if message need to be put in recv_ary or immediately handled. +bool gcc_is_ip_set(GC_Connection *gconn) +{ + return gconn->addr.ip_port.ip.family.value != 0; +} + +/* Decides if message need to be put in received_array or immediately handled. * * Return 2 if message is in correct sequence and may be handled immediately. - * Return 1 if packet is out of sequence and added to recv_ary. + * Return 1 if packet is out of sequence and added to received_array. * Return 0 if message is a duplicate. * Return -1 on failure */ -int gcc_handle_recv_message(GC_Chat *chat, uint32_t peernumber, const uint8_t *data, uint32_t length, - uint8_t packet_type, uint64_t message_id) +int gcc_handle_received_message(GC_Chat *chat, uint32_t peer_number, const uint8_t *data, uint32_t length, + uint8_t packet_type, uint64_t message_id) { - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); - if (gconn == nullptr) { + if (!gconn) { return -1; } /* Appears to be a duplicate packet so we discard it */ - if (message_id < gconn->recv_message_id + 1) { + if (message_id < gconn->received_message_id + 1) { return 0; } - /* we're missing an older message from this peer so we store it in recv_ary */ - if (message_id > gconn->recv_message_id + 1) { - uint16_t idx = get_ary_index(message_id); - struct GC_Message_Ary_Entry *ary_entry = &gconn->recv_ary[idx]; + /* we're missing an older message from this peer so we store it in received_array */ + if (message_id > gconn->received_message_id + 1) { + uint16_t idx = get_array_index(message_id); + struct GC_Message_Array_Entry *ary_entry = &gconn->received_array[idx]; - if (!ary_entry_is_empty(ary_entry)) { + if (!array_entry_is_empty(ary_entry)) { return -1; } - if (create_ary_entry(chat->mono_time, ary_entry, data, length, packet_type, message_id) == -1) { + if (create_array_entry(chat->mono_time, ary_entry, data, length, packet_type, message_id) == -1) { return -1; } return 1; } - ++gconn->recv_message_id; + ++gconn->received_message_id; return 2; } -/* Handles peernumber's array entry with appropriate handler and clears it from array. +/* Handles peer_number's array entry with appropriate handler and clears it from array. * * Return 0 on success. * Return -1 on failure. */ -static int process_recv_ary_entry(GC_Chat *chat, Messenger *m, int groupnum, uint32_t peernumber, - struct GC_Message_Ary_Entry *ary_entry) +static int process_received_array_entry(GC_Chat *chat, Messenger *m, int group_number, uint32_t peer_number, + struct GC_Message_Array_Entry *array_entry) { - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return -1; } - int ret = handle_gc_lossless_helper(m, groupnum, peernumber, ary_entry->data, ary_entry->data_length, - ary_entry->message_id, ary_entry->packet_type); - clear_ary_entry(ary_entry); + int ret = handle_gc_lossless_helper(m, group_number, peer_number, array_entry->data, array_entry->data_length, + array_entry->message_id, array_entry->packet_type); + clear_array_entry(array_entry); if (ret == -1) { - gc_send_message_ack(chat, gconn, 0, ary_entry->message_id); + gc_send_message_ack(chat, gconn, 0, array_entry->message_id); return -1; } - gc_send_message_ack(chat, gconn, ary_entry->message_id, 0); - ++gconn->recv_message_id; + gc_send_message_ack(chat, gconn, array_entry->message_id, 0); + ++gconn->received_message_id; return 0; } -/* Checks for and handles messages that are in proper sequence in gconn's recv_ary. +/* Checks for and handles messages that are in proper sequence in gconn's received_array. * This should always be called after a new packet is handled in correct sequence. * * Return 0 on success. * Return -1 on failure. */ -int gcc_check_recv_ary(Messenger *m, int groupnum, uint32_t peernumber) +int gcc_check_received_array(Messenger *m, int group_number, uint32_t peer_number) { - GC_Chat *chat = gc_get_group(m->group_handler, groupnum); + GC_Chat *chat = gc_get_group(m->group_handler, group_number); if (!chat) { return -1; } - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return -1; } - uint16_t idx = (gconn->recv_message_id + 1) % GCC_BUFFER_SIZE; - struct GC_Message_Ary_Entry *ary_entry = &gconn->recv_ary[idx]; + uint16_t idx = (gconn->received_message_id + 1) % GCC_BUFFER_SIZE; + struct GC_Message_Array_Entry *array_entry = &gconn->received_array[idx]; - while (!ary_entry_is_empty(ary_entry)) { - if (process_recv_ary_entry(chat, m, groupnum, peernumber, ary_entry) == -1) { + while (!array_entry_is_empty(array_entry)) { + if (process_received_array_entry(chat, m, group_number, peer_number, array_entry) == -1) { return -1; } - idx = (gconn->recv_message_id + 1) % GCC_BUFFER_SIZE; - ary_entry = &gconn->recv_ary[idx]; + idx = (gconn->received_message_id + 1) % GCC_BUFFER_SIZE; + array_entry = &gconn->received_array[idx]; } return 0; } -void gcc_resend_packets(Messenger *m, GC_Chat *chat, uint32_t peernumber) +void gcc_resend_packets(Messenger *m, GC_Chat *chat, uint32_t peer_number) { - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return; } uint64_t tm = mono_time_get(m->mono_time); - uint16_t i, start = gconn->send_ary_start, end = gconn->send_message_id % GCC_BUFFER_SIZE; + uint16_t i, start = gconn->send_array_start, end = gconn->send_message_id % GCC_BUFFER_SIZE; for (i = start; i != end; i = (i + 1) % GCC_BUFFER_SIZE) { - struct GC_Message_Ary_Entry *ary_entry = &gconn->send_ary[i]; + struct GC_Message_Array_Entry *array_entry = &gconn->send_array[i]; - if (ary_entry_is_empty(ary_entry)) { + if (array_entry_is_empty(array_entry)) { continue; } - if (tm == ary_entry->last_send_try) { + if (tm == array_entry->last_send_try) { continue; } - uint64_t delta = ary_entry->last_send_try - ary_entry->time_added; - ary_entry->last_send_try = tm; + uint64_t delta = array_entry->last_send_try - array_entry->time_added; + array_entry->last_send_try = tm; /* if this occurrs less than once per second this won't be reliable */ if (delta > 1 && is_power_of_2(delta)) { - gcc_send_group_packet(chat, gconn, ary_entry->data, ary_entry->data_length, ary_entry->packet_type); + gcc_send_group_packet(chat, gconn, array_entry->data, array_entry->data_length, array_entry->packet_type); continue; } - if (mono_time_is_timeout(m->mono_time, ary_entry->time_added, GC_CONFIRMED_PEER_TIMEOUT)) { - gc_peer_delete(m, chat->groupnumber, peernumber, (const uint8_t *)"Peer timed out", 14); + if (mono_time_is_timeout(m->mono_time, array_entry->time_added, GC_CONFIRMED_PEER_TIMEOUT)) { + gc_peer_delete(m, chat->group_number, peer_number, (const uint8_t *)"Peer timed out", 14, false); return; } } @@ -337,7 +344,7 @@ int gcc_send_group_packet(const GC_Chat *chat, const GC_Connection *gconn, const /* Returns true if we have a direct connection with this group connection */ bool gcc_connection_is_direct(const Mono_Time *mono_time, const GC_Connection *gconn) { - return ((GCC_UDP_DIRECT_TIMEOUT + gconn->last_recv_direct_time) > mono_time_get(mono_time)); + return ((GCC_UDP_DIRECT_TIMEOUT + gconn->last_received_direct_time) > mono_time_get(mono_time)); } /* called when a peer leaves the group */ @@ -346,12 +353,12 @@ void gcc_peer_cleanup(GC_Connection *gconn) size_t i; for (i = 0; i < GCC_BUFFER_SIZE; ++i) { - if (gconn->send_ary[i].data) { - free(gconn->send_ary[i].data); + if (gconn->send_array[i].data) { + free(gconn->send_array[i].data); } - if (gconn->recv_ary[i].data) { - free(gconn->recv_ary[i].data); + if (gconn->received_array[i].data) { + free(gconn->received_array[i].data); } } diff --git a/toxcore/group_connection.h b/toxcore/group_connection.h index dced66f69e..51b4a79715 100644 --- a/toxcore/group_connection.h +++ b/toxcore/group_connection.h @@ -24,7 +24,9 @@ /* The time before the direct UDP connection is considered dead */ #define GCC_UDP_DIRECT_TIMEOUT (GC_PING_INTERVAL * 2 + 2) -struct GC_Message_Ary_Entry { +#define HANDSHAKE_SENDING_TIMEOUT 3 + +struct GC_Message_Array_Entry { uint8_t *data; uint32_t data_length; uint8_t packet_type; @@ -36,11 +38,11 @@ struct GC_Message_Ary_Entry { struct GC_Connection { uint64_t send_message_id; /* message_id of the next message we send to peer */ - uint16_t send_ary_start; /* send_ary index of oldest item */ - struct GC_Message_Ary_Entry send_ary[GCC_BUFFER_SIZE]; + uint16_t send_array_start; /* send_array index of oldest item */ + struct GC_Message_Array_Entry send_array[GCC_BUFFER_SIZE]; - uint64_t recv_message_id; /* message_id of peer's last message to us */ - struct GC_Message_Ary_Entry recv_ary[GCC_BUFFER_SIZE]; + uint64_t received_message_id; /* message_id of peer's last message to us */ + struct GC_Message_Array_Entry received_array[GCC_BUFFER_SIZE]; GC_PeerAddress addr; /* holds peer's extended real public key and ip_port */ uint32_t public_key_hash; /* hash of peer's real encryption public key */ @@ -49,59 +51,72 @@ struct GC_Connection { uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; /* made with our session sk and peer's session pk */ int tcp_connection_num; - uint64_t last_recv_direct_time; /* the last time we received a direct packet from this peer */ + uint64_t last_received_direct_time; /* the last time we received a direct packet from this peer */ uint64_t last_tcp_relays_shared; /* the last time we tried to send this peer our tcp relays */ - uint64_t last_rcvd_ping; - uint64_t time_added; + Node_format connected_tcp_relays[MAX_FRIEND_TCP_CONNECTIONS]; + int tcp_relays_index; + bool any_tcp_connections; + + + uint64_t last_received_ping_time; bool pending_sync_request; /* true if we have sent this peer a sync request and have not received a reply*/ bool pending_state_sync; /* used for group state syncing */ bool handshaked; /* true if we've successfully handshaked with this peer */ + uint64_t pending_handshake; + uint8_t pending_handshake_type; + bool is_pending_handshake_response; + bool is_oob_handshake; + uint8_t oob_relay_pk[ENC_PUBLIC_KEY]; bool confirmed; /* true if this peer has given us their info */ + uint32_t friend_shared_state_version; + uint32_t self_sent_shared_state_version; }; -/* Return connection object for peernumber. - * Return NULL if peernumber is invalid. +/* Return connection object for peer_number. + * Return NULL if peer_number is invalid. */ -GC_Connection *gcc_get_connection(const GC_Chat *chat, int peernumber); +GC_Connection *gcc_get_connection(const GC_Chat *chat, int peer_number); -/* Adds data of length to gconn's send_ary. +/* Adds data of length to gconn's send_array. * * Returns 0 on success and increments gconn's send_message_id. * Returns -1 on failure. */ -int gcc_add_send_ary(const Mono_Time *mono_time, GC_Connection *gconn, const uint8_t *data, uint32_t length, - uint8_t packet_type); +int gcc_add_to_send_array(const Mono_Time *mono_time, GC_Connection *gconn, const uint8_t *data, + uint32_t length, uint8_t packet_type); -/* Decides if message need to be put in recv_ary or immediately handled. +/* Decides if message need to be put in received_array or immediately handled. * * Return 2 if message is in correct sequence and may be handled immediately. - * Return 1 if packet is out of sequence and added to recv_ary. + * Return 1 if packet is out of sequence and added to received_array. * Return 0 if message is a duplicate. * Return -1 on failure */ -int gcc_handle_recv_message(GC_Chat *chat, uint32_t peernumber, const uint8_t *data, uint32_t length, - uint8_t packet_type, uint64_t message_id); +int gcc_handle_received_message(GC_Chat *chat, uint32_t peer_number, const uint8_t *data, uint32_t length, + uint8_t packet_type, uint64_t message_id); -/* Return ary index for message_id */ -uint16_t get_ary_index(uint64_t message_id); +/* Return array index for message_id */ +uint16_t get_array_index(uint64_t message_id); -/* Removes send_ary item with message_id. +/* Removes send_array item with message_id. * * Return 0 if success. * Return -1 on failure. */ int gcc_handle_ack(GC_Connection *gconn, uint64_t message_id); -/* Checks for and handles messages that are in proper sequence in gconn's recv_ary. +bool gcc_is_ip_set(GC_Connection *gconn); + +/* Checks for and handles messages that are in proper sequence in gconn's received_array. * This should always be called after a new packet is successfully handled. * * Return 0 on success. * Return -1 on failure. */ -int gcc_check_recv_ary(struct Messenger *m, int groupnum, uint32_t peernumber); +int gcc_check_received_array(struct Messenger *m, int group_number, uint32_t peer_number); -void gcc_resend_packets(struct Messenger *m, GC_Chat *chat, uint32_t peernumber); +void gcc_resend_packets(struct Messenger *m, GC_Chat *chat, uint32_t peer_number); /* Return true if we have a direct connection with this group connection */ bool gcc_connection_is_direct(const Mono_Time *mono_time, const GC_Connection *gconn); diff --git a/toxcore/group_moderation.c b/toxcore/group_moderation.c index 24e841f891..87e0de7594 100644 --- a/toxcore/group_moderation.c +++ b/toxcore/group_moderation.c @@ -133,26 +133,6 @@ bool mod_list_verify_sig_pk(const GC_Chat *chat, const uint8_t *sig_pk) return false; } -/* Returns true if sig_pk is the designated sync moderator, which is defined as the - * moderator (or founder) who has the closest signature public key to the Chat ID. - */ -static bool mod_list_chosen_one(const GC_Chat *chat, const uint8_t *sig_pk) -{ - uint16_t i; - - for (i = 0; i < chat->moderation.num_mods; ++i) { - if (id_closest(get_chat_id(chat->chat_public_key), sig_pk, chat->moderation.mod_list[i]) == 2) { - return false; - } - } - - if (id_closest(get_chat_id(chat->chat_public_key), sig_pk, get_sig_pk(chat->shared_state.founder_public_key)) == 2) { - return false; - } - - return true; -} - /* Removes moderator at index-th position in the moderator list. * * Returns 0 on success. @@ -306,21 +286,9 @@ int sanctions_list_pack(uint8_t *data, uint16_t length, struct GC_Sanction *sanc net_pack_u64(data + packed_len, sanctions[i].time_set); packed_len += TIME_STAMP_SIZE; - if (sanctions[i].type == SA_BAN) { - int ipp_size = pack_ip_port(data + packed_len, length - packed_len, &sanctions[i].info.ban_info.ip_port); + uint8_t sanctions_type = sanctions[i].type; - if (ipp_size == -1 || ipp_size + sizeof(uint16_t) + sizeof(uint32_t) + MAX_GC_NICK_SIZE > length) { - return -1; - } - - packed_len += ipp_size; - memcpy(data + packed_len, sanctions[i].info.ban_info.nick, MAX_GC_NICK_SIZE); - packed_len += MAX_GC_NICK_SIZE; - net_pack_u16(data + packed_len, sanctions[i].info.ban_info.nick_len); - packed_len += sizeof(uint16_t); - net_pack_u32(data + packed_len, sanctions[i].info.ban_info.id); - packed_len += sizeof(uint32_t); - } else if (sanctions[i].type == SA_OBSERVER) { + if (sanctions_type == SA_OBSERVER) { if (packed_len + ENC_PUBLIC_KEY > length) { return -1; } @@ -402,21 +370,7 @@ int sanctions_list_unpack(struct GC_Sanction *sanctions, struct GC_Sanction_Cred net_unpack_u64(data + len_processed, &sanctions[num].time_set); len_processed += TIME_STAMP_SIZE; - if (sanctions[num].type == SA_BAN) { - int ipp_size = unpack_ip_port(&sanctions[num].info.ban_info.ip_port, data + len_processed, length - len_processed, 1); - - if (ipp_size == -1 || ipp_size + sizeof(uint16_t) + sizeof(uint32_t) + MAX_GC_NICK_SIZE > length) { - return -1; - } - - len_processed += ipp_size; - memcpy(sanctions[num].info.ban_info.nick, data + len_processed, MAX_GC_NICK_SIZE); - len_processed += MAX_GC_NICK_SIZE; - net_unpack_u16(data + len_processed, &sanctions[num].info.ban_info.nick_len); - len_processed += sizeof(uint16_t); - net_unpack_u32(data + len_processed, &sanctions[num].info.ban_info.id); - len_processed += sizeof(uint32_t); - } else if (sanctions[num].type == SA_OBSERVER) { + if (sanctions[num].type == SA_OBSERVER) { if (len_processed + ENC_PUBLIC_KEY > length) { return -1; } @@ -482,7 +436,6 @@ void sanctions_list_make_hash(struct GC_Sanction *sanctions, uint32_t new_versio * * Returns 0 on success. * Returns -1 on failure. - * Returns -2 if sanction type is SA_BAN and the ban_id is a duplicate. */ static int sanctions_list_validate_entry(const GC_Chat *chat, struct GC_Sanction *sanction) { @@ -498,24 +451,6 @@ static int sanctions_list_validate_entry(const GC_Chat *chat, struct GC_Sanction return -1; } - if (sanction->type == SA_BAN) { - if (sanction->info.ban_info.nick_len == 0 || sanction->info.ban_info.nick_len > MAX_GC_NICK_SIZE) { - return -1; - } - - if (!ipport_isset(&sanction->info.ban_info.ip_port)) { - return -1; - } - - if (net_family_is_tcp_family(sanction->info.ban_info.ip_port.ip.family)) { - return -1; - } - - if (sanctions_list_get_ban_time_set(chat, sanction->info.ban_info.id) != 0) { - return -2; - } - } - uint8_t packed_data[sizeof(struct GC_Sanction)]; int packed_len = sanctions_list_pack(packed_data, sizeof(packed_data), sanction, nullptr, 1); @@ -679,55 +614,6 @@ static int sanctions_list_remove_index(GC_Chat *chat, uint32_t index, struct GC_ return 0; } -/* Returns a new unique ban ID. */ -static uint32_t get_new_ban_id(const GC_Chat *chat) -{ - uint32_t i, new_id = 0; - - for (i = 0; i < chat->moderation.num_sanctions; ++i) { - if (chat->moderation.sanctions[i].type != SA_BAN) { - continue; - } - - if (chat->moderation.sanctions[i].info.ban_info.id >= new_id) { - new_id = chat->moderation.sanctions[i].info.ban_info.id + 1; - } - } - - return new_id; -} - -/* Removes ban entry with ban_id from sanction list. - * If creds is NULL we make new credentials (this should only be done by a moderator or founder) - * - * Returns 0 on success. - * Returns -1 on failure or if entry was not found - */ -int sanctions_list_remove_ban(GC_Chat *chat, uint32_t ban_id, struct GC_Sanction_Creds *creds) -{ - uint32_t i; - - for (i = 0; i < chat->moderation.num_sanctions; ++i) { - if (chat->moderation.sanctions[i].type != SA_BAN) { - continue; - } - - if (chat->moderation.sanctions[i].info.ban_info.id == ban_id) { - if (sanctions_list_remove_index(chat, i, creds) == -1) { - return -1; - } - - if (creds == nullptr) { - return sanctions_list_make_creds(chat); - } - - return 0; - } - } - - return -1; -} - /* Removes observer entry for public key from sanction list. * If creds is NULL we make new credentials (this should only be done by a moderator or founder) * @@ -739,11 +625,13 @@ int sanctions_list_remove_observer(GC_Chat *chat, const uint8_t *public_key, str uint32_t i; for (i = 0; i < chat->moderation.num_sanctions; ++i) { - if (chat->moderation.sanctions[i].type != SA_OBSERVER) { + struct GC_Sanction *curr_sanction = &chat->moderation.sanctions[i]; + + if (curr_sanction->type != SA_OBSERVER) { continue; } - if (memcmp(public_key, chat->moderation.sanctions[i].info.target_pk, ENC_PUBLIC_KEY) == 0) { + if (memcmp(public_key, curr_sanction->info.target_pk, ENC_PUBLIC_KEY) == 0) { if (sanctions_list_remove_index(chat, i, creds) == -1) { return -1; } @@ -767,11 +655,13 @@ bool sanctions_list_is_observer(const GC_Chat *chat, const uint8_t *public_key) uint32_t i; for (i = 0; i < chat->moderation.num_sanctions; ++i) { - if (chat->moderation.sanctions[i].type != SA_OBSERVER) { + struct GC_Sanction *curr_sanction = &chat->moderation.sanctions[i]; + + if (curr_sanction->type != SA_OBSERVER) { continue; } - if (memcmp(chat->moderation.sanctions[i].info.target_pk, public_key, ENC_PUBLIC_KEY) == 0) { + if (memcmp(curr_sanction->info.target_pk, public_key, ENC_PUBLIC_KEY) == 0) { return true; } } @@ -782,10 +672,6 @@ bool sanctions_list_is_observer(const GC_Chat *chat, const uint8_t *public_key) /* Returns true if sanction already exists in the sanctions list. */ static bool sanctions_list_entry_exists(const GC_Chat *chat, struct GC_Sanction *sanction) { - if (sanction->type == SA_BAN) { - return sanctions_list_ip_banned(chat, &sanction->info.ban_info.ip_port); - } - if (sanction->type == SA_OBSERVER) { return sanctions_list_is_observer(chat, sanction->info.target_pk); } @@ -795,58 +681,6 @@ static bool sanctions_list_entry_exists(const GC_Chat *chat, struct GC_Sanction static int sanctions_list_sign_entry(const GC_Chat *chat, struct GC_Sanction *sanction); -/* Re-signs and re-assigns ban ID's for all sanctions entries with a ban ID equal to ban_id. - * - * Note: This function does not re-distribute the sanctions list to the group which - * you will probably want to do. - * - * Returns 0 on success. - * Returns -1 on failure. - */ -static int sanctions_list_fix_ban_id(GC_Chat *chat, uint32_t ban_id) -{ - uint32_t i; - - for (i = 0; i < chat->moderation.num_sanctions; ++i) { - if (chat->moderation.sanctions[i].type != SA_BAN) { - continue; - } - - if (chat->moderation.sanctions[i].info.ban_info.id != ban_id) { - continue; - } - - struct GC_Sanction sanction; - - memcpy(&sanction, &chat->moderation.sanctions[i], sizeof(struct GC_Sanction)); - - sanction.info.ban_info.id = get_new_ban_id(chat); - - if (sanctions_list_remove_index(chat, i, nullptr) == -1) { - return -1; - } - - if (sanctions_list_sign_entry(chat, &sanction) == -1) { - return -1; - } - - if (sanctions_list_add_entry(chat, &sanction, nullptr) == -1) { - fprintf(stderr, "sanctions_list_add_entry failed in sanctions_list_fix_ban_id\n"); - return -1; - } - - if (sanctions_list_make_creds(chat) == -1) { - return -1; - } - - if (i >= chat->moderation.num_sanctions) { - break; - } - } - - return 0; -} - /* Adds an entry to the sanctions list. The entry is first validated and the resulting * new sanction list is compared against the new credentials if necessary. * @@ -861,31 +695,11 @@ int sanctions_list_add_entry(GC_Chat *chat, struct GC_Sanction *sanction, struct return -1; // TODO(JFreegman): remove oldest entry and continue } - - int ret = sanctions_list_validate_entry(chat, sanction); - - if (ret == -1) { + if (sanctions_list_validate_entry(chat, sanction) < 0) { fprintf(stderr, "sanctions_list_validate_entry failed in add entry\n"); return -1; } - /* Duplicate ban ID: If we are the designated sync mod we re-assign the ID - * and re-distribute the fixed sanctions list. Otherwise we ignore it. - */ - if (ret == -2) { - if (!mod_list_verify_sig_pk(chat, get_sig_pk(chat->self_public_key))) { - return -1; - } - - if (!mod_list_chosen_one(chat, get_sig_pk(chat->self_public_key))) { - return -1; - } - - if (sanctions_list_fix_ban_id(chat, sanction->info.ban_info.id) == -1) { // indirect recursion - return -1; - } - } - if (sanctions_list_entry_exists(chat, sanction)) { return -1; } @@ -924,12 +738,6 @@ int sanctions_list_add_entry(GC_Chat *chat, struct GC_Sanction *sanction, struct chat->moderation.sanctions = new_list; chat->moderation.num_sanctions = index + 1; - if (ret == -2) { - if (broadcast_gc_sanctions_list(chat) == -1) { - return -1; - } - } - return 0; } @@ -952,16 +760,16 @@ static int sanctions_list_sign_entry(const GC_Chat *chat, struct GC_Sanction *sa get_sig_sk(chat->self_secret_key)); } -/* Creates a new sanction entry for peernumber where type is one GROUP_SANCTION_TYPE. +/* Creates a new sanction entry for peer_number where type is one GROUP_SANCTION_TYPE. * New entry is signed and placed in the sanction list, and the sanction list credentials * are updated. * * Returns 0 on success. * Returns -1 on failure. */ -int sanctions_list_make_entry(GC_Chat *chat, uint32_t peernumber, struct GC_Sanction *sanction, uint8_t type) +int sanctions_list_make_entry(GC_Chat *chat, uint32_t peer_number, struct GC_Sanction *sanction, uint8_t type) { - GC_Connection *gconn = gcc_get_connection(chat, peernumber); + GC_Connection *gconn = gcc_get_connection(chat, peer_number); if (gconn == nullptr) { return -1; @@ -969,16 +777,7 @@ int sanctions_list_make_entry(GC_Chat *chat, uint32_t peernumber, struct GC_Sanc memset(sanction, 0, sizeof(struct GC_Sanction)); - if (type == SA_BAN) { - if (net_family_is_tcp_family(gconn->addr.ip_port.ip.family)) { - return -1; - } - - ipport_copy(&sanction->info.ban_info.ip_port, &gconn->addr.ip_port); - memcpy(sanction->info.ban_info.nick, chat->group[peernumber].nick, MAX_GC_NICK_SIZE); - sanction->info.ban_info.nick_len = chat->group[peernumber].nick_len; - sanction->info.ban_info.id = get_new_ban_id(chat); - } else if (type == SA_OBSERVER) { + if (type == SA_OBSERVER) { memcpy(sanction->info.target_pk, gconn->addr.public_key, ENC_PUBLIC_KEY); } else { return -1; @@ -1044,120 +843,4 @@ void sanctions_list_cleanup(GC_Chat *chat) chat->moderation.num_sanctions = 0; } - -/* Ban list queries */ - - -/* Returns true if the IP address is in the ban list. - * All sanction list entries are assumed to be valid. - */ -bool sanctions_list_ip_banned(const GC_Chat *chat, IP_Port *ip_port) -{ - uint32_t i; - - for (i = 0; i < chat->moderation.num_sanctions; ++i) { - if (chat->moderation.sanctions[i].type != SA_BAN) { - continue; - } - - if (ip_equal(&chat->moderation.sanctions[i].info.ban_info.ip_port.ip, &ip_port->ip)) { - return true; - } - } - - return false; -} - -/* Returns the number of sanction list entries that are of type SA_BAN */ -uint32_t sanctions_list_num_banned(const GC_Chat *chat) -{ - uint32_t i, count = 0; - - for (i = 0; i < chat->moderation.num_sanctions; ++i) { - if (chat->moderation.sanctions[i].type == SA_BAN) { - ++count; - } - } - - return count; -} - -/* Fills list with all valid ban ID's. */ -void sanctions_list_get_ban_list(const GC_Chat *chat, uint32_t *list) -{ - if (!list) { - return; - } - - uint32_t i, count = 0; - - for (i = 0; i < chat->moderation.num_sanctions; ++i) { - if (chat->moderation.sanctions[i].type == SA_BAN) { - list[count] = chat->moderation.sanctions[i].info.ban_info.id; - ++count; - } - } -} - -/* Returns the nick length of the ban entry associted with ban_id on success. - * Returns 0 if ban_id does not exist. - */ -uint16_t sanctions_list_get_ban_nick_length(const GC_Chat *chat, uint32_t ban_id) -{ - uint32_t i; - - for (i = 0; i < chat->moderation.num_sanctions; ++i) { - if (chat->moderation.sanctions[i].type != SA_BAN) { - continue; - } - - if (chat->moderation.sanctions[i].info.ban_info.id == ban_id) { - return chat->moderation.sanctions[i].info.ban_info.nick_len; - } - } - - return 0; -} - -/* Copies the nick associated with ban_id to nick. - * - * Returns 0 on success. - * Returns -1 if ban_id does not exist. - */ -int sanctions_list_get_ban_nick(const GC_Chat *chat, uint32_t ban_id, uint8_t *nick) -{ - uint32_t i; - - for (i = 0; i < chat->moderation.num_sanctions; ++i) { - if (chat->moderation.sanctions[i].type == SA_BAN) { - if (chat->moderation.sanctions[i].info.ban_info.id == ban_id) { - memcpy(nick, chat->moderation.sanctions[i].info.ban_info.nick, MAX_GC_NICK_SIZE); - return 0; - } - } - } - - return -1; -} - -/* Returns a timestamp indicating when the ban designated by ban_id was set. - * Returns 0 if ban_id does not exist. - */ -uint64_t sanctions_list_get_ban_time_set(const GC_Chat *chat, uint32_t ban_id) -{ - uint32_t i; - - for (i = 0; i < chat->moderation.num_sanctions; ++i) { - if (chat->moderation.sanctions[i].type != SA_BAN) { - continue; - } - - if (chat->moderation.sanctions[i].info.ban_info.id == ban_id) { - return chat->moderation.sanctions[i].time_set; - } - } - - return 0; -} - #endif /* VANILLA_NACL */ diff --git a/toxcore/group_moderation.h b/toxcore/group_moderation.h index 27dc0a3836..7b1d8f0052 100644 --- a/toxcore/group_moderation.h +++ b/toxcore/group_moderation.h @@ -14,24 +14,15 @@ #define GC_SANCTIONS_CREDENTIALS_SIZE (sizeof(uint32_t) + GC_MODERATION_HASH_SIZE + SIG_PUBLIC_KEY + SIGNATURE_SIZE) typedef enum Group_Sanction_Type { - SA_BAN, SA_OBSERVER, SA_INVALID, } Group_Sanction_Type; -struct GC_Ban { - IP_Port ip_port; - uint8_t nick[MAX_GC_NICK_SIZE]; - uint16_t nick_len; - uint32_t id; -}; - typedef union GC_Sanction_Info { - struct GC_Ban ban_info; /* Used if type is SA_BAN */ - uint8_t target_pk[ENC_PUBLIC_KEY]; /* Used if type is SA_OBSERVER */ + uint8_t target_pk[ENC_PUBLIC_KEY]; /* Used if type is SA_OBSERVER */ } GC_Sanction_Info; -/* Holds data pertaining to a peer who has been banned or demoted to observer. */ +/* Holds data pertaining to a peer who has sanctioned. */ struct GC_Sanction { uint8_t public_sig_key[SIG_PUBLIC_KEY]; uint64_t time_set; @@ -43,6 +34,8 @@ struct GC_Sanction { uint8_t signature[SIGNATURE_SIZE]; }; +typedef struct GC_Sanction GC_Sanction; + /* Unpacks data into the moderator list. * data should contain num_mods entries of size GC_MOD_LIST_ENTRY_SIZE. * @@ -153,13 +146,13 @@ int sanctions_list_check_integrity(const GC_Chat *chat, struct GC_Sanction_Creds */ int sanctions_list_add_entry(GC_Chat *chat, struct GC_Sanction *sanction, struct GC_Sanction_Creds *creds); -/* Creates a new sanction entry for peernumber where type is one GROUP_SANCTION_TYPE. +/* Creates a new sanction entry for peer_number where type is one GROUP_SANCTION_TYPE. * New entry is signed and placed in the sanctions list. * * Returns 0 on success. * Returns -1 on failure. */ -int sanctions_list_make_entry(GC_Chat *chat, uint32_t peernumber, struct GC_Sanction *sanction, uint8_t type); +int sanctions_list_make_entry(GC_Chat *chat, uint32_t peer_number, struct GC_Sanction *sanction, uint8_t type); /* Returns true if public key is in the observer list. */ bool sanctions_list_is_observer(const GC_Chat *chat, const uint8_t *public_key); @@ -172,15 +165,6 @@ bool sanctions_list_is_observer(const GC_Chat *chat, const uint8_t *public_key); */ int sanctions_list_remove_observer(GC_Chat *chat, const uint8_t *public_key, struct GC_Sanction_Creds *creds); -/* Removes ban entry with ban_id from sanction list. - * If creds is NULL we make new credentials (this should only be done by a moderator or founder) - * - * - * Returns 0 on success. - * Returns -1 on failure or if entry was not found - */ -int sanctions_list_remove_ban(GC_Chat *chat, uint32_t ban_id, struct GC_Sanction_Creds *creds); - /* Replaces all sanctions list signatures made by public_sig_key with the caller's. * This is called whenever the founder demotes a moderator. * @@ -200,35 +184,4 @@ void sanctions_list_make_hash(struct GC_Sanction *sanctions, uint32_t new_versio void sanctions_list_cleanup(GC_Chat *chat); - - -/* Ban list queries */ - - -/* Returns true if the IP address is in the ban list. */ -bool sanctions_list_ip_banned(const GC_Chat *chat, IP_Port *ip_port); - -/* Returns the number of sanctions list entries that are of type SA_BAN */ -uint32_t sanctions_list_num_banned(const GC_Chat *chat); - -/* Fills list with all valid ban ID's. */ -void sanctions_list_get_ban_list(const GC_Chat *chat, uint32_t *list); - -/* Returns the nick length of the ban entry associted with ban_id on success. - * Returns 0 if ban_id does not exist. - */ -uint16_t sanctions_list_get_ban_nick_length(const GC_Chat *chat, uint32_t ban_id); - -/* Copies the nick associated with ban_id to nick. - * - * Returns 0 on success. - * Returns -1 if ban_id does not exist. - */ -int sanctions_list_get_ban_nick(const GC_Chat *chat, uint32_t ban_id, uint8_t *nick); - -/* Returns a timestamp indicating when the ban designated by ban_id was set. - * Returns 0 if ban_id does not exist. - */ -uint64_t sanctions_list_get_ban_time_set(const GC_Chat *chat, uint32_t ban_id); - #endif /* GROUP_MODERATION_H */ diff --git a/toxcore/net_crypto.h b/toxcore/net_crypto.h index 9e951154b8..6a6f528ba9 100644 --- a/toxcore/net_crypto.h +++ b/toxcore/net_crypto.h @@ -43,9 +43,9 @@ /** Messages. */ -#define PACKET_ID_PADDING 3 // Denotes padding -#define PACKET_ID_REQUEST 4 // Used to request unreceived packets -#define PACKET_ID_KILL 5 // Used to kill connection +#define PACKET_ID_PADDING 0 // Denotes padding +#define PACKET_ID_REQUEST 1 // Used to request unreceived packets +#define PACKET_ID_KILL 2 // Used to kill connection #define PACKET_ID_ONLINE 24 #define PACKET_ID_OFFLINE 25 diff --git a/toxcore/onion_announce.c b/toxcore/onion_announce.c index 6d38012aa7..f3ae756435 100644 --- a/toxcore/onion_announce.c +++ b/toxcore/onion_announce.c @@ -12,6 +12,7 @@ #include "onion_announce.h" +#include #include #include @@ -21,7 +22,8 @@ #define PING_ID_TIMEOUT ONION_ANNOUNCE_TIMEOUT -#define ANNOUNCE_REQUEST_SIZE_RECV (ONION_ANNOUNCE_REQUEST_SIZE + ONION_RETURN_3) +#define ANNOUNCE_REQUEST_MIN_SIZE_RECV (ONION_ANNOUNCE_REQUEST_MIN_SIZE + ONION_RETURN_3) +#define ANNOUNCE_REQUEST_MAX_SIZE_RECV (ONION_ANNOUNCE_REQUEST_MAX_SIZE + ONION_RETURN_3) #define DATA_REQUEST_MIN_SIZE ONION_DATA_REQUEST_MIN_SIZE #define DATA_REQUEST_MIN_SIZE_RECV (DATA_REQUEST_MIN_SIZE + ONION_RETURN_3) @@ -38,6 +40,7 @@ struct Onion_Announce { Mono_Time *mono_time; DHT *dht; Networking_Core *net; + GC_Announces_List *gc_announces_list; Onion_Announce_Entry entries[ONION_ANNOUNCE_MAX_ENTRIES]; /* This is CRYPTO_SYMMETRIC_KEY_SIZE long just so we can use new_symmetric_key() to fill it */ uint8_t secret_bytes[CRYPTO_SYMMETRIC_KEY_SIZE]; @@ -55,7 +58,7 @@ void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint onion_a->entries[entry].time = time; } -/* Create an onion announce request packet in packet of max_packet_length (recommended size ONION_ANNOUNCE_REQUEST_SIZE). +/* Create an onion announce request packet in packet of max_packet_length (recommended size ONION_ANNOUNCE_REQUEST_MIN_SIZE). * * dest_client_id is the public key of the node the packet will be sent to. * public_key and secret_key is the kepair which will be used to encrypt the request. @@ -72,7 +75,7 @@ int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const u const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *ping_id, const uint8_t *client_id, const uint8_t *data_public_key, uint64_t sendback_data) { - if (max_packet_length < ONION_ANNOUNCE_REQUEST_SIZE) { + if (max_packet_length < ONION_ANNOUNCE_REQUEST_MIN_SIZE) { return -1; } @@ -90,15 +93,63 @@ int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const u int len = encrypt_data(dest_client_id, secret_key, packet + 1, plain, sizeof(plain), packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); - if ((uint32_t)len + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE != ONION_ANNOUNCE_REQUEST_SIZE) { + if ((uint32_t)len + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE != ONION_ANNOUNCE_REQUEST_MIN_SIZE) { return -1; } memcpy(packet + 1 + CRYPTO_NONCE_SIZE, public_key, CRYPTO_PUBLIC_KEY_SIZE); - return ONION_ANNOUNCE_REQUEST_SIZE; + return ONION_ANNOUNCE_REQUEST_MIN_SIZE; } +#ifndef VANILLA_NACL + +// TODO: params - to struct +int create_gc_announce_request(uint8_t *packet, uint16_t max_packet_length, const uint8_t *dest_client_id, + const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *ping_id, + const uint8_t *client_id, const uint8_t *data_public_key, uint64_t sendback_data, + const uint8_t *gc_data, int16_t gc_data_length) +{ + if (max_packet_length < ONION_ANNOUNCE_REQUEST_MAX_SIZE || gc_data_length <= 0) { + return -1; + } + + uint8_t plain[ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + GC_ANNOUNCE_MAX_SIZE]; + uint8_t *position_in_plain = plain; + size_t encrypted_size = sizeof(plain) - GC_ANNOUNCE_MAX_SIZE + gc_data_length; + + memcpy(plain, ping_id, ONION_PING_ID_SIZE); + position_in_plain += ONION_PING_ID_SIZE; + + memcpy(position_in_plain, client_id, CRYPTO_PUBLIC_KEY_SIZE); + position_in_plain += CRYPTO_PUBLIC_KEY_SIZE; + + memcpy(position_in_plain, data_public_key, CRYPTO_PUBLIC_KEY_SIZE); + position_in_plain += CRYPTO_PUBLIC_KEY_SIZE; + + memcpy(position_in_plain, &sendback_data, sizeof(sendback_data)); + position_in_plain += sizeof(sendback_data); + + memcpy(position_in_plain, gc_data, (size_t)gc_data_length); + + packet[0] = NET_PACKET_ANNOUNCE_REQUEST; + random_nonce(packet + 1); + memcpy(packet + 1 + CRYPTO_NONCE_SIZE, public_key, CRYPTO_PUBLIC_KEY_SIZE); + + int len = encrypt_data(dest_client_id, secret_key, packet + 1, plain, + encrypted_size, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); + + uint32_t full_length = (uint32_t)len + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE; + + if (full_length != ONION_ANNOUNCE_REQUEST_MIN_SIZE + gc_data_length) { + return -1; + } + + return full_length; +} +#endif // VANILLA_NACL + /* Create an onion data request packet in packet of max_packet_length (recommended size ONION_MAX_PACKET_SIZE). * * public_key is the real public key of the node which we want to send the data of length length to. @@ -159,7 +210,7 @@ int send_announce_request(Networking_Core *net, const Onion_Path *path, Node_for const uint8_t *secret_key, const uint8_t *ping_id, const uint8_t *client_id, const uint8_t *data_public_key, uint64_t sendback_data) { - uint8_t request[ONION_ANNOUNCE_REQUEST_SIZE]; + uint8_t request[ONION_ANNOUNCE_REQUEST_MIN_SIZE]; int len = create_announce_request(request, sizeof(request), dest.public_key, public_key, secret_key, ping_id, client_id, data_public_key, sendback_data); @@ -354,12 +405,147 @@ static int add_to_entries(Onion_Announce *onion_a, IP_Port ret_ip_port, const ui return in_entries(onion_a, public_key); } +static int handle_gc_announce_request(Onion_Announce *onion_a, IP_Port source, const uint8_t *packet, uint16_t length) +{ + if (length > ANNOUNCE_REQUEST_MAX_SIZE_RECV || length <= ANNOUNCE_REQUEST_MIN_SIZE_RECV) { + return 1; + } + +#ifdef VANILLA_NACL + return 1; +#endif + + const uint8_t *packet_public_key = packet + 1 + CRYPTO_NONCE_SIZE; + uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; + get_shared_key(onion_a->mono_time, &onion_a->shared_keys_recv, shared_key, dht_get_self_secret_key(onion_a->dht), + packet_public_key); + + size_t minimal_size = ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE * 2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH; + VLA(uint8_t, plain, minimal_size + GC_ANNOUNCE_MAX_SIZE); + size_t encrypted_size = minimal_size + length - ANNOUNCE_REQUEST_MIN_SIZE_RECV; + int len = decrypt_data_symmetric(shared_key, packet + 1, + packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, + encrypted_size + CRYPTO_MAC_SIZE, plain); + + if ((uint32_t)len != encrypted_size) { + return 1; + } + + uint8_t ping_id1[ONION_PING_ID_SIZE]; + generate_ping_id(onion_a, mono_time_get(onion_a->mono_time), packet_public_key, source, ping_id1); + + uint8_t ping_id2[ONION_PING_ID_SIZE]; + generate_ping_id(onion_a, mono_time_get(onion_a->mono_time) + PING_ID_TIMEOUT, packet_public_key, source, ping_id2); + + int index; + + uint8_t *data_public_key = plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE; + + if (crypto_memcmp(ping_id1, plain, ONION_PING_ID_SIZE) == 0 + || crypto_memcmp(ping_id2, plain, ONION_PING_ID_SIZE) == 0) { + index = add_to_entries(onion_a, source, packet_public_key, data_public_key, + packet + (length - ONION_RETURN_3)); + } else { + index = in_entries(onion_a, plain + ONION_PING_ID_SIZE); + } + + /*Respond with a gc announce response packet*/ + Node_format nodes_list[MAX_SENT_NODES]; + unsigned int num_nodes = get_close_nodes(onion_a->dht, plain + ONION_PING_ID_SIZE, nodes_list, net_family_unspec, + ip_is_lan(source.ip), 1); + uint8_t nonce[CRYPTO_NONCE_SIZE]; + random_nonce(nonce); + + GC_Announce gc_announces[MAX_SENT_ANNOUNCES]; + uint8_t pl[3 + ONION_PING_ID_SIZE + sizeof(nodes_list) + sizeof(gc_announces)]; + + if (index == -1) { + pl[0] = 0; + memcpy(pl + 1, ping_id2, ONION_PING_ID_SIZE); + } else { + if (public_key_cmp(onion_a->entries[index].public_key, packet_public_key) == 0) { + if (public_key_cmp(onion_a->entries[index].data_public_key, data_public_key) != 0) { + pl[0] = 0; + memcpy(pl + 1, ping_id2, ONION_PING_ID_SIZE); + } else { + pl[0] = 2; + memcpy(pl + 1, ping_id2, ONION_PING_ID_SIZE); + } + } else { + pl[0] = 1; + memcpy(pl + 1, onion_a->entries[index].data_public_key, CRYPTO_PUBLIC_KEY_SIZE); + } + } + + int nodes_length = 0; + + if (num_nodes != 0) { + nodes_length = pack_nodes(pl + 2 + ONION_PING_ID_SIZE, sizeof(nodes_list), nodes_list, num_nodes); + + if (nodes_length <= 0) { + return 1; + } + } + + pl[1 + ONION_PING_ID_SIZE] = (uint8_t)num_nodes; + + GC_Announces_List *gc_announces_list = onion_a->gc_announces_list; + GC_Public_Announce public_announce; + int unpack_result = unpack_public_announce(plain + minimal_size, length - ANNOUNCE_REQUEST_MIN_SIZE_RECV, + &public_announce); + + if (unpack_result == -1) { + return 1; + } + + GC_Peer_Announce *new_announce = add_gc_announce(onion_a->mono_time, gc_announces_list, &public_announce); + + if (!new_announce) { + return 1; + } + + uint8_t num_ann = (uint8_t)get_gc_announces(gc_announces_list, gc_announces, MAX_SENT_ANNOUNCES, + public_announce.chat_public_key, + new_announce->base_announce.peer_public_key); + size_t announces_length; + int offset = 2 + ONION_PING_ID_SIZE + nodes_length; + int packed_announces = pack_announces_list(pl + offset, sizeof(pl) - offset, gc_announces, + num_ann, &announces_length); + + if (packed_announces != num_ann) { + return -1; + } + + offset += announces_length; + + uint8_t data[ONION_ANNOUNCE_RESPONSE_MAX_SIZE]; + len = encrypt_data_symmetric(shared_key, nonce, pl, offset, + data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE); + + if (len != offset + CRYPTO_MAC_SIZE) { + return 1; + } + + data[0] = NET_PACKET_ANNOUNCE_RESPONSE; + memcpy(data + 1, plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE, + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH); + memcpy(data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH, nonce, CRYPTO_NONCE_SIZE); + + if (send_onion_response(onion_a->net, source, data, + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE + len, + packet + (length - ONION_RETURN_3)) == -1) { + return 1; + } + + return 0; +} + static int handle_announce_request(void *object, IP_Port source, const uint8_t *packet, uint16_t length, void *userdata) { Onion_Announce *onion_a = (Onion_Announce *)object; - if (length != ANNOUNCE_REQUEST_SIZE_RECV) { - return 1; + if (length != ANNOUNCE_REQUEST_MIN_SIZE_RECV) { + return handle_gc_announce_request(onion_a, source, packet, length); } const uint8_t *packet_public_key = packet + 1 + CRYPTO_NONCE_SIZE; @@ -370,8 +556,7 @@ static int handle_announce_request(void *object, IP_Port source, const uint8_t * uint8_t plain[ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH]; int len = decrypt_data_symmetric(shared_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, - ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + - CRYPTO_MAC_SIZE, plain); + sizeof(plain) + CRYPTO_MAC_SIZE, plain); if ((uint32_t)len != sizeof(plain)) { return 1; @@ -390,7 +575,7 @@ static int handle_announce_request(void *object, IP_Port source, const uint8_t * if (crypto_memcmp(ping_id1, plain, ONION_PING_ID_SIZE) == 0 || crypto_memcmp(ping_id2, plain, ONION_PING_ID_SIZE) == 0) { index = add_to_entries(onion_a, source, packet_public_key, data_public_key, - packet + (ANNOUNCE_REQUEST_SIZE_RECV - ONION_RETURN_3)); + packet + (length - ONION_RETURN_3)); } else { index = in_entries(onion_a, plain + ONION_PING_ID_SIZE); } @@ -402,7 +587,7 @@ static int handle_announce_request(void *object, IP_Port source, const uint8_t * uint8_t nonce[CRYPTO_NONCE_SIZE]; random_nonce(nonce); - uint8_t pl[1 + ONION_PING_ID_SIZE + sizeof(nodes_list)]; + uint8_t pl[2 + ONION_PING_ID_SIZE + sizeof(nodes_list)]; if (index == -1) { pl[0] = 0; @@ -425,18 +610,20 @@ static int handle_announce_request(void *object, IP_Port source, const uint8_t * int nodes_length = 0; if (num_nodes != 0) { - nodes_length = pack_nodes(pl + 1 + ONION_PING_ID_SIZE, sizeof(nodes_list), nodes_list, num_nodes); + nodes_length = pack_nodes(pl + 2 + ONION_PING_ID_SIZE, sizeof(nodes_list), nodes_list, num_nodes); if (nodes_length <= 0) { return 1; } } + pl[1 + ONION_PING_ID_SIZE] = (uint8_t)num_nodes; + uint8_t data[ONION_ANNOUNCE_RESPONSE_MAX_SIZE]; - len = encrypt_data_symmetric(shared_key, nonce, pl, 1 + ONION_PING_ID_SIZE + nodes_length, + len = encrypt_data_symmetric(shared_key, nonce, pl, 2 + ONION_PING_ID_SIZE + nodes_length, data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE); - if (len != 1 + ONION_PING_ID_SIZE + nodes_length + CRYPTO_MAC_SIZE) { + if (len != 2 + ONION_PING_ID_SIZE + nodes_length + CRYPTO_MAC_SIZE) { return 1; } @@ -447,7 +634,7 @@ static int handle_announce_request(void *object, IP_Port source, const uint8_t * if (send_onion_response(onion_a->net, source, data, 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE + len, - packet + (ANNOUNCE_REQUEST_SIZE_RECV - ONION_RETURN_3)) == -1) { + packet + (length - ONION_RETURN_3)) == -1) { return 1; } @@ -484,18 +671,27 @@ static int handle_data_request(void *object, IP_Port source, const uint8_t *pack return 0; } -Onion_Announce *new_onion_announce(Mono_Time *mono_time, DHT *dht) +Onion_Announce *new_onion_announce(Mono_Time *mono_time, DHT *dht, GC_Announces_List *gc_announces_list) { if (dht == nullptr) { return nullptr; } +#ifndef VANILLA_NACL + + if (gc_announces_list == nullptr) { + return nullptr; + } + +#endif + Onion_Announce *onion_a = (Onion_Announce *)calloc(1, sizeof(Onion_Announce)); if (onion_a == nullptr) { return nullptr; } + onion_a->gc_announces_list = gc_announces_list; onion_a->mono_time = mono_time; onion_a->dht = dht; onion_a->net = dht_get_net(dht); diff --git a/toxcore/onion_announce.h b/toxcore/onion_announce.h index 3145803c15..d8c1fc5c5b 100644 --- a/toxcore/onion_announce.h +++ b/toxcore/onion_announce.h @@ -10,6 +10,7 @@ #define C_TOXCORE_TOXCORE_ONION_ANNOUNCE_H #include "onion.h" +#include "group_announce.h" #define ONION_ANNOUNCE_MAX_ENTRIES 160 #define ONION_ANNOUNCE_TIMEOUT 300 @@ -17,10 +18,12 @@ #define ONION_ANNOUNCE_SENDBACK_DATA_LENGTH (sizeof(uint64_t)) -#define ONION_ANNOUNCE_REQUEST_SIZE (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_MAC_SIZE) +#define MAX_SENT_GC_NODES 1 +#define ONION_ANNOUNCE_REQUEST_MIN_SIZE (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_MAC_SIZE) +#define ONION_ANNOUNCE_REQUEST_MAX_SIZE (ONION_ANNOUNCE_REQUEST_MIN_SIZE + GC_ANNOUNCE_MAX_SIZE) -#define ONION_ANNOUNCE_RESPONSE_MIN_SIZE (1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE + 1 + ONION_PING_ID_SIZE + CRYPTO_MAC_SIZE) -#define ONION_ANNOUNCE_RESPONSE_MAX_SIZE (ONION_ANNOUNCE_RESPONSE_MIN_SIZE + sizeof(Node_format)*MAX_SENT_NODES) +#define ONION_ANNOUNCE_RESPONSE_MIN_SIZE (2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE + ONION_PING_ID_SIZE + CRYPTO_MAC_SIZE) +#define ONION_ANNOUNCE_RESPONSE_MAX_SIZE (ONION_ANNOUNCE_RESPONSE_MIN_SIZE + GC_ANNOUNCE_MAX_SIZE * MAX_SENT_NODES) #define ONION_DATA_RESPONSE_MIN_SIZE (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE) @@ -37,7 +40,7 @@ typedef struct Onion_Announce Onion_Announce; uint8_t *onion_announce_entry_public_key(Onion_Announce *onion_a, uint32_t entry); void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint64_t time); -/* Create an onion announce request packet in packet of max_packet_length (recommended size ONION_ANNOUNCE_REQUEST_SIZE). +/* Create an onion announce request packet in packet of max_packet_length (recommended size ONION_ANNOUNCE_REQUEST_MIN_SIZE). * * dest_client_id is the public key of the node the packet will be sent to. * public_key and secret_key is the kepair which will be used to encrypt the request. @@ -54,6 +57,12 @@ int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const u const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *ping_id, const uint8_t *client_id, const uint8_t *data_public_key, uint64_t sendback_data); + +int create_gc_announce_request(uint8_t *packet, uint16_t max_packet_length, const uint8_t *dest_client_id, + const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *ping_id, + const uint8_t *client_id, const uint8_t *data_public_key, uint64_t sendback_data, + const uint8_t *gc_data, int16_t gc_data_length); + /* Create an onion data request packet in packet of max_packet_length (recommended size ONION_MAX_PACKET_SIZE). * * public_key is the real public key of the node which we want to send the data of length length to. @@ -105,7 +114,7 @@ int send_data_request(Networking_Core *net, const Onion_Path *path, IP_Port dest const uint8_t *encrypt_public_key, const uint8_t *nonce, const uint8_t *data, uint16_t length); -Onion_Announce *new_onion_announce(Mono_Time *mono_time, DHT *dht); +Onion_Announce *new_onion_announce(Mono_Time *mono_time, DHT *dht, GC_Announces_List *gc_announces_list); void kill_onion_announce(Onion_Announce *onion_a); diff --git a/toxcore/onion_client.c b/toxcore/onion_client.c index e161928bae..237084ce54 100644 --- a/toxcore/onion_client.c +++ b/toxcore/onion_client.c @@ -13,10 +13,13 @@ #include "onion_client.h" +#include #include #include +#include #include "LAN_discovery.h" +#include "group_chats.h" #include "mono_time.h" #include "util.h" @@ -25,115 +28,6 @@ #define ANNOUNCE_ARRAY_SIZE 256 #define ANNOUNCE_TIMEOUT 10 -typedef struct Onion_Node { - uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; - IP_Port ip_port; - uint8_t ping_id[ONION_PING_ID_SIZE]; - uint8_t data_public_key[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t is_stored; - - uint64_t added_time; - - uint64_t timestamp; - - uint64_t last_pinged; - - uint8_t unsuccessful_pings; - - uint32_t path_used; -} Onion_Node; - -typedef struct Onion_Client_Paths { - Onion_Path paths[NUMBER_ONION_PATHS]; - uint64_t last_path_success[NUMBER_ONION_PATHS]; - uint64_t last_path_used[NUMBER_ONION_PATHS]; - uint64_t path_creation_time[NUMBER_ONION_PATHS]; - /* number of times used without success. */ - unsigned int last_path_used_times[NUMBER_ONION_PATHS]; -} Onion_Client_Paths; - -typedef struct Last_Pinged { - uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; - uint64_t timestamp; -} Last_Pinged; - -typedef struct Onion_Friend { - uint8_t status; /* 0 if friend is not valid, 1 if friend is valid.*/ - uint8_t is_online; /* Set by the onion_set_friend_status function. */ - - uint8_t know_dht_public_key; /* 0 if we don't know the dht public key of the other, 1 if we do. */ - uint8_t dht_public_key[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t real_public_key[CRYPTO_PUBLIC_KEY_SIZE]; - - Onion_Node clients_list[MAX_ONION_CLIENTS]; - uint8_t temp_public_key[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t temp_secret_key[CRYPTO_SECRET_KEY_SIZE]; - - uint64_t last_dht_pk_onion_sent; - uint64_t last_dht_pk_dht_sent; - - uint64_t last_noreplay; - - uint64_t last_seen; - - Last_Pinged last_pinged[MAX_STORED_PINGED_NODES]; - uint8_t last_pinged_index; - - recv_tcp_relay_cb *tcp_relay_node_callback; - void *tcp_relay_node_callback_object; - uint32_t tcp_relay_node_callback_number; - - onion_dht_pk_cb *dht_pk_callback; - void *dht_pk_callback_object; - uint32_t dht_pk_callback_number; - - uint32_t run_count; -} Onion_Friend; - -typedef struct Onion_Data_Handler { - oniondata_handler_cb *function; - void *object; -} Onion_Data_Handler; - -struct Onion_Client { - Mono_Time *mono_time; - - DHT *dht; - Net_Crypto *c; - Networking_Core *net; - Onion_Friend *friends_list; - uint16_t num_friends; - - Onion_Node clients_announce_list[MAX_ONION_CLIENTS_ANNOUNCE]; - uint64_t last_announce; - - Onion_Client_Paths onion_paths_self; - Onion_Client_Paths onion_paths_friends; - - uint8_t secret_symmetric_key[CRYPTO_SYMMETRIC_KEY_SIZE]; - uint64_t last_run; - uint64_t first_run; - - uint8_t temp_public_key[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t temp_secret_key[CRYPTO_SECRET_KEY_SIZE]; - - Last_Pinged last_pinged[MAX_STORED_PINGED_NODES]; - - Node_format path_nodes[MAX_PATH_NODES]; - uint16_t path_nodes_index; - - Node_format path_nodes_bs[MAX_PATH_NODES]; - uint16_t path_nodes_index_bs; - - Ping_Array *announce_ping_array; - uint8_t last_pinged_index; - Onion_Data_Handler onion_data_handlers[256]; - - uint64_t last_packet_recv; - - unsigned int onion_connected; - bool udp_connected; -}; DHT *onion_get_dht(const Onion_Client *onion_c) { @@ -369,7 +263,7 @@ static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_pa } if (path_timed_out(onion_c->mono_time, onion_paths, pathnum)) { - Node_format nodes[ONION_PATH_LENGTH]; + Node_format nodes[ONION_PATH_LENGTH] = {{{0}}}; if (random_nodes_path_onion(onion_c, nodes, ONION_PATH_LENGTH) != ONION_PATH_LENGTH) { return -1; @@ -582,7 +476,7 @@ static int client_send_announce_request(Onion_Client *onion_c, uint32_t num, IP_ ping_id = zero_ping_id; } - uint8_t request[ONION_ANNOUNCE_REQUEST_SIZE]; + uint8_t request[ONION_ANNOUNCE_REQUEST_MAX_SIZE]; int len; if (num == 0) { @@ -590,9 +484,25 @@ static int client_send_announce_request(Onion_Client *onion_c, uint32_t num, IP_ nc_get_self_secret_key(onion_c->c), ping_id, nc_get_self_public_key(onion_c->c), onion_c->temp_public_key, sendback); } else { - len = create_announce_request(request, sizeof(request), dest_pubkey, onion_c->friends_list[num - 1].temp_public_key, - onion_c->friends_list[num - 1].temp_secret_key, ping_id, - onion_c->friends_list[num - 1].real_public_key, zero_ping_id, sendback); + Onion_Friend *onion_friend = &onion_c->friends_list[num - 1]; + + if (onion_friend->gc_data_length == 0) { // contact is a friend + len = create_announce_request(request, sizeof(request), dest_pubkey, onion_friend->temp_public_key, + onion_friend->temp_secret_key, ping_id, onion_friend->real_public_key, + zero_ping_id, sendback); + } else if (onion_friend->gc_data_length > 0) { // contact is a gc + +#ifndef VANILLA_NACL + len = create_gc_announce_request(request, sizeof(request), dest_pubkey, onion_friend->temp_public_key, + onion_friend->temp_secret_key, ping_id, onion_friend->real_public_key, + zero_ping_id, sendback, onion_friend->gc_data, + onion_friend->gc_data_length); +#else + return -1; +#endif // VANILLA_NACL + } else { + return 0; + } } if (len == -1) { @@ -836,8 +746,6 @@ static int handle_announce_response(void *object, IP_Port source, const uint8_t return 1; } - uint16_t len_nodes = length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE; - uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; IP_Port ip_port; uint32_t path_num; @@ -847,7 +755,8 @@ static int handle_announce_response(void *object, IP_Port source, const uint8_t return 1; } - VLA(uint8_t, plain, 1 + ONION_PING_ID_SIZE + len_nodes); + uint8_t plain[1 + ONION_PING_ID_SIZE + ONION_ANNOUNCE_RESPONSE_MAX_SIZE - ONION_ANNOUNCE_RESPONSE_MIN_SIZE]; + int plain_size = 1 + ONION_PING_ID_SIZE + length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE; int len; if (num == 0) { @@ -866,7 +775,7 @@ static int handle_announce_response(void *object, IP_Port source, const uint8_t length - (1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE), plain); } - if ((uint32_t)len != SIZEOF_VLA(plain)) { + if ((uint32_t)len != plain_size) { return 1; } @@ -876,11 +785,19 @@ static int handle_announce_response(void *object, IP_Port source, const uint8_t return 1; } - if (len_nodes != 0) { + uint16_t len_nodes = 0; + uint8_t nodes_count = plain[1 + ONION_PING_ID_SIZE]; + + if (nodes_count > 0) { + if (nodes_count > MAX_SENT_NODES) { + return 1; + } + Node_format nodes[MAX_SENT_NODES]; - int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, nullptr, plain + 1 + ONION_PING_ID_SIZE, len_nodes, 0); + int num_nodes = unpack_nodes(nodes, nodes_count, &len_nodes, plain + 2 + ONION_PING_ID_SIZE, + plain_size - 2 - ONION_PING_ID_SIZE, 0); - if (num_nodes <= 0) { + if (num_nodes < 0) { return 1; } @@ -889,7 +806,35 @@ static int handle_announce_response(void *object, IP_Port source, const uint8_t } } - // TODO(irungentoo): LAN vs non LAN ips?, if we are connected only to LAN, are we offline? +#ifndef VANILLA_NACL + + if (len_nodes + 1 < length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE) { + GC_Announce announces[MAX_SENT_ANNOUNCES]; + GC_Chat *chat = gc_get_group_by_public_key(onion_c->gc_session, + onion_c->friends_list[num - 1].gc_public_key); + + if (!chat) { + return 1; + } + + int offset = 2 + ONION_PING_ID_SIZE + len_nodes; + int gc_announces_count = unpack_announces_list(plain + offset, plain_size - offset, + announces, MAX_SENT_ANNOUNCES, nullptr); + + if (gc_announces_count == -1) { + return 1; + } + + int added_peers = add_peers_from_announces(onion_c->gc_session, chat, announces, gc_announces_count); + + if (added_peers < 0) { + return 1; + } + } + +#endif + + // TODO: LAN vs non LAN ips?, if we are connected only to LAN, are we offline? onion_c->last_packet_recv = mono_time_get(onion_c->mono_time); return 0; } @@ -1849,12 +1794,20 @@ void do_onion_client(Onion_Client *onion_c) onion_c->last_run = mono_time_get(onion_c->mono_time); } -Onion_Client *new_onion_client(Mono_Time *mono_time, Net_Crypto *c) +Onion_Client *new_onion_client(Mono_Time *mono_time, Net_Crypto *c, GC_Session *gc_session) { - if (c == nullptr) { + if (!c) { + return nullptr; + } + +#ifndef VANILLA_NACL + + if (gc_session == nullptr) { return nullptr; } +#endif + Onion_Client *onion_c = (Onion_Client *)calloc(1, sizeof(Onion_Client)); if (onion_c == nullptr) { @@ -1868,6 +1821,7 @@ Onion_Client *new_onion_client(Mono_Time *mono_time, Net_Crypto *c) return nullptr; } + onion_c->gc_session = gc_session; onion_c->mono_time = mono_time; onion_c->dht = nc_get_dht(c); onion_c->net = dht_get_net(onion_c->dht); diff --git a/toxcore/onion_client.h b/toxcore/onion_client.h index 5b6bea659a..3f84936a8d 100644 --- a/toxcore/onion_client.h +++ b/toxcore/onion_client.h @@ -13,6 +13,7 @@ #include "net_crypto.h" #include "onion_announce.h" #include "ping_array.h" +#include "group_chats.h" #define MAX_ONION_CLIENTS 8 #define MAX_ONION_CLIENTS_ANNOUNCE 12 // Number of nodes to announce ourselves to. @@ -39,15 +40,14 @@ #define MAX_PATH_NODES 32 -/* If no announce response packets are received within this interval tox will - * be considered offline. We give time for a node to be pinged often enough - * that it times out, which leads to the network being thoroughly tested as it - * is replaced. +#define GC_MAX_DATA_LENGTH GC_PUBLIC_ANNOUNCE_MAX_SIZE + +/* If no packets are received within that interval tox will + * be considered offline. */ #define ONION_OFFLINE_TIMEOUT (ONION_NODE_PING_INTERVAL * (ONION_NODE_MAX_PINGS+2)) /* Onion data packet ids. */ -#define ONION_DATA_FRIEND_REQ CRYPTO_PACKET_FRIEND_REQ #define ONION_DATA_DHTPK CRYPTO_PACKET_DHTPK typedef struct Onion_Client Onion_Client; @@ -171,7 +171,7 @@ void oniondata_registerhandler(Onion_Client *onion_c, uint8_t byte, oniondata_ha void do_onion_client(Onion_Client *onion_c); -Onion_Client *new_onion_client(Mono_Time *mono_time, Net_Crypto *c); +Onion_Client *new_onion_client(Mono_Time *mono_time, Net_Crypto *c, GC_Session *gc_session); void kill_onion_client(Onion_Client *onion_c); @@ -182,4 +182,119 @@ void kill_onion_client(Onion_Client *onion_c); */ unsigned int onion_connection_status(const Onion_Client *onion_c); +typedef struct Onion_Node { + uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; + IP_Port ip_port; + uint8_t ping_id[ONION_PING_ID_SIZE]; + uint8_t data_public_key[CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t is_stored; + + uint64_t added_time; + + uint64_t timestamp; + + uint64_t last_pinged; + + uint8_t unsuccessful_pings; + + uint32_t path_used; +} Onion_Node; + +typedef struct Onion_Client_Paths { + Onion_Path paths[NUMBER_ONION_PATHS]; + uint64_t last_path_success[NUMBER_ONION_PATHS]; + uint64_t last_path_used[NUMBER_ONION_PATHS]; + uint64_t path_creation_time[NUMBER_ONION_PATHS]; + /* number of times used without success. */ + unsigned int last_path_used_times[NUMBER_ONION_PATHS]; +} Onion_Client_Paths; + +typedef struct Last_Pinged { + uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; + uint64_t timestamp; +} Last_Pinged; + +typedef struct Onion_Friend { + uint8_t status; /* 0 if friend is not valid, 1 if friend is valid.*/ + uint8_t is_online; /* Set by the onion_set_friend_status function. */ + + uint8_t know_dht_public_key; /* 0 if we don't know the dht public key of the other, 1 if we do. */ + uint8_t dht_public_key[CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t real_public_key[CRYPTO_PUBLIC_KEY_SIZE]; + + Onion_Node clients_list[MAX_ONION_CLIENTS]; + uint8_t temp_public_key[CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t temp_secret_key[CRYPTO_SECRET_KEY_SIZE]; + + uint64_t last_dht_pk_onion_sent; + uint64_t last_dht_pk_dht_sent; + + uint64_t last_noreplay; + + uint64_t last_seen; + + Last_Pinged last_pinged[MAX_STORED_PINGED_NODES]; + uint8_t last_pinged_index; + + recv_tcp_relay_cb *tcp_relay_node_callback; + void *tcp_relay_node_callback_object; + uint32_t tcp_relay_node_callback_number; + + onion_dht_pk_cb *dht_pk_callback; + void *dht_pk_callback_object; + uint32_t dht_pk_callback_number; + + uint32_t run_count; + + uint8_t gc_data[GC_MAX_DATA_LENGTH]; + uint8_t gc_public_key[ENC_PUBLIC_KEY]; + int16_t gc_data_length; +} Onion_Friend; + +typedef struct Onion_Data_Handler { + oniondata_handler_cb *function; + void *object; +} Onion_Data_Handler; + +struct Onion_Client { + Mono_Time *mono_time; + + DHT *dht; + Net_Crypto *c; + GC_Session *gc_session; + Networking_Core *net; + Onion_Friend *friends_list; + uint16_t num_friends; + + Onion_Node clients_announce_list[MAX_ONION_CLIENTS_ANNOUNCE]; + uint64_t last_announce; + + Onion_Client_Paths onion_paths_self; + Onion_Client_Paths onion_paths_friends; + + uint8_t secret_symmetric_key[CRYPTO_SYMMETRIC_KEY_SIZE]; + uint64_t last_run; + uint64_t first_run; + + uint8_t temp_public_key[CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t temp_secret_key[CRYPTO_SECRET_KEY_SIZE]; + + Last_Pinged last_pinged[MAX_STORED_PINGED_NODES]; + + Node_format path_nodes[MAX_PATH_NODES]; + uint16_t path_nodes_index; + + Node_format path_nodes_bs[MAX_PATH_NODES]; + uint16_t path_nodes_index_bs; + + Ping_Array *announce_ping_array; + uint8_t last_pinged_index; + Onion_Data_Handler onion_data_handlers[256]; + + uint64_t last_packet_recv; + + unsigned int onion_connected; + bool udp_connected; +}; + #endif diff --git a/toxcore/tox.api.h b/toxcore/tox.api.h index ee51eb786b..c29a6922c4 100644 --- a/toxcore/tox.api.h +++ b/toxcore/tox.api.h @@ -2854,6 +2854,8 @@ namespace group { * Size of a peer public key. */ const PEER_PUBLIC_KEY_SIZE = 32; + + const MAX_PEER_LENGTH = 128; } /******************************************************************************* @@ -2894,13 +2896,13 @@ namespace group { */ enum class ROLE { /** - * May kick and ban all other peers as well as set their role to anything (except founder). + * May kick all other peers as well as set their role to anything (except founder). * Founders may also set the group password, toggle the privacy state, and set the peer limit. */ FOUNDER, /** - * May kick, ban and set the user and observer roles for peers below this role. + * May kick and set the user and observer roles for peers below this role. * May also set the group topic. */ MODERATOR, @@ -2924,6 +2926,9 @@ namespace group { * ******************************************************************************/ +namespace groups { + const void get_list(uint32_t *list); +} namespace group { @@ -2939,18 +2944,22 @@ namespace group { * the group will attempt to announce itself to the DHT and anyone with the Chat ID may join. * Otherwise a friend invite will be required to join the group. * @param group_name The name of the group. The name must be non-NULL. - * @param length The length of the group name. This must be greater than zero and no larger than + * @param group_name_length The length of the group name. This must be greater than zero and no larger than * $MAX_GROUP_NAME_LENGTH. + * @param name The name of the peer creating the group. + * @param name_length The length of the peer's name. This must be greater than zero and no larger + * than $MAX_NAME_LENGTH. * - * @return groupnumber on success, UINT32_MAX on failure. + * @return group_number on success, UINT32_MAX on failure. */ - uint32_t new(PRIVACY_STATE privacy_state, const uint8_t[length <= MAX_GROUP_NAME_LENGTH] group_name) { + uint32_t new(PRIVACY_STATE privacy_state, const uint8_t[group_name_length <= MAX_GROUP_NAME_LENGTH] group_name, + const uint8_t[name_length <= MAX_NAME_LENGTH] name) { /** - * The group name exceeded $MAX_GROUP_NAME_LENGTH. + * name exceeds $MAX_NAME_LENGTH or group_name exceeded $MAX_GROUP_NAME_LENGTH. */ TOO_LONG, /** - * group_name is NULL or length is zero. + * name or group_name is NULL or length is zero. */ EMPTY, /** @@ -2981,12 +2990,16 @@ namespace group { * * @param chat_id The Chat ID of the group you wish to join. This must be $CHAT_ID_SIZE bytes. * @param password The password required to join the group. Set to NULL if no password is required. - * @param length The length of the password. If length is equal to zero, + * @param password_length The length of the password. If length is equal to zero, * the password parameter is ignored. length must be no larger than $MAX_PASSWORD_SIZE. + * @param name The name of the peer joining the group. + * @param name_length The length of the peer's name. This must be greater than zero and no larger + * than $MAX_NAME_LENGTH. * - * @return groupnumber on success, UINT32_MAX on failure. + * @return group_number on success, UINT32_MAX on failure. */ - uint32_t join(const uint8_t[CHAT_ID_SIZE] chat_id, const uint8_t[length <= MAX_PASSWORD_SIZE] password) { + uint32_t join(const uint8_t[CHAT_ID_SIZE] chat_id, const uint8_t[name_length <= MAX_NAME_LENGTH] name, + const uint8_t[password_length <= MAX_PASSWORD_SIZE] password) { /** * The group instance failed to initialize. */ @@ -2997,9 +3010,36 @@ namespace group { */ BAD_CHAT_ID, /** - * Password length exceeded $MAX_PASSWORD_SIZE. + * name is NULL or name_length is zero. + */ + EMPTY, + /** + * name exceeds $MAX_NAME_LENGTH. */ TOO_LONG, + /** + * Failed to set password. This usually occurs if the password exceeds $MAX_PASSWORD_SIZE. + */ + PASSWORD, + /** + * There was a core error when initiating the group. + */ + CORE, + } + + bool is_connected(uint32_t group_number) { + GROUP_NOT_FOUND, + } + + bool disconnect(uint32_t group_number) { + GROUP_NOT_FOUND, + ALREADY_DISCONNECTED, + ERROR, + } + + error for peer_list_query { + GROUP_NOT_FOUND, + PARAMETER_IS_NULL, } /** @@ -3008,15 +3048,16 @@ namespace group { * This function disconnects from all peers in the group, then attempts to reconnect with the group. * The caller's state is not changed (i.e. name, status, role, chat public key etc.) * - * @param groupnumber The group number of the group we wish to reconnect to. + * @param group_number The group number of the group we wish to reconnect to. * * @return true on success. */ - bool reconnect(uint32_t groupnumber) { + bool reconnect(uint32_t group_number) { /** * The group number passed did not designate a valid group. */ GROUP_NOT_FOUND, + MALLOC, } /** @@ -3026,14 +3067,14 @@ namespace group { * peers in a group, and deletes the group from the chat array. All group state information is permanently * lost, including keys and role credentials. * - * @param groupnumber The group number of the group we wish to leave. + * @param group_number The group number of the group we wish to leave. * @param message The parting message to be sent to all the peers. Set to NULL if we do not wish to * send a parting message. * @param length The length of the parting message. Set to 0 if we do not wish to send a parting message. * * @return true if the group chat instance is successfully deleted. */ - bool leave(uint32_t groupnumber, const uint8_t[length <= MAX_PART_LENGTH] message) { + bool leave(uint32_t group_number, const uint8_t[length <= MAX_PART_LENGTH] message) { /** * The group number passed did not designate a valid group. */ @@ -3051,7 +3092,6 @@ namespace group { */ DELETE_FAIL, } - } /******************************************************************************* @@ -3060,6 +3100,7 @@ namespace group { * ******************************************************************************/ + namespace group { inline namespace self { @@ -3113,18 +3154,18 @@ namespace group { * * @return true on success. */ - set(uint32_t groupnumber) with error for self_name_set; + set(uint32_t group_number) with error for self_name_set; /** * Return the length of the client's current nickname for the group instance designated - * by groupnumber as passed to $set. + * by group_number as passed to $set. * * If no nickname was set before calling this function, the name is empty, * and this function returns 0. * * @see threading for concurrency implications. */ - size(uint32_t groupnumber) with error for self_query; + size(uint32_t group_number) with error for self_query; /** * Write the nickname set by $set to a byte array. @@ -3139,7 +3180,7 @@ namespace group { * * @returns true on success. */ - get(uint32_t groupnumber) with error for self_query; + get(uint32_t group_number) with error for self_query; } /** @@ -3167,13 +3208,13 @@ namespace group { * * @return true on success. */ - set(uint32_t groupnumber) with error for self_status_set; + set(uint32_t group_number) with error for self_status_set; /** * returns the client's status for the group instance on success. * return value is unspecified on failure. */ - get(uint32_t groupnumber) with error for self_query; + get(uint32_t group_number) with error for self_query; } ROLE role { @@ -3182,7 +3223,7 @@ namespace group { * returns the client's role for the group instance on success. * return value is unspecified on failure. */ - get(uint32_t groupnumber) with error for self_query; + get(uint32_t group_number) with error for self_query; } uint32_t peer_id { @@ -3191,7 +3232,7 @@ namespace group { * returns the client's peer id for the group instance on success. * return value is unspecified on failure. */ - get(uint32_t groupnumber) with error for self_query; + get(uint32_t group_number) with error for self_query; } uint8_t [length] public_key { @@ -3200,7 +3241,7 @@ namespace group { * Write the client's group public key designated by the given group number to a byte array. * * This key will be parmanently tied to the client's identity for this particular group until - * the client explicitly leaves the group or gets kicked/banned. This key is the only way for + * the client explicitly leaves the group or gets kicked. This key is the only way for * other peers to reliably identify the client across client restarts. * * `public_key` should have room for at least $PEER_PUBLIC_KEY_SIZE bytes. @@ -3210,7 +3251,7 @@ namespace group { * * @return true on success. */ - get(uint32_t groupnumber) with error for self_query; + get(uint32_t group_number) with error for self_query; } } @@ -3249,7 +3290,7 @@ namespace group { * The return value is equal to the `length` argument received by the last * `${event name}` callback. */ - size(uint32_t groupnumber, uint32_t peer_id) with error for query; + size(uint32_t group_number, uint32_t peer_id) with error for query; /** * Write the name of the peer designated by the given ID to a byte @@ -3260,13 +3301,13 @@ namespace group { * The data written to `name` is equal to the data received by the last * `${event name}` callback. * - * @param groupnumber The group number of the group we wish to query. + * @param group_number The group number of the group we wish to query. * @param peer_id The ID of the peer whose name we want to retrieve. * @param name A valid memory region large enough to store the friend's name. * * @return true on success. */ - get(uint32_t groupnumber, uint32_t peer_id) with error for query; + get(uint32_t group_number, uint32_t peer_id) with error for query; } USER_STATUS status { @@ -3278,7 +3319,7 @@ namespace group { * The status returned is equal to the last status received through the * `${event status}` callback. */ - get(uint32_t groupnumber, uint32_t peer_id) with error for query; + get(uint32_t group_number, uint32_t peer_id) with error for query; } ROLE role { @@ -3289,7 +3330,7 @@ namespace group { * The role returned is equal to the last role received through the * `${event moderation}` callback. */ - get(uint32_t groupnumber, uint32_t peer_id) with error for query; + get(uint32_t group_number, uint32_t peer_id) with error for query; } uint8_t[length] public_key { @@ -3298,7 +3339,7 @@ namespace group { * Write the group public key with the designated peer_id for the designated group number to public_key. * * This key will be parmanently tied to a particular peer until they explicitly leave the group or - * get kicked/banned, and is the only way to reliably identify the same peer across client restarts. + * get kicked, and is the only way to reliably identify the same peer across client restarts. * * `public_key` should have room for at least $PEER_PUBLIC_KEY_SIZE bytes. * @@ -3307,32 +3348,32 @@ namespace group { * * @return true on success. */ - get(uint32_t groupnumber, uint32_t peer_id) with error for query; + get(uint32_t group_number, uint32_t peer_id) with error for query; } /** * This event is triggered when a peer changes their nickname. */ - event name { + event name const { /** - * @param groupnumber The group number of the group the name change is intended for. + * @param group_number The group number of the group the name change is intended for. * @param peer_id The ID of the peer who has changed their name. * @param name The name data. * @param length The length of the name. */ - typedef void(uint32_t groupnumber, uint32_t peer_id, const uint8_t[length <= MAX_NAME_LENGTH] name); + typedef void(uint32_t group_number, uint32_t peer_id, const uint8_t[length <= MAX_NAME_LENGTH] name); } /** * This event is triggered when a peer changes their status. */ - event status { + event status const { /** - * @param groupnumber The group number of the group the status change is intended for. + * @param group_number The group number of the group the status change is intended for. * @param peer_id The ID of the peer who has changed their status. * @param status The new status of the peer. */ - typedef void(uint32_t groupnumber, uint32_t peer_id, USER_STATUS status); + typedef void(uint32_t group_number, uint32_t peer_id, USER_STATUS status); } } @@ -3381,6 +3422,7 @@ namespace group { * The packet failed to send. */ FAIL_SEND, + GROUP_IS_DISCONNECTED, } uint8_t[length <= MAX_TOPIC_LENGTH] topic { @@ -3393,7 +3435,7 @@ namespace group { * * @returns true on success. */ - set(uint32_t groupnumber) with error for topic_set; + set(uint32_t group_number) with error for topic_set; /** * Return the length of the group topic. If the group number is invalid, the @@ -3402,7 +3444,7 @@ namespace group { * The return value is equal to the `length` argument received by the last * `${event topic}` callback. */ - size(uint32_t groupnumber) with error for state_queries; + size(uint32_t group_number) with error for state_queries; /** * Write the topic designated by the given group number to a byte array. @@ -3417,20 +3459,20 @@ namespace group { * * @return true on success. */ - get(uint32_t groupnumber) with error for state_queries; + get(uint32_t group_number) with error for state_queries; } /** * This event is triggered when a peer changes the group topic. */ - event topic { + event topic const { /** - * @param groupnumber The group number of the group the topic change is intended for. + * @param group_number The group number of the group the topic change is intended for. * @param peer_id The ID of the peer who changed the topic. * @param topic The topic data. * @param length The topic length. */ - typedef void(uint32_t groupnumber, uint32_t peer_id, const uint8_t[length <= MAX_TOPIC_LENGTH] topic); + typedef void(uint32_t group_number, uint32_t peer_id, const uint8_t[length <= MAX_TOPIC_LENGTH] topic); } uint8_t[length <= MAX_TOPIC_LENGTH] name { @@ -3438,7 +3480,7 @@ namespace group { * Return the length of the group name. If the group number is invalid, the * return value is unspecified. */ - size(uint32_t groupnumber) with error for state_queries; + size(uint32_t group_number) with error for state_queries; /** * Write the name of the group designated by the given group number to a byte array. @@ -3450,7 +3492,7 @@ namespace group { * * @return true on success. */ - get(uint32_t groupnumber) with error for state_queries; + get(uint32_t group_number) with error for state_queries; } uint8_t[length] chat_id { @@ -3465,7 +3507,7 @@ namespace group { * * @return true on success. */ - get(uint32_t groupnumber) with error for state_queries; + get(uint32_t group_number) with error for state_queries; } uint32_t number_groups { @@ -3486,18 +3528,18 @@ namespace group { * * @see the `Group chat founder controls` section for the respective set function. */ - get(uint32_t groupnumber) with error for state_queries; + get(uint32_t group_number) with error for state_queries; } /** * This event is triggered when the group founder changes the privacy state. */ - event privacy_state { + event privacy_state const { /** - * @param groupnumber The group number of the group the topic change is intended for. + * @param group_number The group number of the group the topic change is intended for. * @param privacy_state The new privacy state. */ - typedef void(uint32_t groupnumber, PRIVACY_STATE privacy_state); + typedef void(uint32_t group_number, PRIVACY_STATE privacy_state); } uint32_t peer_limit { @@ -3511,18 +3553,18 @@ namespace group { * * @see the `Group chat founder controls` section for the respective set function. */ - get(uint32_t groupnumber) with error for state_queries; + get(uint32_t group_number) with error for state_queries; } /** * This event is triggered when the group founder changes the maximum peer limit. */ - event peer_limit { + event peer_limit const { /** - * @param groupnumber The group number of the group for which the peer limit has changed. + * @param group_number The group number of the group for which the peer limit has changed. * @param peer_limit The new peer limit for the group. */ - typedef void(uint32_t groupnumber, uint32_t peer_limit); + typedef void(uint32_t group_number, uint32_t peer_limit); } uint8_t[length <= MAX_PASSWORD_SIZE] password { @@ -3531,7 +3573,7 @@ namespace group { * Return the length of the group password. If the group number is invalid, the * return value is unspecified. */ - size(uint32_t groupnumber) with error for state_queries; + size(uint32_t group_number) with error for state_queries; /** * Write the password for the group designated by the given group number to a byte array. @@ -3548,19 +3590,19 @@ namespace group { * * @return true on success. */ - get(uint32_t groupnumber) with error for state_queries; + get(uint32_t group_number) with error for state_queries; } /** * This event is triggered when the group founder changes the group password. */ - event password { + event password const { /** - * @param groupnumber The group number of the group for which the password has changed. + * @param group_number The group number of the group for which the password has changed. * @param password The new group password. * @param length The length of the password. */ - typedef void(uint32_t groupnumber, const uint8_t[length <= MAX_PASSWORD_SIZE] password); + typedef void(uint32_t group_number, const uint8_t[length <= MAX_PASSWORD_SIZE] password); } } @@ -3584,7 +3626,7 @@ namespace group { * must be split by the client and sent as separate messages. Other clients can * then reassemble the fragments. Messages may not be empty. * - * @param groupnumber The group number of the group the message is intended for. + * @param group_number The group number of the group the message is intended for. * @param type Message type (normal, action, ...). * @param message A non-NULL pointer to the first element of a byte array * containing the message text. @@ -3592,7 +3634,7 @@ namespace group { * * @return true on success. */ - bool message(uint32_t groupnumber, MESSAGE_TYPE type, const uint8_t[length <= MAX_MESSAGE_LENGTH] message) { + bool message(uint32_t group_number, MESSAGE_TYPE type, const uint8_t[length <= MAX_MESSAGE_LENGTH] message) { /** * The group number passed did not designate a valid group. */ @@ -3617,6 +3659,7 @@ namespace group { * Packet failed to send. */ FAIL_SEND, + GROUP_IS_DISCONNECTED, } /** @@ -3629,7 +3672,7 @@ namespace group { * must be split by the client and sent as separate messages. Other clients can * then reassemble the fragments. Messages may not be empty. * - * @param groupnumber The group number of the group the message is intended for. + * @param group_number The group number of the group the message is intended for. * @param peer_id The ID of the peer the message is intended for. * @param message A non-NULL pointer to the first element of a byte array * containing the message text. @@ -3637,7 +3680,7 @@ namespace group { * * @return true on success. */ - bool private_message(uint32_t groupnumber, uint32_t peer_id, const uint8_t[length <= MAX_MESSAGE_LENGTH] message) { + bool private_message(uint32_t group_number, uint32_t peer_id, MESSAGE_TYPE type, const uint8_t[length <= MAX_MESSAGE_LENGTH] message) { /** * The group number passed did not designate a valid group. */ @@ -3662,6 +3705,8 @@ namespace group { * Packet failed to send. */ FAIL_SEND, + GROUP_IS_DISCONNECTED, + BAD_TYPE, } /** @@ -3677,14 +3722,14 @@ namespace group { * Unless latency is an issue or message reliability is not important, it is recommended that you use * lossless custom packets. * - * @param groupnumber The group number of the group the message is intended for. + * @param group_number The group number of the group the message is intended for. * @param lossless True if the packet should be lossless. * @param data A byte array containing the packet data. * @param length The length of the packet data byte array. * * @return true on success. */ - bool custom_packet(uint32_t groupnumber, bool lossless, const uint8_t[length <= MAX_MESSAGE_LENGTH] data) { + bool custom_packet(uint32_t group_number, bool lossless, const uint8_t[length <= MAX_MESSAGE_LENGTH] data) { /** * The group number passed did not designate a valid group. */ @@ -3701,6 +3746,7 @@ namespace group { * The caller does not have the required permissions to send group messages. */ PERMISSIONS, + GROUP_IS_DISCONNECTED, } } } @@ -3716,41 +3762,41 @@ namespace group { /** * This event is triggered when the client receives a group message. */ - event message { + event message const { /** - * @param groupnumber The group number of the group the message is intended for. + * @param group_number The group number of the group the message is intended for. * @param peer_id The ID of the peer who sent the message. * @param type The type of message (normal, action, ...). * @param message The message data. * @param length The length of the message. */ - typedef void(uint32_t groupnumber, uint32_t peer_id, MESSAGE_TYPE type, const uint8_t[length <= MAX_MESSAGE_LENGTH] message); + typedef void(uint32_t group_number, uint32_t peer_id, MESSAGE_TYPE type, const uint8_t[length <= MAX_MESSAGE_LENGTH] message); } /** * This event is triggered when the client receives a private message. */ - event private_message { + event private_message const { /** - * @param groupnumber The group number of the group the private message is intended for. + * @param group_number The group number of the group the private message is intended for. * @param peer_id The ID of the peer who sent the private message. * @param message The message data. * @param length The length of the message. */ - typedef void(uint32_t groupnumber, uint32_t peer_id, const uint8_t[length <= MAX_MESSAGE_LENGTH] message); + typedef void(uint32_t group_number, uint32_t peer_id, MESSAGE_TYPE type, const uint8_t[length <= MAX_MESSAGE_LENGTH] message); } /** * This event is triggered when the client receives a custom packet. */ - event custom_packet { + event custom_packet const { /** - * @param groupnumber The group number of the group the custom packet is intended for. + * @param group_number The group number of the group the custom packet is intended for. * @param peer_id The ID of the peer who sent the custom packet. * @param data The custom packet data. * @param length The length of the data. */ - typedef void(uint32_t groupnumber, uint32_t peer_id, const uint8_t[length <= MAX_MESSAGE_LENGTH] data); + typedef void(uint32_t group_number, uint32_t peer_id, const uint8_t[length <= MAX_MESSAGE_LENGTH] data); } } @@ -3770,12 +3816,12 @@ namespace group { * * This function creates an invite request packet and pushes it to the send queue. * - * @param groupnumber The group number of the group the message is intended for. + * @param group_number The group number of the group the message is intended for. * @param friend_number The friend number of the friend the invite is intended for. * * @return true on success. */ - bool friend(uint32_t groupnumber, uint32_t friend_number) { + bool friend(uint32_t group_number, uint32_t friend_number) { /** * The group number passed did not designate a valid group. */ @@ -3792,6 +3838,7 @@ namespace group { * Packet failed to send. */ FAIL_SEND, + GROUP_IS_DISCONNECTED, } /** @@ -3800,13 +3847,18 @@ namespace group { * * @param invite_data The invite data received from the `${event invite}` event. * @param length The length of the invite data. + * @param name The name of the peer joining the group. + * @param name_length The length of the peer's name. This must be greater than zero and no larger + * than $MAX_NAME_LENGTH. * @param password The password required to join the group. Set to NULL if no password is required. * @param password_length The length of the password. If password_length is equal to zero, the password * parameter will be ignored. password_length must be no larger than $MAX_PASSWORD_SIZE. * - * @return the groupnumber on success, UINT32_MAX on failure. + * @return the group_number on success, UINT32_MAX on failure. */ - uint32_t accept(const uint8_t[length] invite_data, const uint8_t[password_length <= MAX_PASSWORD_SIZE] password) { + uint32_t accept(uint32_t friend_number, const uint8_t[length] invite_data, + const uint8_t[name_length <= MAX_NAME_LENGTH] name, + const uint8_t[password_length <= MAX_PASSWORD_SIZE] password) { /** * The invite data is not in the expected format. */ @@ -3816,9 +3868,25 @@ namespace group { */ INIT_FAILED, /** - * Password length exceeded $MAX_PASSWORD_SIZE. + * name exceeds $MAX_NAME_LENGTH */ TOO_LONG, + /** + * name is NULL or name_length is zero. + */ + EMPTY, + /** + * Failed to set password. This usually occurs if the password exceeds $MAX_PASSWORD_SIZE. + */ + PASSWORD, + /** + * There was a core error when initiating the group. + */ + CORE, + /** + * Packet failed to send. + */ + FAIL_SEND, } } @@ -3826,51 +3894,51 @@ namespace group { * This event is triggered when the client receives a group invite from a friend. The client must store * invite_data which is used to join the group via tox_group_invite_accept. */ - event invite { + event invite const { /** * @param friend_number The friend number of the contact who sent the invite. * @param invite_data The invite data. * @param length The length of invite_data. */ - typedef void(uint32_t friend_number, const uint8_t[length] invite_data); + typedef void(uint32_t friend_number, const uint8_t[length] invite_data, const uint8_t[group_name_length] group_name); } /** * This event is triggered when a peer other than self joins the group. */ - event peer_join { + event peer_join const { /** - * @param groupnumber The group number of the group in which a new peer has joined. + * @param group_number The group number of the group in which a new peer has joined. * @param peer_id The permanent ID of the new peer. This id should not be relied on for * client behaviour and should be treated as a random value. */ - typedef void(uint32_t groupnumber, uint32_t peer_id); + typedef void(uint32_t group_number, uint32_t peer_id); } /** * This event is triggered when a peer other than self exits the group. */ - event peer_exit { + event peer_exit const { /** - * @param groupnumber The group number of the group in which a peer has left. + * @param group_number The group number of the group in which a peer has left. * @param peer_id The ID of the peer who left the group. This ID no longer designates a valid peer * and cannot be used for API calls. * @param name The nickname of the peer who left the group. * @param part_message The parting message data. * @param length The length of the parting message. */ - typedef void(uint32_t groupnumber, uint32_t peer_id, const uint8_t[name_length <= MAX_NAME_LENGTH] name, const uint8_t[length <= MAX_PART_LENGTH] part_message); + typedef void(uint32_t group_number, uint32_t peer_id, const uint8_t[name_length <= MAX_NAME_LENGTH] name, const uint8_t[length <= MAX_PART_LENGTH] part_message); } /** * This event is triggered when the client has successfully joined a group. Use this to initialize * any group information the client may need. */ - event self_join { + event self_join const { /** - * @param groupnumber The group number of the group that the client has joined. + * @param group_number The group number of the group that the client has joined. */ - typedef void(uint32_t groupnumber); + typedef void(uint32_t group_number); } /** @@ -3903,12 +3971,12 @@ namespace group { /** * This event is triggered when the client fails to join a group. */ - event join_fail { + event join_fail const { /** - * @param groupnumber The group number of the group for which the join has failed. + * @param group_number The group number of the group for which the join has failed. * @param fail_type The type of group rejection. */ - typedef void(uint32_t groupnumber, JOIN_FAIL fail_type); + typedef void(uint32_t group_number, JOIN_FAIL fail_type); } } @@ -3929,13 +3997,13 @@ namespace group { * This function sets the groups password, creates a new group shared state including the change, * and distributes it to the rest of the group. * - * @param groupnumber The group number of the group for which we wish to set the password. + * @param group_number The group number of the group for which we wish to set the password. * @param password The password we want to set. Set password to NULL to unset the password. * @param length The length of the password. length must be no longer than $MAX_PASSWORD_SIZE. * * @return true on success. */ - bool set_password(uint32_t groupnumber, const uint8_t[length <= MAX_PASSWORD_SIZE] password) { + bool set_password(uint32_t group_number, const uint8_t[length <= MAX_PASSWORD_SIZE] password) { /** * The group number passed did not designate a valid group. */ @@ -3952,6 +4020,7 @@ namespace group { * The packet failed to send. */ FAIL_SEND, + GROUP_IS_DISCONNECTED, } /** @@ -3963,12 +4032,12 @@ namespace group { * If an attempt is made to set the privacy state to the same state that the group is already * in, the function call will be successful and no action will be taken. * - * @param groupnumber The group number of the group for which we wish to change the privacy state. + * @param group_number The group number of the group for which we wish to change the privacy state. * @param privacy_state The privacy state we wish to set the group to. * * @return true on success. */ - bool set_privacy_state(uint32_t groupnumber, PRIVACY_STATE privacy_state) { + bool set_privacy_state(uint32_t group_number, PRIVACY_STATE privacy_state) { /** * The group number passed did not designate a valid group. */ @@ -3990,6 +4059,7 @@ namespace group { * The packet failed to send. */ FAIL_SEND, + GROUP_IS_DISCONNECTED, } /** @@ -3998,12 +4068,12 @@ namespace group { * This function sets a limit for the number of peers who may be in the group, creates a new * group shared state including the change, and distributes it to the rest of the group. * - * @param groupnumber The group number of the group for which we wish to set the peer limit. + * @param group_number The group number of the group for which we wish to set the peer limit. * @param max_peers The maximum number of peers to allow in the group. * * @return true on success. */ - bool set_peer_limit(uint32_t groupnumber, uint32_t max_peers) { + bool set_peer_limit(uint32_t group_number, uint32_t max_peers) { /** * The group number passed did not designate a valid group. */ @@ -4021,6 +4091,7 @@ namespace group { * The packet failed to send. */ FAIL_SEND, + GROUP_IS_DISCONNECTED, } } @@ -4037,13 +4108,13 @@ namespace group { /** * Ignore or unignore a peer. * - * @param groupnumber The group number of the group the in which you wish to ignore a peer. + * @param group_number The group number of the group the in which you wish to ignore a peer. * @param peer_id The ID of the peer who shall be ignored or unignored. * @param ignore True to ignore the peer, false to unignore the peer. * * @return true on success. */ - bool toggle_ignore(uint32_t groupnumber, uint32_t peer_id, bool ignore) { + bool toggle_ignore(uint32_t group_number, uint32_t peer_id, bool ignore) { /** * The group number passed did not designate a valid group. */ @@ -4052,6 +4123,10 @@ namespace group { * The ID passed did not designate a valid peer. */ PEER_NOT_FOUND, + /** + * The caller attempted to ignore himself. + */ + SELF, } namespace mod { @@ -4063,13 +4138,13 @@ namespace group { * It will also send a packet to the rest of the group, requesting that they perform * the role reassignment. Note: peers cannot be set to the founder role. * - * @param groupnumber The group number of the group the in which you wish set the peer's role. + * @param group_number The group number of the group the in which you wish set the peer's role. * @param peer_id The ID of the peer whose role you wish to set. * @param role The role you wish to set the peer to. * * @return true on success. */ - bool set_role(uint32_t groupnumber, uint32_t peer_id, ROLE role) { + bool set_role(uint32_t group_number, uint32_t peer_id, ROLE role) { /** * The group number passed did not designate a valid group. */ @@ -4092,22 +4167,25 @@ namespace group { * or if the packet fails to send. */ FAIL_ACTION, + /** + * The caller attempted to set their own role. + */ + SELF, } /** - * Kick/ban a peer. + * Kick a peer. * - * This function will remove a peer from the caller's peer list and optionally add their IP address - * to the ban list. It will also send a packet to all group members requesting them - * to do the same. + * This function will remove a peer from the caller's peer list and send a packet to all + * group members requesting them to do the same. Note: This function will not trigger + * the `${event peer_exit}` event for the caller. * - * @param groupnumber The group number of the group the ban is intended for. - * @param peer_id The ID of the peer who will be kicked and/or added to the ban list. - * @param set_ban Set to true if a ban shall be set on the peer's IP address. + * @param group_number The group number of the group the action is intended for. + * @param peer_id The ID of the peer who will be kicked. * * @return true on success. */ - bool remove_peer(uint32_t groupnumber, uint32_t peer_id, bool set_ban) { + bool kick_peer(uint32_t group_number, uint32_t peer_id) { /** * The group number passed did not designate a valid group. */ @@ -4121,52 +4199,21 @@ namespace group { */ PERMISSIONS, /** - * The peer could not be removed from the group. - * - * If a ban was set, this error indicates that the ban entry could not be created. - * This is usually due to the peer's IP address already occurring in the ban list. It may also - * be due to the entry containing invalid peer information, or a failure to cryptographically - * authenticate the entry. + * The peer could not be kicked from the group. */ FAIL_ACTION, /** * The packet failed to send. */ FAIL_SEND, - } - - /** - * Removes a ban. - * - * This function removes a ban entry from the ban list, and sends a packet to the rest of - * the group requesting that they do the same. - * - * @param groupnumber The group number of the group in which the ban is to be removed. - * @param ban_id The ID of the ban entry that shall be removed. - * - * @return true on success - */ - bool remove_ban(uint32_t groupnumber, uint32_t ban_id) { - /** - * The group number passed did not designate a valid group. - */ - GROUP_NOT_FOUND, - /** - * The caller does not have the required permissions for this action. - */ - PERMISSIONS, /** - * The ban entry could not be removed. This may occur if ban_id does not designate - * a valid ban entry. + * The caller attempted to set their own role. */ - FAIL_ACTION, - /** - * The packet failed to send. - */ - FAIL_SEND, + SELF, } } + /** * Represents moderation events. These should be used with the `${event moderation}` event. */ @@ -4176,11 +4223,6 @@ namespace group { */ KICK, - /** - * A peer has been banned from the group. - */ - BAN, - /** * A peer as been given the observer role. */ @@ -4198,95 +4240,17 @@ namespace group { } /** - * This event is triggered when a moderator or founder executes a moderation event. + * This event is triggered when a moderator or founder executes a moderation event, with the exception + * of the peer who initiates the event. */ - event moderation { + event moderation const { /** - * @param groupnumber The group number of the group the event is intended for. + * @param group_number The group number of the group the event is intended for. * @param source_peer_number The ID of the peer who initiated the event. * @param target_peer_number The ID of the peer who is the target of the event. * @param mod_type The type of event. */ - typedef void(uint32_t groupnumber, uint32_t source_peer_number, uint32_t target_peer_number, MOD_EVENT mod_type); - } - -} - - -/******************************************************************************* - * - * :: Group chat ban list queries - * - ******************************************************************************/ - -namespace group { - - namespace ban { - - /** - * Error codes for group ban list queries. - */ - error for query { - /** - * The group number passed did not designate a valid group. - */ - GROUP_NOT_FOUND, - /** - * The ban_id does not designate a valid ban list entry. - */ - BAD_ID, - } - - uint32_t[size] list { - - /** - * Return the number of entries in the ban list for the group designated by - * the given group number. If the group number is invalid, the return value is unspecified. - */ - size(uint32_t groupnumber) with error for query; - - /** - * Copy a list of valid ban list ID's into an array. - * - * Call $size to determine the number of elements to allocate. - * - * @param list A memory region with enough space to hold the ban list. If - * this parameter is NULL, this function has no effect. - * - * @return true on success. - */ - get(uint32_t groupnumber) with error for query; - } - - uint8_t[length <= MAX_NAME_LENGTH] name { - - /** - * Return the length of the name for the ban list entry designated by ban_id, in the - * group designated by the given group number. If either groupnumber or ban_id is invalid, - * the return value is unspecified. - */ - size(uint32_t groupnumber, uint32_t ban_id) with error for query; - - /** - * Write the name of the ban entry designated by ban_id in the group designated by the - * given group number to a byte array. - * - * Call $size to find out how much memory to allocate for the result. - * - * @return true on success. - */ - get(uint32_t groupnumber, uint32_t ban_id) with error for query; - } - - uint64_t time_set { - - /** - * Return a time stamp indicating the time the ban was set, for the ban list entry - * designated by ban_id, in the group designated by the given group number. - * If either groupnumber or ban_id is invalid, the return value is unspecified. - */ - get(uint32_t groupnumber, uint32_t ban_id) with error for query; - } + typedef void(uint32_t group_number, uint32_t source_peer_number, uint32_t target_peer_number, MOD_EVENT mod_type); } } @@ -4347,9 +4311,9 @@ typedef TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE Tox_Err_Group_Founder_Set_Privac typedef TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT Tox_Err_Group_Founder_Set_Peer_Limit; typedef TOX_ERR_GROUP_TOGGLE_IGNORE Tox_Err_Group_Toggle_Ignore; typedef TOX_ERR_GROUP_MOD_SET_ROLE Tox_Err_Group_Mod_Set_Role; -typedef TOX_ERR_GROUP_MOD_REMOVE_PEER Tox_Err_Group_Mod_Remove_Peer; -typedef TOX_ERR_GROUP_MOD_REMOVE_BAN Tox_Err_Group_Mod_Remove_Ban; -typedef TOX_ERR_GROUP_BAN_QUERY Tox_Err_Group_Ban_Query; +typedef TOX_ERR_GROUP_MOD_KICK_PEER Tox_Err_Group_Mod_Kick_Peer; +typedef TOX_ERR_GROUP_DISCONNECT Tox_Err_Group_Disconnect; +typedef TOX_ERR_GROUP_IS_CONNECTED Tox_Err_Group_Is_Connected; typedef TOX_USER_STATUS Tox_User_Status; typedef TOX_MESSAGE_TYPE Tox_Message_Type; typedef TOX_PROXY_TYPE Tox_Proxy_Type; diff --git a/toxcore/tox.c b/toxcore/tox.c index 349cd434a6..338225a32e 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -357,151 +357,153 @@ static void tox_friend_lossless_packet_handler(Messenger *m, uint32_t friend_num } #ifndef VANILLA_NACL -static void tox_group_peer_name_handler(Messenger *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *name, +static void tox_group_peer_name_handler(Messenger *m, uint32_t group_number, uint32_t peer_id, const uint8_t *name, size_t length, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_peer_name_callback != nullptr) { - tox->group_peer_name_callback(tox, groupnumber, peer_id, name, length, tox->non_const_user_data); + tox->group_peer_name_callback(tox, group_number, peer_id, name, length, tox->non_const_user_data); } } -static void tox_group_peer_status_handler(Messenger *m, uint32_t groupnumber, uint32_t peer_id, unsigned int status, +static void tox_group_peer_status_handler(Messenger *m, uint32_t group_number, uint32_t peer_id, unsigned int status, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_peer_status_callback != nullptr) { - tox->group_peer_status_callback(tox, groupnumber, peer_id, (Tox_User_Status)status, tox->non_const_user_data); + tox->group_peer_status_callback(tox, group_number, peer_id, (Tox_User_Status)status, tox->non_const_user_data); } } -static void tox_group_topic_handler(Messenger *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *topic, +static void tox_group_topic_handler(Messenger *m, uint32_t group_number, uint32_t peer_id, const uint8_t *topic, size_t length, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_topic_callback != nullptr) { - tox->group_topic_callback(tox, groupnumber, peer_id, topic, length, tox->non_const_user_data); + tox->group_topic_callback(tox, group_number, peer_id, topic, length, tox->non_const_user_data); } } -static void tox_group_privacy_state_handler(Messenger *m, uint32_t groupnumber, unsigned int privacy_state, +static void tox_group_privacy_state_handler(Messenger *m, uint32_t group_number, unsigned int privacy_state, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_privacy_state_callback != nullptr) { - tox->group_privacy_state_callback(tox, groupnumber, (Tox_Group_Privacy_State)privacy_state, tox->non_const_user_data); + tox->group_privacy_state_callback(tox, group_number, (Tox_Group_Privacy_State)privacy_state, tox->non_const_user_data); } } -static void tox_group_peer_limit_handler(Messenger *m, uint32_t groupnumber, uint32_t peer_limit, void *user_data) +static void tox_group_peer_limit_handler(Messenger *m, uint32_t group_number, uint32_t peer_limit, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_peer_limit_callback != nullptr) { - tox->group_peer_limit_callback(tox, groupnumber, peer_limit, tox->non_const_user_data); + tox->group_peer_limit_callback(tox, group_number, peer_limit, tox->non_const_user_data); } } -static void tox_group_password_handler(Messenger *m, uint32_t groupnumber, const uint8_t *password, size_t length, +static void tox_group_password_handler(Messenger *m, uint32_t group_number, const uint8_t *password, size_t length, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_password_callback != nullptr) { - tox->group_password_callback(tox, groupnumber, password, length, tox->non_const_user_data); + tox->group_password_callback(tox, group_number, password, length, tox->non_const_user_data); } } -static void tox_group_message_handler(Messenger *m, uint32_t groupnumber, uint32_t peer_id, unsigned int type, +static void tox_group_message_handler(Messenger *m, uint32_t group_number, uint32_t peer_id, unsigned int type, const uint8_t *message, size_t length, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_message_callback != nullptr) { - tox->group_message_callback(tox, groupnumber, peer_id, (Tox_Message_Type)type, message, length, + tox->group_message_callback(tox, group_number, peer_id, (Tox_Message_Type)type, message, length, tox->non_const_user_data); } } -static void tox_group_private_message_handler(Messenger *m, uint32_t groupnumber, uint32_t peer_id, - const uint8_t *message, size_t length, void *user_data) +static void tox_group_private_message_handler(Messenger *m, uint32_t group_number, uint32_t peer_id, + unsigned int type, const uint8_t *message, size_t length, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_private_message_callback != nullptr) { - tox->group_private_message_callback(tox, groupnumber, peer_id, message, length, tox->non_const_user_data); + tox->group_private_message_callback(tox, group_number, peer_id, (Tox_Message_Type)type, message, length, + tox->non_const_user_data); } } -static void tox_group_custom_packet_handler(Messenger *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *data, +static void tox_group_custom_packet_handler(Messenger *m, uint32_t group_number, uint32_t peer_id, const uint8_t *data, size_t length, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_custom_packet_callback != nullptr) { - tox->group_custom_packet_callback(tox, groupnumber, peer_id, data, length, tox->non_const_user_data); + tox->group_custom_packet_callback(tox, group_number, peer_id, data, length, tox->non_const_user_data); } } static void tox_group_invite_handler(Messenger *m, uint32_t friend_number, const uint8_t *invite_data, size_t length, - void *user_data) + const uint8_t *group_name, size_t group_name_length, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_invite_callback != nullptr) { - tox->group_invite_callback(tox, friend_number, invite_data, length, tox->non_const_user_data); + tox->group_invite_callback(tox, friend_number, invite_data, length, group_name, group_name_length, + tox->non_const_user_data); } } -static void tox_group_peer_join_handler(Messenger *m, uint32_t groupnumber, uint32_t peer_id, void *user_data) +static void tox_group_peer_join_handler(Messenger *m, uint32_t group_number, uint32_t peer_id, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_peer_join_callback != nullptr) { - tox->group_peer_join_callback(tox, groupnumber, peer_id, tox->non_const_user_data); + tox->group_peer_join_callback(tox, group_number, peer_id, tox->non_const_user_data); } } -static void tox_group_peer_exit_handler(Messenger *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *name, +static void tox_group_peer_exit_handler(Messenger *m, uint32_t group_number, uint32_t peer_id, const uint8_t *name, size_t name_length, const uint8_t *part_message, size_t length, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_peer_exit_callback != nullptr) { - tox->group_peer_exit_callback(tox, groupnumber, peer_id, name, name_length, part_message, length, + tox->group_peer_exit_callback(tox, group_number, peer_id, name, name_length, part_message, length, tox->non_const_user_data); } } -static void tox_group_self_join_handler(Messenger *m, uint32_t groupnumber, void *user_data) +static void tox_group_self_join_handler(Messenger *m, uint32_t group_number, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_self_join_callback != nullptr) { - tox->group_self_join_callback(tox, groupnumber, tox->non_const_user_data); + tox->group_self_join_callback(tox, group_number, tox->non_const_user_data); } } -static void tox_group_join_fail_handler(Messenger *m, uint32_t groupnumber, unsigned int fail_type, void *user_data) +static void tox_group_join_fail_handler(Messenger *m, uint32_t group_number, unsigned int fail_type, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_join_fail_callback != nullptr) { - tox->group_join_fail_callback(tox, groupnumber, (Tox_Group_Join_Fail)fail_type, tox->non_const_user_data); + tox->group_join_fail_callback(tox, group_number, (Tox_Group_Join_Fail)fail_type, tox->non_const_user_data); } } -static void tox_group_moderation_handler(Messenger *m, uint32_t groupnumber, uint32_t source_peer_number, +static void tox_group_moderation_handler(Messenger *m, uint32_t group_number, uint32_t source_peer_number, uint32_t target_peer_number, unsigned int mod_type, void *user_data) { Tox *tox = (Tox *)user_data; if (tox->group_moderation_callback != nullptr) { - tox->group_moderation_callback(tox, groupnumber, source_peer_number, target_peer_number, (Tox_Group_Mod_Event)mod_type, + tox->group_moderation_callback(tox, group_number, source_peer_number, target_peer_number, (Tox_Group_Mod_Event)mod_type, tox->non_const_user_data); } } @@ -2502,101 +2504,85 @@ uint16_t tox_self_get_tcp_port(const Tox *tox, Tox_Err_Get_Port *error) /* GROUPCHAT FUNCTIONS */ #ifndef VANILLA_NACL -void tox_callback_group_invite(Tox *tox, tox_group_invite_cb *function, void *userdata) +void tox_callback_group_invite(Tox *tox, tox_group_invite_cb *function) { - assert(userdata == nullptr); tox->group_invite_callback = function; } -void tox_callback_group_message(Tox *tox, tox_group_message_cb *function, void *userdata) +void tox_callback_group_message(Tox *tox, tox_group_message_cb *function) { - assert(userdata == nullptr); tox->group_message_callback = function; } -void tox_callback_group_private_message(Tox *tox, tox_group_private_message_cb *function, void *userdata) +void tox_callback_group_private_message(Tox *tox, tox_group_private_message_cb *function) { - assert(userdata == nullptr); tox->group_private_message_callback = function; } -void tox_callback_group_custom_packet(Tox *tox, tox_group_custom_packet_cb *function, void *userdata) +void tox_callback_group_custom_packet(Tox *tox, tox_group_custom_packet_cb *function) { - assert(userdata == nullptr); tox->group_custom_packet_callback = function; } -void tox_callback_group_moderation(Tox *tox, tox_group_moderation_cb *function, void *userdata) +void tox_callback_group_moderation(Tox *tox, tox_group_moderation_cb *function) { - assert(userdata == nullptr); tox->group_moderation_callback = function; } -void tox_callback_group_peer_name(Tox *tox, tox_group_peer_name_cb *function, void *userdata) +void tox_callback_group_peer_name(Tox *tox, tox_group_peer_name_cb *function) { - assert(userdata == nullptr); tox->group_peer_name_callback = function; } -void tox_callback_group_peer_status(Tox *tox, tox_group_peer_status_cb *function, void *userdata) +void tox_callback_group_peer_status(Tox *tox, tox_group_peer_status_cb *function) { - assert(userdata == nullptr); tox->group_peer_status_callback = function; } -void tox_callback_group_topic(Tox *tox, tox_group_topic_cb *function, void *userdata) +void tox_callback_group_topic(Tox *tox, tox_group_topic_cb *function) { - assert(userdata == nullptr); tox->group_topic_callback = function; } -void tox_callback_group_privacy_state(Tox *tox, tox_group_privacy_state_cb *function, void *user_data) +void tox_callback_group_privacy_state(Tox *tox, tox_group_privacy_state_cb *function) { - assert(user_data == nullptr); tox->group_privacy_state_callback = function; } -void tox_callback_group_peer_limit(Tox *tox, tox_group_peer_limit_cb *function, void *user_data) +void tox_callback_group_peer_limit(Tox *tox, tox_group_peer_limit_cb *function) { - assert(user_data == nullptr); tox->group_peer_limit_callback = function; } -void tox_callback_group_password(Tox *tox, tox_group_password_cb *function, void *user_data) +void tox_callback_group_password(Tox *tox, tox_group_password_cb *function) { - assert(user_data == nullptr); tox->group_password_callback = function; } -void tox_callback_group_peer_join(Tox *tox, tox_group_peer_join_cb *function, void *userdata) +void tox_callback_group_peer_join(Tox *tox, tox_group_peer_join_cb *function) { - assert(userdata == nullptr); tox->group_peer_join_callback = function; } -void tox_callback_group_peer_exit(Tox *tox, tox_group_peer_exit_cb *function, void *userdata) +void tox_callback_group_peer_exit(Tox *tox, tox_group_peer_exit_cb *function) { - assert(userdata == nullptr); tox->group_peer_exit_callback = function; } -void tox_callback_group_self_join(Tox *tox, tox_group_self_join_cb *function, void *userdata) +void tox_callback_group_self_join(Tox *tox, tox_group_self_join_cb *function) { - assert(userdata == nullptr); tox->group_self_join_callback = function; } -void tox_callback_group_join_fail(Tox *tox, tox_group_join_fail_cb *function, void *userdata) +void tox_callback_group_join_fail(Tox *tox, tox_group_join_fail_cb *function) { - assert(userdata == nullptr); tox->group_join_fail_callback = function; } -uint32_t tox_group_new(Tox *tox, Tox_Group_Privacy_State privacy_state, const uint8_t *group_name, size_t length, - Tox_Err_Group_New *error) +uint32_t tox_group_new(Tox *tox, Tox_Group_Privacy_State privacy_state, const uint8_t *group_name, + size_t group_name_length, const uint8_t *name, size_t name_length, Tox_Err_Group_New *error) { - Messenger *m = tox->m; - int ret = gc_group_add(m->group_handler, privacy_state, group_name, length); + int ret = gc_group_add(tox->m->group_handler, privacy_state, group_name, group_name_length, name, name_length); if (ret >= 0) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_NEW_OK); @@ -2633,11 +2619,10 @@ uint32_t tox_group_new(Tox *tox, Tox_Group_Privacy_State privacy_state, const ui return UINT32_MAX; } -uint32_t tox_group_join(Tox *tox, const uint8_t *chat_id, const uint8_t *password, size_t length, - Tox_Err_Group_Join *error) +uint32_t tox_group_join(Tox *tox, const uint8_t *chat_id, const uint8_t *name, size_t name_length, + const uint8_t *password, size_t password_length, Tox_Err_Group_Join *error) { - Messenger *m = tox->m; - int ret = gc_group_join(m->group_handler, chat_id, password, length); + int ret = gc_group_join(tox->m->group_handler, chat_id, name, name_length, password, password_length); if (ret >= 0) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_JOIN_OK); @@ -2656,39 +2641,84 @@ uint32_t tox_group_join(Tox *tox, const uint8_t *chat_id, const uint8_t *passwor case -3: SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_JOIN_TOO_LONG); return UINT32_MAX; + + case -4: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_JOIN_EMPTY); + return UINT32_MAX; + + case -5: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_JOIN_PASSWORD); + return UINT32_MAX; + + case -6: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_JOIN_CORE); + return UINT32_MAX; } /* can't happen */ return UINT32_MAX; } -bool tox_group_reconnect(Tox *tox, uint32_t groupnumber, Tox_Err_Group_Reconnect *error) +bool tox_group_is_connected(Tox *tox, uint32_t group_number, Tox_Err_Group_Is_Connected *error) +{ + GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); + + if (chat == nullptr) { + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_IS_CONNECTED_GROUP_NOT_FOUND); + return 0; + } + + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_IS_CONNECTED_OK); + + return chat->connection_state != CS_MANUALLY_DISCONNECTED; +} + +bool tox_group_disconnect(Tox *tox, uint32_t group_number, Tox_Err_Group_Disconnect *error) +{ + GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); + + if (chat == nullptr) { + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_DISCONNECT_GROUP_NOT_FOUND); + return 0; + } + + if (chat->connection_state == CS_MANUALLY_DISCONNECTED) { + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_DISCONNECT_ALREADY_DISCONNECTED); + return 0; + } + + bool disconnection_result = gc_disconnect_from_group(tox->m->group_handler, chat); + SET_ERROR_PARAMETER(error, disconnection_result ? TOX_ERR_GROUP_DISCONNECT_OK : TOX_ERR_GROUP_DISCONNECT_ERROR); + + return disconnection_result; +} + +bool tox_group_reconnect(Tox *tox, uint32_t group_number, Tox_Err_Group_Reconnect *error) { - Messenger *m = tox->m; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_RECONNECT_GROUP_NOT_FOUND); return 0; } - gc_rejoin_group(m->group_handler, chat); - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_RECONNECT_OK); - return 1; + bool reconnection_result = gc_rejoin_group(tox->m->group_handler, chat); + SET_ERROR_PARAMETER(error, reconnection_result ? TOX_ERR_GROUP_RECONNECT_OK : TOX_ERR_GROUP_RECONNECT_MALLOC); + + return reconnection_result; } -bool tox_group_leave(Tox *tox, uint32_t groupnumber, const uint8_t *partmessage, size_t length, +bool tox_group_leave(Tox *tox, uint32_t group_number, const uint8_t *partmessage, size_t length, Tox_Err_Group_Leave *error) { - Messenger *m = tox->m; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_LEAVE_GROUP_NOT_FOUND); return 0; } - int ret = gc_group_exit(m->group_handler, chat, partmessage, length); + int ret = gc_group_exit(tox->m->group_handler, chat, partmessage, length); switch (ret) { case 0: @@ -2712,11 +2742,10 @@ bool tox_group_leave(Tox *tox, uint32_t groupnumber, const uint8_t *partmessage, return 0; } -bool tox_group_self_set_name(Tox *tox, uint32_t groupnumber, const uint8_t *name, size_t length, +bool tox_group_self_set_name(Tox *tox, uint32_t group_number, const uint8_t *name, size_t length, Tox_Err_Group_Self_Name_Set *error) { - Messenger *m = tox->m; - int ret = gc_set_self_nick(m, groupnumber, name, length); + int ret = gc_set_self_nick(tox->m, group_number, name, length); switch (ret) { case 0: @@ -2748,10 +2777,9 @@ bool tox_group_self_set_name(Tox *tox, uint32_t groupnumber, const uint8_t *name return 0; } -size_t tox_group_self_get_name_size(const Tox *tox, uint32_t groupnumber, Tox_Err_Group_Self_Query *error) +size_t tox_group_self_get_name_size(const Tox *tox, uint32_t group_number, Tox_Err_Group_Self_Query *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND); @@ -2762,10 +2790,9 @@ size_t tox_group_self_get_name_size(const Tox *tox, uint32_t groupnumber, Tox_Er return gc_get_self_nick_size(chat); } -bool tox_group_self_get_name(const Tox *tox, uint32_t groupnumber, uint8_t *name, Tox_Err_Group_Self_Query *error) +bool tox_group_self_get_name(const Tox *tox, uint32_t group_number, uint8_t *name, Tox_Err_Group_Self_Query *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND); @@ -2777,11 +2804,10 @@ bool tox_group_self_get_name(const Tox *tox, uint32_t groupnumber, uint8_t *name return 1; } -bool tox_group_self_set_status(Tox *tox, uint32_t groupnumber, Tox_User_Status status, +bool tox_group_self_set_status(Tox *tox, uint32_t group_number, Tox_User_Status status, Tox_Err_Group_Self_Status_Set *error) { - Messenger *m = tox->m; - int ret = gc_set_self_status(m, groupnumber, status); + int ret = gc_set_self_status(tox->m, group_number, status); switch (ret) { case 0: @@ -2805,10 +2831,9 @@ bool tox_group_self_set_status(Tox *tox, uint32_t groupnumber, Tox_User_Status s return 0; } -Tox_User_Status tox_group_self_get_status(const Tox *tox, uint32_t groupnumber, Tox_Err_Group_Self_Query *error) +Tox_User_Status tox_group_self_get_status(const Tox *tox, uint32_t group_number, Tox_Err_Group_Self_Query *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND); @@ -2820,10 +2845,9 @@ Tox_User_Status tox_group_self_get_status(const Tox *tox, uint32_t groupnumber, return (Tox_User_Status)status; } -Tox_Group_Role tox_group_self_get_role(const Tox *tox, uint32_t groupnumber, Tox_Err_Group_Self_Query *error) +Tox_Group_Role tox_group_self_get_role(const Tox *tox, uint32_t group_number, Tox_Err_Group_Self_Query *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND); @@ -2835,10 +2859,9 @@ Tox_Group_Role tox_group_self_get_role(const Tox *tox, uint32_t groupnumber, Tox return (Tox_Group_Role)role; } -uint32_t tox_group_self_get_peer_id(const Tox *tox, uint32_t groupnumber, Tox_Err_Group_Self_Query *error) +uint32_t tox_group_self_get_peer_id(const Tox *tox, uint32_t group_number, Tox_Err_Group_Self_Query *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND); @@ -2849,11 +2872,10 @@ uint32_t tox_group_self_get_peer_id(const Tox *tox, uint32_t groupnumber, Tox_Er return gc_get_self_peer_id(chat); } -bool tox_group_self_get_public_key(const Tox *tox, uint32_t groupnumber, uint8_t *public_key, +bool tox_group_self_get_public_key(const Tox *tox, uint32_t group_number, uint8_t *public_key, Tox_Err_Group_Self_Query *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND); @@ -2865,11 +2887,10 @@ bool tox_group_self_get_public_key(const Tox *tox, uint32_t groupnumber, uint8_t return 1; } -size_t tox_group_peer_get_name_size(const Tox *tox, uint32_t groupnumber, uint32_t peer_id, +size_t tox_group_peer_get_name_size(const Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Err_Group_Peer_Query *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND); @@ -2887,11 +2908,10 @@ size_t tox_group_peer_get_name_size(const Tox *tox, uint32_t groupnumber, uint32 } } -bool tox_group_peer_get_name(const Tox *tox, uint32_t groupnumber, uint32_t peer_id, uint8_t *name, +bool tox_group_peer_get_name(const Tox *tox, uint32_t group_number, uint32_t peer_id, uint8_t *name, Tox_Err_Group_Peer_Query *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND); @@ -2909,11 +2929,10 @@ bool tox_group_peer_get_name(const Tox *tox, uint32_t groupnumber, uint32_t peer return 1; } -Tox_User_Status tox_group_peer_get_status(const Tox *tox, uint32_t groupnumber, uint32_t peer_id, +Tox_User_Status tox_group_peer_get_status(const Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Err_Group_Peer_Query *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND); @@ -2931,11 +2950,10 @@ Tox_User_Status tox_group_peer_get_status(const Tox *tox, uint32_t groupnumber, return (Tox_User_Status)ret; } -Tox_Group_Role tox_group_peer_get_role(const Tox *tox, uint32_t groupnumber, uint32_t peer_id, +Tox_Group_Role tox_group_peer_get_role(const Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Err_Group_Peer_Query *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND); @@ -2953,18 +2971,17 @@ Tox_Group_Role tox_group_peer_get_role(const Tox *tox, uint32_t groupnumber, uin return (Tox_Group_Role)ret; } -bool tox_group_peer_get_public_key(const Tox *tox, uint32_t groupnumber, uint32_t peer_id, uint8_t *public_key, +bool tox_group_peer_get_public_key(const Tox *tox, uint32_t group_number, uint32_t peer_id, uint8_t *public_key, Tox_Err_Group_Peer_Query *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND); return 0; } - int ret = gc_get_peer_public_key(chat, peer_id, public_key); + int ret = gc_get_peer_public_key_by_peer_id(chat, peer_id, public_key); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND); @@ -2975,17 +2992,21 @@ bool tox_group_peer_get_public_key(const Tox *tox, uint32_t groupnumber, uint32_ return 1; } -bool tox_group_set_topic(Tox *tox, uint32_t groupnumber, const uint8_t *topic, size_t length, +bool tox_group_set_topic(Tox *tox, uint32_t group_number, const uint8_t *topic, size_t length, Tox_Err_Group_Topic_Set *error) { - Messenger *m = tox->m; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOPIC_SET_GROUP_NOT_FOUND); return 0; } + if (chat->connection_state == CS_MANUALLY_DISCONNECTED) { + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOPIC_SET_GROUP_IS_DISCONNECTED); + return 0; + } + int ret = gc_set_topic(chat, topic, length); switch (ret) { @@ -3014,10 +3035,9 @@ bool tox_group_set_topic(Tox *tox, uint32_t groupnumber, const uint8_t *topic, s return 0; } -size_t tox_group_get_topic_size(const Tox *tox, uint32_t groupnumber, Tox_Err_Group_State_Queries *error) +size_t tox_group_get_topic_size(const Tox *tox, uint32_t group_number, Tox_Err_Group_State_Queries *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND); @@ -3028,10 +3048,9 @@ size_t tox_group_get_topic_size(const Tox *tox, uint32_t groupnumber, Tox_Err_Gr return gc_get_topic_size(chat); } -bool tox_group_get_topic(const Tox *tox, uint32_t groupnumber, uint8_t *topic, Tox_Err_Group_State_Queries *error) +bool tox_group_get_topic(const Tox *tox, uint32_t group_number, uint8_t *topic, Tox_Err_Group_State_Queries *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND); @@ -3043,10 +3062,9 @@ bool tox_group_get_topic(const Tox *tox, uint32_t groupnumber, uint8_t *topic, T return 1; } -size_t tox_group_get_name_size(const Tox *tox, uint32_t groupnumber, Tox_Err_Group_State_Queries *error) +size_t tox_group_get_name_size(const Tox *tox, uint32_t group_number, Tox_Err_Group_State_Queries *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND); @@ -3057,10 +3075,9 @@ size_t tox_group_get_name_size(const Tox *tox, uint32_t groupnumber, Tox_Err_Gro return gc_get_group_name_size(chat); } -bool tox_group_get_name(const Tox *tox, uint32_t groupnumber, uint8_t *groupname, Tox_Err_Group_State_Queries *error) +bool tox_group_get_name(const Tox *tox, uint32_t group_number, uint8_t *groupname, Tox_Err_Group_State_Queries *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND); @@ -3072,10 +3089,9 @@ bool tox_group_get_name(const Tox *tox, uint32_t groupnumber, uint8_t *groupname return 1; } -bool tox_group_get_chat_id(const Tox *tox, uint32_t groupnumber, uint8_t *chat_id, Tox_Err_Group_State_Queries *error) +bool tox_group_get_chat_id(const Tox *tox, uint32_t group_number, uint8_t *chat_id, Tox_Err_Group_State_Queries *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND); @@ -3089,15 +3105,20 @@ bool tox_group_get_chat_id(const Tox *tox, uint32_t groupnumber, uint8_t *chat_i uint32_t tox_group_get_number_groups(const Tox *tox) { - const Messenger *m = tox->m; - return gc_count_groups(m->group_handler); + return gc_count_groups(tox->m->group_handler); +} + +void tox_groups_get_list(const Tox *tox, uint32_t *list) +{ + const GC_Session *c = tox->m->group_handler; + + gc_copy_groups_numbers(c, list); } -Tox_Group_Privacy_State tox_group_get_privacy_state(const Tox *tox, uint32_t groupnumber, +Tox_Group_Privacy_State tox_group_get_privacy_state(const Tox *tox, uint32_t group_number, Tox_Err_Group_State_Queries *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND); @@ -3109,10 +3130,9 @@ Tox_Group_Privacy_State tox_group_get_privacy_state(const Tox *tox, uint32_t gro return (Tox_Group_Privacy_State)state; } -uint32_t tox_group_get_peer_limit(const Tox *tox, uint32_t groupnumber, Tox_Err_Group_State_Queries *error) +uint32_t tox_group_get_peer_limit(const Tox *tox, uint32_t group_number, Tox_Err_Group_State_Queries *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND); @@ -3123,10 +3143,9 @@ uint32_t tox_group_get_peer_limit(const Tox *tox, uint32_t groupnumber, Tox_Err_ return gc_get_max_peers(chat); } -size_t tox_group_get_password_size(const Tox *tox, uint32_t groupnumber, Tox_Err_Group_State_Queries *error) +size_t tox_group_get_password_size(const Tox *tox, uint32_t group_number, Tox_Err_Group_State_Queries *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND); @@ -3137,10 +3156,10 @@ size_t tox_group_get_password_size(const Tox *tox, uint32_t groupnumber, Tox_Err return gc_get_password_size(chat); } -bool tox_group_get_password(const Tox *tox, uint32_t groupnumber, uint8_t *password, Tox_Err_Group_State_Queries *error) +bool tox_group_get_password(const Tox *tox, uint32_t group_number, uint8_t *password, + Tox_Err_Group_State_Queries *error) { - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND); @@ -3152,17 +3171,21 @@ bool tox_group_get_password(const Tox *tox, uint32_t groupnumber, uint8_t *passw return 1; } -bool tox_group_send_message(Tox *tox, uint32_t groupnumber, Tox_Message_Type type, const uint8_t *message, +bool tox_group_send_message(Tox *tox, uint32_t group_number, Tox_Message_Type type, const uint8_t *message, size_t length, Tox_Err_Group_Send_Message *error) { - const Messenger *m = tox->m; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_MESSAGE_GROUP_NOT_FOUND); return 0; } + if (chat->connection_state == CS_MANUALLY_DISCONNECTED) { + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_MESSAGE_GROUP_IS_DISCONNECTED); + return 0; + } + int ret = gc_send_message(chat, message, length, type); switch (ret) { @@ -3195,18 +3218,22 @@ bool tox_group_send_message(Tox *tox, uint32_t groupnumber, Tox_Message_Type typ return 0; } -bool tox_group_send_private_message(Tox *tox, uint32_t groupnumber, uint32_t peer_id, const uint8_t *message, - size_t length, Tox_Err_Group_Send_Private_Message *error) +bool tox_group_send_private_message(Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Message_Type type, + const uint8_t *message, size_t length, Tox_Err_Group_Send_Private_Message *error) { - const Messenger *m = tox->m; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_GROUP_NOT_FOUND); return 0; } - int ret = gc_send_private_message(chat, peer_id, message, length); + if (chat->connection_state == CS_MANUALLY_DISCONNECTED) { + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_GROUP_IS_DISCONNECTED); + return 0; + } + + int ret = gc_send_private_message(chat, peer_id, type, message, length); switch (ret) { case 0: @@ -3226,10 +3253,14 @@ bool tox_group_send_private_message(Tox *tox, uint32_t groupnumber, uint32_t pee return 0; case -4: - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PERMISSIONS); + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_BAD_TYPE); return 0; case -5: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PERMISSIONS); + return 0; + + case -6: SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_FAIL_SEND); return 0; } @@ -3238,17 +3269,21 @@ bool tox_group_send_private_message(Tox *tox, uint32_t groupnumber, uint32_t pee return 0; } -bool tox_group_send_custom_packet(Tox *tox, uint32_t groupnumber, bool lossless, const uint8_t *data, +bool tox_group_send_custom_packet(Tox *tox, uint32_t group_number, bool lossless, const uint8_t *data, size_t length, Tox_Err_Group_Send_Custom_Packet *error) { - const Messenger *m = tox->m; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PACKET_GROUP_NOT_FOUND); return 0; } + if (chat->connection_state == CS_MANUALLY_DISCONNECTED) { + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PACKET_GROUP_IS_DISCONNECTED); + return 0; + } + int ret = gc_send_custom_packet(chat, lossless, data, length); switch (ret) { @@ -3273,22 +3308,27 @@ bool tox_group_send_custom_packet(Tox *tox, uint32_t groupnumber, bool lossless, return 0; } -bool tox_group_invite_friend(Tox *tox, uint32_t groupnumber, uint32_t friend_number, Tox_Err_Group_Invite_Friend *error) +bool tox_group_invite_friend(Tox *tox, uint32_t group_number, uint32_t friend_number, + Tox_Err_Group_Invite_Friend *error) { - Messenger *m = tox->m; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_FRIEND_GROUP_NOT_FOUND); return 0; } - if (!friend_is_valid(m, friend_number)) { + if (chat->connection_state == CS_MANUALLY_DISCONNECTED) { + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_FRIEND_GROUP_IS_DISCONNECTED); + return 0; + } + + if (!friend_is_valid(tox->m, friend_number)) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_FRIEND_FRIEND_NOT_FOUND); return 0; } - int ret = gc_invite_friend(m->group_handler, chat, friend_number, + int ret = gc_invite_friend(tox->m->group_handler, chat, friend_number, send_group_invite_packet); switch (ret) { @@ -3313,11 +3353,12 @@ bool tox_group_invite_friend(Tox *tox, uint32_t groupnumber, uint32_t friend_num return 0; } -uint32_t tox_group_invite_accept(Tox *tox, const uint8_t *invite_data, size_t length, const uint8_t *password, +uint32_t tox_group_invite_accept(Tox *tox, uint32_t friend_number, const uint8_t *invite_data, size_t length, + const uint8_t *name, size_t name_length, const uint8_t *password, size_t password_length, Tox_Err_Group_Invite_Accept *error) { - Messenger *m = tox->m; - int ret = gc_accept_invite(m->group_handler, invite_data, length, password, password_length); + int ret = gc_accept_invite(tox->m->group_handler, friend_number, invite_data, length, name, name_length, password, + password_length); if (ret >= 0) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_OK); @@ -3336,23 +3377,43 @@ uint32_t tox_group_invite_accept(Tox *tox, const uint8_t *invite_data, size_t le case -3: SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG); return UINT32_MAX; + + case -4: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_EMPTY); + return UINT32_MAX; + + case -5: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_PASSWORD); + return UINT32_MAX; + + case -6: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_CORE); + return UINT32_MAX; + + case -7: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_FAIL_SEND); + return UINT32_MAX; } /* can't happen */ return UINT32_MAX; } -bool tox_group_founder_set_password(Tox *tox, uint32_t groupnumber, const uint8_t *password, size_t length, +bool tox_group_founder_set_password(Tox *tox, uint32_t group_number, const uint8_t *password, size_t length, Tox_Err_Group_Founder_Set_Password *error) { - Messenger *m = tox->m; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_GROUP_NOT_FOUND); return 0; } + if (chat->connection_state == CS_MANUALLY_DISCONNECTED) { + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_GROUP_IS_DISCONNECTED); + return 0; + } + int ret = gc_founder_set_password(chat, password, length); switch (ret) { @@ -3377,11 +3438,10 @@ bool tox_group_founder_set_password(Tox *tox, uint32_t groupnumber, const uint8_ return 0; } -bool tox_group_founder_set_privacy_state(Tox *tox, uint32_t groupnumber, Tox_Group_Privacy_State privacy_state, +bool tox_group_founder_set_privacy_state(Tox *tox, uint32_t group_number, Tox_Group_Privacy_State privacy_state, Tox_Err_Group_Founder_Set_Privacy_State *error) { - Messenger *m = tox->m; - int ret = gc_founder_set_privacy_state(m, groupnumber, privacy_state); + int ret = gc_founder_set_privacy_state(tox->m, group_number, privacy_state); switch (ret) { case 0: @@ -3401,10 +3461,14 @@ bool tox_group_founder_set_privacy_state(Tox *tox, uint32_t groupnumber, Tox_Gro return 0; case -4: - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SET); + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_GROUP_IS_DISCONNECTED); return 0; case -5: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SET); + return 0; + + case -6: SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SEND); return 0; } @@ -3413,18 +3477,22 @@ bool tox_group_founder_set_privacy_state(Tox *tox, uint32_t groupnumber, Tox_Gro return 0; } -bool tox_group_founder_set_peer_limit(Tox *tox, uint32_t groupnumber, uint32_t maxpeers, +bool tox_group_founder_set_peer_limit(Tox *tox, uint32_t group_number, uint32_t maxpeers, Tox_Err_Group_Founder_Set_Peer_Limit *error) { - Messenger *m = tox->m; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_GROUP_NOT_FOUND); return 0; } - int ret = gc_founder_set_max_peers(chat, groupnumber, maxpeers); + if (chat->connection_state == CS_MANUALLY_DISCONNECTED) { + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_GROUP_IS_DISCONNECTED); + return 0; + } + + int ret = gc_founder_set_max_peers(chat, maxpeers); switch (ret) { case 0: @@ -3448,11 +3516,10 @@ bool tox_group_founder_set_peer_limit(Tox *tox, uint32_t groupnumber, uint32_t m return 0; } -bool tox_group_toggle_ignore(Tox *tox, uint32_t groupnumber, uint32_t peer_id, bool ignore, +bool tox_group_toggle_ignore(Tox *tox, uint32_t group_number, uint32_t peer_id, bool ignore, Tox_Err_Group_Toggle_Ignore *error) { - Messenger *m = tox->m; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); + GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number); if (chat == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOGGLE_IGNORE_GROUP_NOT_FOUND); @@ -3461,20 +3528,28 @@ bool tox_group_toggle_ignore(Tox *tox, uint32_t groupnumber, uint32_t peer_id, b int ret = gc_toggle_ignore(chat, peer_id, ignore); - if (ret == -1) { - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOGGLE_IGNORE_PEER_NOT_FOUND); - return 0; - } else { - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOGGLE_IGNORE_OK); - return 1; + switch (ret) { + case 0: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOGGLE_IGNORE_OK); + return 1; + + case -1: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOGGLE_IGNORE_PEER_NOT_FOUND); + return 0; + + case -2: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOGGLE_IGNORE_SELF); + return 0; } + + /* can't happen */ + return 0; } -bool tox_group_mod_set_role(Tox *tox, uint32_t groupnumber, uint32_t peer_id, Tox_Group_Role role, +bool tox_group_mod_set_role(Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Group_Role role, Tox_Err_Group_Mod_Set_Role *error) { - Messenger *m = tox->m; - int ret = gc_set_peer_role(m, groupnumber, peer_id, role); + int ret = gc_set_peer_role(tox->m, group_number, peer_id, role); switch (ret) { case 0: @@ -3500,75 +3575,47 @@ bool tox_group_mod_set_role(Tox *tox, uint32_t groupnumber, uint32_t peer_id, To case -5: SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_SET_ROLE_FAIL_ACTION); return 0; + + case -6: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_SET_ROLE_SELF); + return 0; } /* can't happen */ return 0; } -bool tox_group_mod_remove_peer(Tox *tox, uint32_t groupnumber, uint32_t peer_id, bool set_ban, - Tox_Err_Group_Mod_Remove_Peer *error) +bool tox_group_mod_kick_peer(Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Err_Group_Mod_Kick_Peer *error) { - Messenger *m = tox->m; - int ret = gc_remove_peer(m, groupnumber, peer_id, set_ban); + int ret = gc_kick_peer(tox->m, group_number, peer_id); switch (ret) { case 0: - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_REMOVE_PEER_OK); + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_KICK_PEER_OK); return 1; case -1: - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_REMOVE_PEER_GROUP_NOT_FOUND); + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_KICK_PEER_GROUP_NOT_FOUND); return 0; case -2: - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_REMOVE_PEER_PEER_NOT_FOUND); + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_KICK_PEER_PEER_NOT_FOUND); return 0; case -3: - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_REMOVE_PEER_PERMISSIONS); + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_KICK_PEER_PERMISSIONS); return 0; case -4: - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_ACTION); + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_KICK_PEER_FAIL_ACTION); return 0; case -5: - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_SEND); + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_KICK_PEER_FAIL_SEND); return 0; - } - /* can't happen */ - return 0; -} - -bool tox_group_mod_remove_ban(Tox *tox, uint32_t groupnumber, uint32_t ban_id, Tox_Err_Group_Mod_Remove_Ban *error) -{ - Messenger *m = tox->m; - GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); - - if (chat == nullptr) { - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_REMOVE_BAN_GROUP_NOT_FOUND); - return 0; - } - - int ret = gc_remove_ban(chat, ban_id); - - switch (ret) { - case 0: - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_REMOVE_BAN_OK); - return 1; - - case -1: - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_REMOVE_BAN_PERMISSIONS); - return 0; - - case -2: - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_ACTION); - return 0; - - case -3: - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_SEND); + case -6: + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_MOD_KICK_PEER_SELF); return 0; } @@ -3576,98 +3623,4 @@ bool tox_group_mod_remove_ban(Tox *tox, uint32_t groupnumber, uint32_t ban_id, T return 0; } -size_t tox_group_ban_get_list_size(const Tox *tox, uint32_t groupnumber, Tox_Err_Group_Ban_Query *error) -{ - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); - - if (chat == nullptr) { - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_GROUP_NOT_FOUND); - return -1; - } - - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_OK); - return sanctions_list_num_banned(chat); -} - -bool tox_group_ban_get_list(const Tox *tox, uint32_t groupnumber, uint32_t *list, Tox_Err_Group_Ban_Query *error) -{ - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); - - if (chat == nullptr) { - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_GROUP_NOT_FOUND); - return 0; - } - - sanctions_list_get_ban_list(chat, list); - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_OK); - return 1; -} - -size_t tox_group_ban_get_name_size(const Tox *tox, uint32_t groupnumber, uint32_t ban_id, - Tox_Err_Group_Ban_Query *error) -{ - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); - - if (chat == nullptr) { - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_GROUP_NOT_FOUND); - return -1; - } - - uint16_t ret = sanctions_list_get_ban_nick_length(chat, ban_id); - - if (ret == 0) { - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_BAD_ID); - return -1; - } - - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_OK); - return ret; -} - -bool tox_group_ban_get_name(const Tox *tox, uint32_t groupnumber, uint32_t ban_id, uint8_t *name, - Tox_Err_Group_Ban_Query *error) -{ - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); - - if (chat == nullptr) { - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_GROUP_NOT_FOUND); - return 0; - } - - int ret = sanctions_list_get_ban_nick(chat, ban_id, name); - - if (ret == -1) { - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_BAD_ID); - return 0; - } - - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_OK); - return 1; -} - -uint64_t tox_group_ban_get_time_set(const Tox *tox, uint32_t groupnumber, uint32_t ban_id, - Tox_Err_Group_Ban_Query *error) -{ - const Messenger *m = tox->m; - const GC_Chat *chat = gc_get_group(m->group_handler, groupnumber); - - if (chat == nullptr) { - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_GROUP_NOT_FOUND); - return -1; - } - - uint64_t ret = sanctions_list_get_ban_time_set(chat, ban_id); - - if (ret == 0) { - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_BAD_ID); - return -1; - } - - SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BAN_QUERY_OK); - return ret; -} #endif /* VANILLA_NACL */ diff --git a/toxcore/tox.h b/toxcore/tox.h index ffad5cd236..b13abe2706 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h @@ -3270,6 +3270,10 @@ uint32_t tox_group_chat_id_size(void); uint32_t tox_group_peer_public_key_size(void); +#define TOX_GROUP_MAX_PEER_LENGTH 128 + +uint32_t tox_group_max_peer_length(void); + /******************************************************************************* * @@ -3313,13 +3317,13 @@ typedef enum TOX_GROUP_PRIVACY_STATE { typedef enum TOX_GROUP_ROLE { /** - * May kick and ban all other peers as well as set their role to anything (except founder). + * May kick all other peers as well as set their role to anything (except founder). * Founders may also set the group password, toggle the privacy state, and set the peer limit. */ TOX_GROUP_ROLE_FOUNDER, /** - * May kick, ban and set the user and observer roles for peers below this role. + * May kick and set the user and observer roles for peers below this role. * May also set the group topic. */ TOX_GROUP_ROLE_MODERATOR, @@ -3346,6 +3350,8 @@ typedef enum TOX_GROUP_ROLE { +void tox_groups_get_list(const Tox *tox, uint32_t *list); + typedef enum TOX_ERR_GROUP_NEW { /** @@ -3354,12 +3360,12 @@ typedef enum TOX_ERR_GROUP_NEW { TOX_ERR_GROUP_NEW_OK, /** - * The group name exceeded TOX_GROUP_MAX_GROUP_NAME_LENGTH. + * name exceeds TOX_MAX_NAME_LENGTH or group_name exceeded TOX_GROUP_MAX_GROUP_NAME_LENGTH. */ TOX_ERR_GROUP_NEW_TOO_LONG, /** - * group_name is NULL or length is zero. + * name or group_name is NULL or length is zero. */ TOX_ERR_GROUP_NEW_EMPTY, @@ -3399,13 +3405,16 @@ typedef enum TOX_ERR_GROUP_NEW { * the group will attempt to announce itself to the DHT and anyone with the Chat ID may join. * Otherwise a friend invite will be required to join the group. * @param group_name The name of the group. The name must be non-NULL. - * @param length The length of the group name. This must be greater than zero and no larger than + * @param group_name_length The length of the group name. This must be greater than zero and no larger than * TOX_GROUP_MAX_GROUP_NAME_LENGTH. + * @param name The name of the peer creating the group. + * @param name_length The length of the peer's name. This must be greater than zero and no larger + * than TOX_MAX_NAME_LENGTH. * - * @return groupnumber on success, UINT32_MAX on failure. + * @return group_number on success, UINT32_MAX on failure. */ -uint32_t tox_group_new(Tox *tox, TOX_GROUP_PRIVACY_STATE privacy_state, const uint8_t *group_name, size_t length, - TOX_ERR_GROUP_NEW *error); +uint32_t tox_group_new(Tox *tox, TOX_GROUP_PRIVACY_STATE privacy_state, const uint8_t *group_name, + size_t group_name_length, const uint8_t *name, size_t name_length, TOX_ERR_GROUP_NEW *error); typedef enum TOX_ERR_GROUP_JOIN { @@ -3426,10 +3435,25 @@ typedef enum TOX_ERR_GROUP_JOIN { TOX_ERR_GROUP_JOIN_BAD_CHAT_ID, /** - * Password length exceeded TOX_GROUP_MAX_PASSWORD_SIZE. + * name is NULL or name_length is zero. + */ + TOX_ERR_GROUP_JOIN_EMPTY, + + /** + * name exceeds TOX_MAX_NAME_LENGTH. */ TOX_ERR_GROUP_JOIN_TOO_LONG, + /** + * Failed to set password. This usually occurs if the password exceeds TOX_GROUP_MAX_PASSWORD_SIZE. + */ + TOX_ERR_GROUP_JOIN_PASSWORD, + + /** + * There was a core error when initiating the group. + */ + TOX_ERR_GROUP_JOIN_CORE, + } TOX_ERR_GROUP_JOIN; @@ -3442,13 +3466,80 @@ typedef enum TOX_ERR_GROUP_JOIN { * * @param chat_id The Chat ID of the group you wish to join. This must be TOX_GROUP_CHAT_ID_SIZE bytes. * @param password The password required to join the group. Set to NULL if no password is required. - * @param length The length of the password. If length is equal to zero, + * @param password_length The length of the password. If length is equal to zero, * the password parameter is ignored. length must be no larger than TOX_GROUP_MAX_PASSWORD_SIZE. + * @param name The name of the peer joining the group. + * @param name_length The length of the peer's name. This must be greater than zero and no larger + * than TOX_MAX_NAME_LENGTH. * - * @return groupnumber on success, UINT32_MAX on failure. + * @return group_number on success, UINT32_MAX on failure. */ -uint32_t tox_group_join(Tox *tox, const uint8_t *chat_id, const uint8_t *password, size_t length, - TOX_ERR_GROUP_JOIN *error); +uint32_t tox_group_join(Tox *tox, const uint8_t *chat_id, const uint8_t *name, size_t name_length, + const uint8_t *password, size_t password_length, TOX_ERR_GROUP_JOIN *error); + +typedef enum TOX_ERR_GROUP_IS_CONNECTED { + + /** + * The function returned successfully. + */ + TOX_ERR_GROUP_IS_CONNECTED_OK, + + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_IS_CONNECTED_GROUP_NOT_FOUND, + +} TOX_ERR_GROUP_IS_CONNECTED; + + +bool tox_group_is_connected(Tox *tox, uint32_t group_number, TOX_ERR_GROUP_IS_CONNECTED *error); + +typedef enum TOX_ERR_GROUP_DISCONNECT { + + /** + * The function returned successfully. + */ + TOX_ERR_GROUP_DISCONNECT_OK, + + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_DISCONNECT_GROUP_NOT_FOUND, + + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_DISCONNECT_ALREADY_DISCONNECTED, + + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_DISCONNECT_ERROR, + +} TOX_ERR_GROUP_DISCONNECT; + + +bool tox_group_disconnect(Tox *tox, uint32_t group_number, TOX_ERR_GROUP_DISCONNECT *error); + +typedef enum TOX_ERR_GROUP_PEER_LIST_QUERY { + + /** + * The function returned successfully. + */ + TOX_ERR_GROUP_PEER_LIST_QUERY_OK, + + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_PEER_LIST_QUERY_GROUP_NOT_FOUND, + + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_PEER_LIST_QUERY_PARAMETER_IS_NULL, + +} TOX_ERR_GROUP_PEER_LIST_QUERY; + typedef enum TOX_ERR_GROUP_RECONNECT { @@ -3462,6 +3553,11 @@ typedef enum TOX_ERR_GROUP_RECONNECT { */ TOX_ERR_GROUP_RECONNECT_GROUP_NOT_FOUND, + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_RECONNECT_MALLOC, + } TOX_ERR_GROUP_RECONNECT; @@ -3471,11 +3567,11 @@ typedef enum TOX_ERR_GROUP_RECONNECT { * This function disconnects from all peers in the group, then attempts to reconnect with the group. * The caller's state is not changed (i.e. name, status, role, chat public key etc.) * - * @param groupnumber The group number of the group we wish to reconnect to. + * @param group_number The group number of the group we wish to reconnect to. * * @return true on success. */ -bool tox_group_reconnect(Tox *tox, uint32_t groupnumber, TOX_ERR_GROUP_RECONNECT *error); +bool tox_group_reconnect(Tox *tox, uint32_t group_number, TOX_ERR_GROUP_RECONNECT *error); typedef enum TOX_ERR_GROUP_LEAVE { @@ -3514,14 +3610,15 @@ typedef enum TOX_ERR_GROUP_LEAVE { * peers in a group, and deletes the group from the chat array. All group state information is permanently * lost, including keys and role credentials. * - * @param groupnumber The group number of the group we wish to leave. + * @param group_number The group number of the group we wish to leave. * @param message The parting message to be sent to all the peers. Set to NULL if we do not wish to * send a parting message. * @param length The length of the parting message. Set to 0 if we do not wish to send a parting message. * * @return true if the group chat instance is successfully deleted. */ -bool tox_group_leave(Tox *tox, uint32_t groupnumber, const uint8_t *message, size_t length, TOX_ERR_GROUP_LEAVE *error); +bool tox_group_leave(Tox *tox, uint32_t group_number, const uint8_t *message, size_t length, + TOX_ERR_GROUP_LEAVE *error); /******************************************************************************* @@ -3599,19 +3696,19 @@ typedef enum TOX_ERR_GROUP_SELF_NAME_SET { * * @return true on success. */ -bool tox_group_self_set_name(Tox *tox, uint32_t groupnumber, const uint8_t *name, size_t length, +bool tox_group_self_set_name(Tox *tox, uint32_t group_number, const uint8_t *name, size_t length, TOX_ERR_GROUP_SELF_NAME_SET *error); /** * Return the length of the client's current nickname for the group instance designated - * by groupnumber as passed to tox_group_self_set_name. + * by group_number as passed to tox_group_self_set_name. * * If no nickname was set before calling this function, the name is empty, * and this function returns 0. * * @see threading for concurrency implications. */ -size_t tox_group_self_get_name_size(const Tox *tox, uint32_t groupnumber, TOX_ERR_GROUP_SELF_QUERY *error); +size_t tox_group_self_get_name_size(const Tox *tox, uint32_t group_number, TOX_ERR_GROUP_SELF_QUERY *error); /** * Write the nickname set by tox_group_self_set_name to a byte array. @@ -3626,7 +3723,7 @@ size_t tox_group_self_get_name_size(const Tox *tox, uint32_t groupnumber, TOX_ER * * @returns true on success. */ -bool tox_group_self_get_name(const Tox *tox, uint32_t groupnumber, uint8_t *name, TOX_ERR_GROUP_SELF_QUERY *error); +bool tox_group_self_get_name(const Tox *tox, uint32_t group_number, uint8_t *name, TOX_ERR_GROUP_SELF_QUERY *error); /** * Error codes for self status setting. @@ -3661,32 +3758,32 @@ typedef enum TOX_ERR_GROUP_SELF_STATUS_SET { * * @return true on success. */ -bool tox_group_self_set_status(Tox *tox, uint32_t groupnumber, TOX_USER_STATUS status, +bool tox_group_self_set_status(Tox *tox, uint32_t group_number, TOX_USER_STATUS status, TOX_ERR_GROUP_SELF_STATUS_SET *error); /** * returns the client's status for the group instance on success. * return value is unspecified on failure. */ -TOX_USER_STATUS tox_group_self_get_status(const Tox *tox, uint32_t groupnumber, TOX_ERR_GROUP_SELF_QUERY *error); +TOX_USER_STATUS tox_group_self_get_status(const Tox *tox, uint32_t group_number, TOX_ERR_GROUP_SELF_QUERY *error); /** * returns the client's role for the group instance on success. * return value is unspecified on failure. */ -TOX_GROUP_ROLE tox_group_self_get_role(const Tox *tox, uint32_t groupnumber, TOX_ERR_GROUP_SELF_QUERY *error); +TOX_GROUP_ROLE tox_group_self_get_role(const Tox *tox, uint32_t group_number, TOX_ERR_GROUP_SELF_QUERY *error); /** * returns the client's peer id for the group instance on success. * return value is unspecified on failure. */ -uint32_t tox_group_self_get_peer_id(const Tox *tox, uint32_t groupnumber, TOX_ERR_GROUP_SELF_QUERY *error); +uint32_t tox_group_self_get_peer_id(const Tox *tox, uint32_t group_number, TOX_ERR_GROUP_SELF_QUERY *error); /** * Write the client's group public key designated by the given group number to a byte array. * * This key will be parmanently tied to the client's identity for this particular group until - * the client explicitly leaves the group or gets kicked/banned. This key is the only way for + * the client explicitly leaves the group or gets kicked. This key is the only way for * other peers to reliably identify the client across client restarts. * * `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. @@ -3696,7 +3793,7 @@ uint32_t tox_group_self_get_peer_id(const Tox *tox, uint32_t groupnumber, TOX_ER * * @return true on success. */ -bool tox_group_self_get_public_key(const Tox *tox, uint32_t groupnumber, uint8_t *public_key, +bool tox_group_self_get_public_key(const Tox *tox, uint32_t group_number, uint8_t *public_key, TOX_ERR_GROUP_SELF_QUERY *error); @@ -3738,7 +3835,7 @@ typedef enum TOX_ERR_GROUP_PEER_QUERY { * The return value is equal to the `length` argument received by the last * `group_peer_name` callback. */ -size_t tox_group_peer_get_name_size(const Tox *tox, uint32_t groupnumber, uint32_t peer_id, +size_t tox_group_peer_get_name_size(const Tox *tox, uint32_t group_number, uint32_t peer_id, TOX_ERR_GROUP_PEER_QUERY *error); /** @@ -3750,13 +3847,13 @@ size_t tox_group_peer_get_name_size(const Tox *tox, uint32_t groupnumber, uint32 * The data written to `name` is equal to the data received by the last * `group_peer_name` callback. * - * @param groupnumber The group number of the group we wish to query. + * @param group_number The group number of the group we wish to query. * @param peer_id The ID of the peer whose name we want to retrieve. * @param name A valid memory region large enough to store the friend's name. * * @return true on success. */ -bool tox_group_peer_get_name(const Tox *tox, uint32_t groupnumber, uint32_t peer_id, uint8_t *name, +bool tox_group_peer_get_name(const Tox *tox, uint32_t group_number, uint32_t peer_id, uint8_t *name, TOX_ERR_GROUP_PEER_QUERY *error); /** @@ -3766,7 +3863,7 @@ bool tox_group_peer_get_name(const Tox *tox, uint32_t groupnumber, uint32_t peer * The status returned is equal to the last status received through the * `group_peer_status` callback. */ -TOX_USER_STATUS tox_group_peer_get_status(const Tox *tox, uint32_t groupnumber, uint32_t peer_id, +TOX_USER_STATUS tox_group_peer_get_status(const Tox *tox, uint32_t group_number, uint32_t peer_id, TOX_ERR_GROUP_PEER_QUERY *error); /** @@ -3776,14 +3873,14 @@ TOX_USER_STATUS tox_group_peer_get_status(const Tox *tox, uint32_t groupnumber, * The role returned is equal to the last role received through the * `group_moderation` callback. */ -TOX_GROUP_ROLE tox_group_peer_get_role(const Tox *tox, uint32_t groupnumber, uint32_t peer_id, +TOX_GROUP_ROLE tox_group_peer_get_role(const Tox *tox, uint32_t group_number, uint32_t peer_id, TOX_ERR_GROUP_PEER_QUERY *error); /** * Write the group public key with the designated peer_id for the designated group number to public_key. * * This key will be parmanently tied to a particular peer until they explicitly leave the group or - * get kicked/banned, and is the only way to reliably identify the same peer across client restarts. + * get kicked, and is the only way to reliably identify the same peer across client restarts. * * `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. * @@ -3792,16 +3889,16 @@ TOX_GROUP_ROLE tox_group_peer_get_role(const Tox *tox, uint32_t groupnumber, uin * * @return true on success. */ -bool tox_group_peer_get_public_key(const Tox *tox, uint32_t groupnumber, uint32_t peer_id, uint8_t *public_key, +bool tox_group_peer_get_public_key(const Tox *tox, uint32_t group_number, uint32_t peer_id, uint8_t *public_key, TOX_ERR_GROUP_PEER_QUERY *error); /** - * @param groupnumber The group number of the group the name change is intended for. + * @param group_number The group number of the group the name change is intended for. * @param peer_id The ID of the peer who has changed their name. * @param name The name data. * @param length The length of the name. */ -typedef void tox_group_peer_name_cb(Tox *tox, uint32_t groupnumber, uint32_t peer_id, const uint8_t *name, +typedef void tox_group_peer_name_cb(Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *name, size_t length, void *user_data); @@ -3810,14 +3907,14 @@ typedef void tox_group_peer_name_cb(Tox *tox, uint32_t groupnumber, uint32_t pee * * This event is triggered when a peer changes their nickname. */ -void tox_callback_group_peer_name(Tox *tox, tox_group_peer_name_cb *callback, void *user_data); +void tox_callback_group_peer_name(Tox *tox, tox_group_peer_name_cb *callback); /** - * @param groupnumber The group number of the group the status change is intended for. + * @param group_number The group number of the group the status change is intended for. * @param peer_id The ID of the peer who has changed their status. * @param status The new status of the peer. */ -typedef void tox_group_peer_status_cb(Tox *tox, uint32_t groupnumber, uint32_t peer_id, TOX_USER_STATUS status, +typedef void tox_group_peer_status_cb(Tox *tox, uint32_t group_number, uint32_t peer_id, TOX_USER_STATUS status, void *user_data); @@ -3826,7 +3923,7 @@ typedef void tox_group_peer_status_cb(Tox *tox, uint32_t groupnumber, uint32_t p * * This event is triggered when a peer changes their status. */ -void tox_callback_group_peer_status(Tox *tox, tox_group_peer_status_cb *callback, void *user_data); +void tox_callback_group_peer_status(Tox *tox, tox_group_peer_status_cb *callback); /******************************************************************************* @@ -3890,6 +3987,11 @@ typedef enum TOX_ERR_GROUP_TOPIC_SET { */ TOX_ERR_GROUP_TOPIC_SET_FAIL_SEND, + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_TOPIC_SET_GROUP_IS_DISCONNECTED, + } TOX_ERR_GROUP_TOPIC_SET; @@ -3901,7 +4003,7 @@ typedef enum TOX_ERR_GROUP_TOPIC_SET { * * @returns true on success. */ -bool tox_group_set_topic(Tox *tox, uint32_t groupnumber, const uint8_t *topic, size_t length, +bool tox_group_set_topic(Tox *tox, uint32_t group_number, const uint8_t *topic, size_t length, TOX_ERR_GROUP_TOPIC_SET *error); /** @@ -3911,7 +4013,7 @@ bool tox_group_set_topic(Tox *tox, uint32_t groupnumber, const uint8_t *topic, s * The return value is equal to the `length` argument received by the last * `group_topic` callback. */ -size_t tox_group_get_topic_size(const Tox *tox, uint32_t groupnumber, TOX_ERR_GROUP_STATE_QUERIES *error); +size_t tox_group_get_topic_size(const Tox *tox, uint32_t group_number, TOX_ERR_GROUP_STATE_QUERIES *error); /** * Write the topic designated by the given group number to a byte array. @@ -3926,15 +4028,15 @@ size_t tox_group_get_topic_size(const Tox *tox, uint32_t groupnumber, TOX_ERR_GR * * @return true on success. */ -bool tox_group_get_topic(const Tox *tox, uint32_t groupnumber, uint8_t *topic, TOX_ERR_GROUP_STATE_QUERIES *error); +bool tox_group_get_topic(const Tox *tox, uint32_t group_number, uint8_t *topic, TOX_ERR_GROUP_STATE_QUERIES *error); /** - * @param groupnumber The group number of the group the topic change is intended for. + * @param group_number The group number of the group the topic change is intended for. * @param peer_id The ID of the peer who changed the topic. * @param topic The topic data. * @param length The topic length. */ -typedef void tox_group_topic_cb(Tox *tox, uint32_t groupnumber, uint32_t peer_id, const uint8_t *topic, size_t length, +typedef void tox_group_topic_cb(Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *topic, size_t length, void *user_data); @@ -3943,13 +4045,13 @@ typedef void tox_group_topic_cb(Tox *tox, uint32_t groupnumber, uint32_t peer_id * * This event is triggered when a peer changes the group topic. */ -void tox_callback_group_topic(Tox *tox, tox_group_topic_cb *callback, void *user_data); +void tox_callback_group_topic(Tox *tox, tox_group_topic_cb *callback); /** * Return the length of the group name. If the group number is invalid, the * return value is unspecified. */ -size_t tox_group_get_name_size(const Tox *tox, uint32_t groupnumber, TOX_ERR_GROUP_STATE_QUERIES *error); +size_t tox_group_get_name_size(const Tox *tox, uint32_t group_number, TOX_ERR_GROUP_STATE_QUERIES *error); /** * Write the name of the group designated by the given group number to a byte array. @@ -3961,7 +4063,7 @@ size_t tox_group_get_name_size(const Tox *tox, uint32_t groupnumber, TOX_ERR_GRO * * @return true on success. */ -bool tox_group_get_name(const Tox *tox, uint32_t groupnumber, uint8_t *name, TOX_ERR_GROUP_STATE_QUERIES *error); +bool tox_group_get_name(const Tox *tox, uint32_t group_number, uint8_t *name, TOX_ERR_GROUP_STATE_QUERIES *error); /** * Write the Chat ID designated by the given group number to a byte array. @@ -3973,7 +4075,7 @@ bool tox_group_get_name(const Tox *tox, uint32_t groupnumber, uint8_t *name, TOX * * @return true on success. */ -bool tox_group_get_chat_id(const Tox *tox, uint32_t groupnumber, uint8_t *chat_id, TOX_ERR_GROUP_STATE_QUERIES *error); +bool tox_group_get_chat_id(const Tox *tox, uint32_t group_number, uint8_t *chat_id, TOX_ERR_GROUP_STATE_QUERIES *error); /** * Return the number of groups in the Tox chats array. @@ -3989,14 +4091,14 @@ uint32_t tox_group_get_number_groups(const Tox *tox); * * @see the `Group chat founder controls` section for the respective set function. */ -TOX_GROUP_PRIVACY_STATE tox_group_get_privacy_state(const Tox *tox, uint32_t groupnumber, +TOX_GROUP_PRIVACY_STATE tox_group_get_privacy_state(const Tox *tox, uint32_t group_number, TOX_ERR_GROUP_STATE_QUERIES *error); /** - * @param groupnumber The group number of the group the topic change is intended for. + * @param group_number The group number of the group the topic change is intended for. * @param privacy_state The new privacy state. */ -typedef void tox_group_privacy_state_cb(Tox *tox, uint32_t groupnumber, TOX_GROUP_PRIVACY_STATE privacy_state, +typedef void tox_group_privacy_state_cb(Tox *tox, uint32_t group_number, TOX_GROUP_PRIVACY_STATE privacy_state, void *user_data); @@ -4005,7 +4107,7 @@ typedef void tox_group_privacy_state_cb(Tox *tox, uint32_t groupnumber, TOX_GROU * * This event is triggered when the group founder changes the privacy state. */ -void tox_callback_group_privacy_state(Tox *tox, tox_group_privacy_state_cb *callback, void *user_data); +void tox_callback_group_privacy_state(Tox *tox, tox_group_privacy_state_cb *callback); /** * Return the maximum number of peers allowed for the group designated by the given group number. @@ -4016,13 +4118,13 @@ void tox_callback_group_privacy_state(Tox *tox, tox_group_privacy_state_cb *call * * @see the `Group chat founder controls` section for the respective set function. */ -uint32_t tox_group_get_peer_limit(const Tox *tox, uint32_t groupnumber, TOX_ERR_GROUP_STATE_QUERIES *error); +uint32_t tox_group_get_peer_limit(const Tox *tox, uint32_t group_number, TOX_ERR_GROUP_STATE_QUERIES *error); /** - * @param groupnumber The group number of the group for which the peer limit has changed. + * @param group_number The group number of the group for which the peer limit has changed. * @param peer_limit The new peer limit for the group. */ -typedef void tox_group_peer_limit_cb(Tox *tox, uint32_t groupnumber, uint32_t peer_limit, void *user_data); +typedef void tox_group_peer_limit_cb(Tox *tox, uint32_t group_number, uint32_t peer_limit, void *user_data); /** @@ -4030,13 +4132,13 @@ typedef void tox_group_peer_limit_cb(Tox *tox, uint32_t groupnumber, uint32_t pe * * This event is triggered when the group founder changes the maximum peer limit. */ -void tox_callback_group_peer_limit(Tox *tox, tox_group_peer_limit_cb *callback, void *user_data); +void tox_callback_group_peer_limit(Tox *tox, tox_group_peer_limit_cb *callback); /** * Return the length of the group password. If the group number is invalid, the * return value is unspecified. */ -size_t tox_group_get_password_size(const Tox *tox, uint32_t groupnumber, TOX_ERR_GROUP_STATE_QUERIES *error); +size_t tox_group_get_password_size(const Tox *tox, uint32_t group_number, TOX_ERR_GROUP_STATE_QUERIES *error); /** * Write the password for the group designated by the given group number to a byte array. @@ -4053,15 +4155,15 @@ size_t tox_group_get_password_size(const Tox *tox, uint32_t groupnumber, TOX_ERR * * @return true on success. */ -bool tox_group_get_password(const Tox *tox, uint32_t groupnumber, uint8_t *password, +bool tox_group_get_password(const Tox *tox, uint32_t group_number, uint8_t *password, TOX_ERR_GROUP_STATE_QUERIES *error); /** - * @param groupnumber The group number of the group for which the password has changed. + * @param group_number The group number of the group for which the password has changed. * @param password The new group password. * @param length The length of the password. */ -typedef void tox_group_password_cb(Tox *tox, uint32_t groupnumber, const uint8_t *password, size_t length, +typedef void tox_group_password_cb(Tox *tox, uint32_t group_number, const uint8_t *password, size_t length, void *user_data); @@ -4070,7 +4172,7 @@ typedef void tox_group_password_cb(Tox *tox, uint32_t groupnumber, const uint8_t * * This event is triggered when the group founder changes the group password. */ -void tox_callback_group_password(Tox *tox, tox_group_password_cb *callback, void *user_data); +void tox_callback_group_password(Tox *tox, tox_group_password_cb *callback); /******************************************************************************* @@ -4118,6 +4220,11 @@ typedef enum TOX_ERR_GROUP_SEND_MESSAGE { */ TOX_ERR_GROUP_SEND_MESSAGE_FAIL_SEND, + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_SEND_MESSAGE_GROUP_IS_DISCONNECTED, + } TOX_ERR_GROUP_SEND_MESSAGE; @@ -4131,7 +4238,7 @@ typedef enum TOX_ERR_GROUP_SEND_MESSAGE { * must be split by the client and sent as separate messages. Other clients can * then reassemble the fragments. Messages may not be empty. * - * @param groupnumber The group number of the group the message is intended for. + * @param group_number The group number of the group the message is intended for. * @param type Message type (normal, action, ...). * @param message A non-NULL pointer to the first element of a byte array * containing the message text. @@ -4139,7 +4246,7 @@ typedef enum TOX_ERR_GROUP_SEND_MESSAGE { * * @return true on success. */ -bool tox_group_send_message(Tox *tox, uint32_t groupnumber, TOX_MESSAGE_TYPE type, const uint8_t *message, +bool tox_group_send_message(Tox *tox, uint32_t group_number, TOX_MESSAGE_TYPE type, const uint8_t *message, size_t length, TOX_ERR_GROUP_SEND_MESSAGE *error); typedef enum TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE { @@ -4179,6 +4286,16 @@ typedef enum TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE { */ TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_FAIL_SEND, + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_GROUP_IS_DISCONNECTED, + + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_BAD_TYPE, + } TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE; @@ -4192,7 +4309,7 @@ typedef enum TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE { * must be split by the client and sent as separate messages. Other clients can * then reassemble the fragments. Messages may not be empty. * - * @param groupnumber The group number of the group the message is intended for. + * @param group_number The group number of the group the message is intended for. * @param peer_id The ID of the peer the message is intended for. * @param message A non-NULL pointer to the first element of a byte array * containing the message text. @@ -4200,8 +4317,8 @@ typedef enum TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE { * * @return true on success. */ -bool tox_group_send_private_message(Tox *tox, uint32_t groupnumber, uint32_t peer_id, const uint8_t *message, - size_t length, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE *error); +bool tox_group_send_private_message(Tox *tox, uint32_t group_number, uint32_t peer_id, TOX_MESSAGE_TYPE type, + const uint8_t *message, size_t length, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE *error); typedef enum TOX_ERR_GROUP_SEND_CUSTOM_PACKET { @@ -4230,6 +4347,11 @@ typedef enum TOX_ERR_GROUP_SEND_CUSTOM_PACKET { */ TOX_ERR_GROUP_SEND_CUSTOM_PACKET_PERMISSIONS, + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_SEND_CUSTOM_PACKET_GROUP_IS_DISCONNECTED, + } TOX_ERR_GROUP_SEND_CUSTOM_PACKET; @@ -4246,14 +4368,14 @@ typedef enum TOX_ERR_GROUP_SEND_CUSTOM_PACKET { * Unless latency is an issue or message reliability is not important, it is recommended that you use * lossless custom packets. * - * @param groupnumber The group number of the group the message is intended for. + * @param group_number The group number of the group the message is intended for. * @param lossless True if the packet should be lossless. * @param data A byte array containing the packet data. * @param length The length of the packet data byte array. * * @return true on success. */ -bool tox_group_send_custom_packet(Tox *tox, uint32_t groupnumber, bool lossless, const uint8_t *data, size_t length, +bool tox_group_send_custom_packet(Tox *tox, uint32_t group_number, bool lossless, const uint8_t *data, size_t length, TOX_ERR_GROUP_SEND_CUSTOM_PACKET *error); @@ -4266,13 +4388,13 @@ bool tox_group_send_custom_packet(Tox *tox, uint32_t groupnumber, bool lossless, /** - * @param groupnumber The group number of the group the message is intended for. + * @param group_number The group number of the group the message is intended for. * @param peer_id The ID of the peer who sent the message. * @param type The type of message (normal, action, ...). * @param message The message data. * @param length The length of the message. */ -typedef void tox_group_message_cb(Tox *tox, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type, +typedef void tox_group_message_cb(Tox *tox, uint32_t group_number, uint32_t peer_id, TOX_MESSAGE_TYPE type, const uint8_t *message, size_t length, void *user_data); @@ -4281,16 +4403,16 @@ typedef void tox_group_message_cb(Tox *tox, uint32_t groupnumber, uint32_t peer_ * * This event is triggered when the client receives a group message. */ -void tox_callback_group_message(Tox *tox, tox_group_message_cb *callback, void *user_data); +void tox_callback_group_message(Tox *tox, tox_group_message_cb *callback); /** - * @param groupnumber The group number of the group the private message is intended for. + * @param group_number The group number of the group the private message is intended for. * @param peer_id The ID of the peer who sent the private message. * @param message The message data. * @param length The length of the message. */ -typedef void tox_group_private_message_cb(Tox *tox, uint32_t groupnumber, uint32_t peer_id, const uint8_t *message, - size_t length, void *user_data); +typedef void tox_group_private_message_cb(Tox *tox, uint32_t group_number, uint32_t peer_id, TOX_MESSAGE_TYPE type, + const uint8_t *message, size_t length, void *user_data); /** @@ -4298,15 +4420,15 @@ typedef void tox_group_private_message_cb(Tox *tox, uint32_t groupnumber, uint32 * * This event is triggered when the client receives a private message. */ -void tox_callback_group_private_message(Tox *tox, tox_group_private_message_cb *callback, void *user_data); +void tox_callback_group_private_message(Tox *tox, tox_group_private_message_cb *callback); /** - * @param groupnumber The group number of the group the custom packet is intended for. + * @param group_number The group number of the group the custom packet is intended for. * @param peer_id The ID of the peer who sent the custom packet. * @param data The custom packet data. * @param length The length of the data. */ -typedef void tox_group_custom_packet_cb(Tox *tox, uint32_t groupnumber, uint32_t peer_id, const uint8_t *data, +typedef void tox_group_custom_packet_cb(Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *data, size_t length, void *user_data); @@ -4315,7 +4437,7 @@ typedef void tox_group_custom_packet_cb(Tox *tox, uint32_t groupnumber, uint32_t * * This event is triggered when the client receives a custom packet. */ -void tox_callback_group_custom_packet(Tox *tox, tox_group_custom_packet_cb *callback, void *user_data); +void tox_callback_group_custom_packet(Tox *tox, tox_group_custom_packet_cb *callback); /******************************************************************************* @@ -4353,6 +4475,11 @@ typedef enum TOX_ERR_GROUP_INVITE_FRIEND { */ TOX_ERR_GROUP_INVITE_FRIEND_FAIL_SEND, + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_INVITE_FRIEND_GROUP_IS_DISCONNECTED, + } TOX_ERR_GROUP_INVITE_FRIEND; @@ -4361,12 +4488,12 @@ typedef enum TOX_ERR_GROUP_INVITE_FRIEND { * * This function creates an invite request packet and pushes it to the send queue. * - * @param groupnumber The group number of the group the message is intended for. + * @param group_number The group number of the group the message is intended for. * @param friend_number The friend number of the friend the invite is intended for. * * @return true on success. */ -bool tox_group_invite_friend(Tox *tox, uint32_t groupnumber, uint32_t friend_number, +bool tox_group_invite_friend(Tox *tox, uint32_t group_number, uint32_t friend_number, TOX_ERR_GROUP_INVITE_FRIEND *error); typedef enum TOX_ERR_GROUP_INVITE_ACCEPT { @@ -4387,10 +4514,30 @@ typedef enum TOX_ERR_GROUP_INVITE_ACCEPT { TOX_ERR_GROUP_INVITE_ACCEPT_INIT_FAILED, /** - * Password length exceeded TOX_GROUP_MAX_PASSWORD_SIZE. + * name exceeds TOX_MAX_NAME_LENGTH */ TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG, + /** + * name is NULL or name_length is zero. + */ + TOX_ERR_GROUP_INVITE_ACCEPT_EMPTY, + + /** + * Failed to set password. This usually occurs if the password exceeds TOX_GROUP_MAX_PASSWORD_SIZE. + */ + TOX_ERR_GROUP_INVITE_ACCEPT_PASSWORD, + + /** + * There was a core error when initiating the group. + */ + TOX_ERR_GROUP_INVITE_ACCEPT_CORE, + + /** + * Packet failed to send. + */ + TOX_ERR_GROUP_INVITE_ACCEPT_FAIL_SEND, + } TOX_ERR_GROUP_INVITE_ACCEPT; @@ -4400,14 +4547,18 @@ typedef enum TOX_ERR_GROUP_INVITE_ACCEPT { * * @param invite_data The invite data received from the `group_invite` event. * @param length The length of the invite data. + * @param name The name of the peer joining the group. + * @param name_length The length of the peer's name. This must be greater than zero and no larger + * than TOX_MAX_NAME_LENGTH. * @param password The password required to join the group. Set to NULL if no password is required. * @param password_length The length of the password. If password_length is equal to zero, the password * parameter will be ignored. password_length must be no larger than TOX_GROUP_MAX_PASSWORD_SIZE. * - * @return the groupnumber on success, UINT32_MAX on failure. + * @return the group_number on success, UINT32_MAX on failure. */ -uint32_t tox_group_invite_accept(Tox *tox, const uint8_t *invite_data, size_t length, const uint8_t *password, - size_t password_length, TOX_ERR_GROUP_INVITE_ACCEPT *error); +uint32_t tox_group_invite_accept(Tox *tox, uint32_t friend_number, const uint8_t *invite_data, size_t length, + const uint8_t *name, size_t name_length, const uint8_t *password, size_t password_length, + TOX_ERR_GROUP_INVITE_ACCEPT *error); /** * @param friend_number The friend number of the contact who sent the invite. @@ -4415,7 +4566,7 @@ uint32_t tox_group_invite_accept(Tox *tox, const uint8_t *invite_data, size_t le * @param length The length of invite_data. */ typedef void tox_group_invite_cb(Tox *tox, uint32_t friend_number, const uint8_t *invite_data, size_t length, - void *user_data); + const uint8_t *group_name, size_t group_name_length, void *user_data); /** @@ -4424,14 +4575,14 @@ typedef void tox_group_invite_cb(Tox *tox, uint32_t friend_number, const uint8_t * This event is triggered when the client receives a group invite from a friend. The client must store * invite_data which is used to join the group via tox_group_invite_accept. */ -void tox_callback_group_invite(Tox *tox, tox_group_invite_cb *callback, void *user_data); +void tox_callback_group_invite(Tox *tox, tox_group_invite_cb *callback); /** - * @param groupnumber The group number of the group in which a new peer has joined. + * @param group_number The group number of the group in which a new peer has joined. * @param peer_id The permanent ID of the new peer. This id should not be relied on for * client behaviour and should be treated as a random value. */ -typedef void tox_group_peer_join_cb(Tox *tox, uint32_t groupnumber, uint32_t peer_id, void *user_data); +typedef void tox_group_peer_join_cb(Tox *tox, uint32_t group_number, uint32_t peer_id, void *user_data); /** @@ -4439,17 +4590,17 @@ typedef void tox_group_peer_join_cb(Tox *tox, uint32_t groupnumber, uint32_t pee * * This event is triggered when a peer other than self joins the group. */ -void tox_callback_group_peer_join(Tox *tox, tox_group_peer_join_cb *callback, void *user_data); +void tox_callback_group_peer_join(Tox *tox, tox_group_peer_join_cb *callback); /** - * @param groupnumber The group number of the group in which a peer has left. + * @param group_number The group number of the group in which a peer has left. * @param peer_id The ID of the peer who left the group. This ID no longer designates a valid peer * and cannot be used for API calls. * @param name The nickname of the peer who left the group. * @param part_message The parting message data. * @param length The length of the parting message. */ -typedef void tox_group_peer_exit_cb(Tox *tox, uint32_t groupnumber, uint32_t peer_id, const uint8_t *name, +typedef void tox_group_peer_exit_cb(Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *name, size_t name_length, const uint8_t *part_message, size_t length, void *user_data); @@ -4458,12 +4609,12 @@ typedef void tox_group_peer_exit_cb(Tox *tox, uint32_t groupnumber, uint32_t pee * * This event is triggered when a peer other than self exits the group. */ -void tox_callback_group_peer_exit(Tox *tox, tox_group_peer_exit_cb *callback, void *user_data); +void tox_callback_group_peer_exit(Tox *tox, tox_group_peer_exit_cb *callback); /** - * @param groupnumber The group number of the group that the client has joined. + * @param group_number The group number of the group that the client has joined. */ -typedef void tox_group_self_join_cb(Tox *tox, uint32_t groupnumber, void *user_data); +typedef void tox_group_self_join_cb(Tox *tox, uint32_t group_number, void *user_data); /** @@ -4472,7 +4623,7 @@ typedef void tox_group_self_join_cb(Tox *tox, uint32_t groupnumber, void *user_d * This event is triggered when the client has successfully joined a group. Use this to initialize * any group information the client may need. */ -void tox_callback_group_self_join(Tox *tox, tox_group_self_join_cb *callback, void *user_data); +void tox_callback_group_self_join(Tox *tox, tox_group_self_join_cb *callback); /** * Represents types of failed group join attempts. These are used in the tox_callback_group_rejected @@ -4505,10 +4656,10 @@ typedef enum TOX_GROUP_JOIN_FAIL { /** - * @param groupnumber The group number of the group for which the join has failed. + * @param group_number The group number of the group for which the join has failed. * @param fail_type The type of group rejection. */ -typedef void tox_group_join_fail_cb(Tox *tox, uint32_t groupnumber, TOX_GROUP_JOIN_FAIL fail_type, void *user_data); +typedef void tox_group_join_fail_cb(Tox *tox, uint32_t group_number, TOX_GROUP_JOIN_FAIL fail_type, void *user_data); /** @@ -4516,7 +4667,7 @@ typedef void tox_group_join_fail_cb(Tox *tox, uint32_t groupnumber, TOX_GROUP_JO * * This event is triggered when the client fails to join a group. */ -void tox_callback_group_join_fail(Tox *tox, tox_group_join_fail_cb *callback, void *user_data); +void tox_callback_group_join_fail(Tox *tox, tox_group_join_fail_cb *callback); /******************************************************************************* @@ -4554,6 +4705,11 @@ typedef enum TOX_ERR_GROUP_FOUNDER_SET_PASSWORD { */ TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_FAIL_SEND, + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_GROUP_IS_DISCONNECTED, + } TOX_ERR_GROUP_FOUNDER_SET_PASSWORD; @@ -4563,13 +4719,13 @@ typedef enum TOX_ERR_GROUP_FOUNDER_SET_PASSWORD { * This function sets the groups password, creates a new group shared state including the change, * and distributes it to the rest of the group. * - * @param groupnumber The group number of the group for which we wish to set the password. + * @param group_number The group number of the group for which we wish to set the password. * @param password The password we want to set. Set password to NULL to unset the password. * @param length The length of the password. length must be no longer than TOX_GROUP_MAX_PASSWORD_SIZE. * * @return true on success. */ -bool tox_group_founder_set_password(Tox *tox, uint32_t groupnumber, const uint8_t *password, size_t length, +bool tox_group_founder_set_password(Tox *tox, uint32_t group_number, const uint8_t *password, size_t length, TOX_ERR_GROUP_FOUNDER_SET_PASSWORD *error); typedef enum TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE { @@ -4605,6 +4761,11 @@ typedef enum TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE { */ TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SEND, + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_GROUP_IS_DISCONNECTED, + } TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE; @@ -4617,12 +4778,12 @@ typedef enum TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE { * If an attempt is made to set the privacy state to the same state that the group is already * in, the function call will be successful and no action will be taken. * - * @param groupnumber The group number of the group for which we wish to change the privacy state. + * @param group_number The group number of the group for which we wish to change the privacy state. * @param privacy_state The privacy state we wish to set the group to. * * @return true on success. */ -bool tox_group_founder_set_privacy_state(Tox *tox, uint32_t groupnumber, TOX_GROUP_PRIVACY_STATE privacy_state, +bool tox_group_founder_set_privacy_state(Tox *tox, uint32_t group_number, TOX_GROUP_PRIVACY_STATE privacy_state, TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE *error); typedef enum TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT { @@ -4653,6 +4814,11 @@ typedef enum TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT { */ TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_FAIL_SEND, + /** + * TODO: Generate doc + */ + TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_GROUP_IS_DISCONNECTED, + } TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT; @@ -4662,12 +4828,12 @@ typedef enum TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT { * This function sets a limit for the number of peers who may be in the group, creates a new * group shared state including the change, and distributes it to the rest of the group. * - * @param groupnumber The group number of the group for which we wish to set the peer limit. + * @param group_number The group number of the group for which we wish to set the peer limit. * @param max_peers The maximum number of peers to allow in the group. * * @return true on success. */ -bool tox_group_founder_set_peer_limit(Tox *tox, uint32_t groupnumber, uint32_t max_peers, +bool tox_group_founder_set_peer_limit(Tox *tox, uint32_t group_number, uint32_t max_peers, TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT *error); @@ -4696,19 +4862,24 @@ typedef enum TOX_ERR_GROUP_TOGGLE_IGNORE { */ TOX_ERR_GROUP_TOGGLE_IGNORE_PEER_NOT_FOUND, + /** + * The caller attempted to ignore himself. + */ + TOX_ERR_GROUP_TOGGLE_IGNORE_SELF, + } TOX_ERR_GROUP_TOGGLE_IGNORE; /** * Ignore or unignore a peer. * - * @param groupnumber The group number of the group the in which you wish to ignore a peer. + * @param group_number The group number of the group the in which you wish to ignore a peer. * @param peer_id The ID of the peer who shall be ignored or unignored. * @param ignore True to ignore the peer, false to unignore the peer. * * @return true on success. */ -bool tox_group_toggle_ignore(Tox *tox, uint32_t groupnumber, uint32_t peer_id, bool ignore, +bool tox_group_toggle_ignore(Tox *tox, uint32_t group_number, uint32_t peer_id, bool ignore, TOX_ERR_GROUP_TOGGLE_IGNORE *error); typedef enum TOX_ERR_GROUP_MOD_SET_ROLE { @@ -4745,6 +4916,11 @@ typedef enum TOX_ERR_GROUP_MOD_SET_ROLE { */ TOX_ERR_GROUP_MOD_SET_ROLE_FAIL_ACTION, + /** + * The caller attempted to set their own role. + */ + TOX_ERR_GROUP_MOD_SET_ROLE_SELF, + } TOX_ERR_GROUP_MOD_SET_ROLE; @@ -4755,114 +4931,68 @@ typedef enum TOX_ERR_GROUP_MOD_SET_ROLE { * It will also send a packet to the rest of the group, requesting that they perform * the role reassignment. Note: peers cannot be set to the founder role. * - * @param groupnumber The group number of the group the in which you wish set the peer's role. + * @param group_number The group number of the group the in which you wish set the peer's role. * @param peer_id The ID of the peer whose role you wish to set. * @param role The role you wish to set the peer to. * * @return true on success. */ -bool tox_group_mod_set_role(Tox *tox, uint32_t groupnumber, uint32_t peer_id, TOX_GROUP_ROLE role, +bool tox_group_mod_set_role(Tox *tox, uint32_t group_number, uint32_t peer_id, TOX_GROUP_ROLE role, TOX_ERR_GROUP_MOD_SET_ROLE *error); -typedef enum TOX_ERR_GROUP_MOD_REMOVE_PEER { +typedef enum TOX_ERR_GROUP_MOD_KICK_PEER { /** * The function returned successfully. */ - TOX_ERR_GROUP_MOD_REMOVE_PEER_OK, + TOX_ERR_GROUP_MOD_KICK_PEER_OK, /** * The group number passed did not designate a valid group. */ - TOX_ERR_GROUP_MOD_REMOVE_PEER_GROUP_NOT_FOUND, + TOX_ERR_GROUP_MOD_KICK_PEER_GROUP_NOT_FOUND, /** * The ID passed did not designate a valid peer. */ - TOX_ERR_GROUP_MOD_REMOVE_PEER_PEER_NOT_FOUND, + TOX_ERR_GROUP_MOD_KICK_PEER_PEER_NOT_FOUND, /** * The caller does not have the required permissions for this action. */ - TOX_ERR_GROUP_MOD_REMOVE_PEER_PERMISSIONS, + TOX_ERR_GROUP_MOD_KICK_PEER_PERMISSIONS, /** - * The peer could not be removed from the group. - * - * If a ban was set, this error indicates that the ban entry could not be created. - * This is usually due to the peer's IP address already occurring in the ban list. It may also - * be due to the entry containing invalid peer information, or a failure to cryptographically - * authenticate the entry. + * The peer could not be kicked from the group. */ - TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_ACTION, + TOX_ERR_GROUP_MOD_KICK_PEER_FAIL_ACTION, /** * The packet failed to send. */ - TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_SEND, - -} TOX_ERR_GROUP_MOD_REMOVE_PEER; - - -/** - * Kick/ban a peer. - * - * This function will remove a peer from the caller's peer list and optionally add their IP address - * to the ban list. It will also send a packet to all group members requesting them - * to do the same. - * - * @param groupnumber The group number of the group the ban is intended for. - * @param peer_id The ID of the peer who will be kicked and/or added to the ban list. - * @param set_ban Set to true if a ban shall be set on the peer's IP address. - * - * @return true on success. - */ -bool tox_group_mod_remove_peer(Tox *tox, uint32_t groupnumber, uint32_t peer_id, bool set_ban, - TOX_ERR_GROUP_MOD_REMOVE_PEER *error); - -typedef enum TOX_ERR_GROUP_MOD_REMOVE_BAN { - - /** - * The function returned successfully. - */ - TOX_ERR_GROUP_MOD_REMOVE_BAN_OK, + TOX_ERR_GROUP_MOD_KICK_PEER_FAIL_SEND, /** - * The group number passed did not designate a valid group. + * The caller attempted to set their own role. */ - TOX_ERR_GROUP_MOD_REMOVE_BAN_GROUP_NOT_FOUND, + TOX_ERR_GROUP_MOD_KICK_PEER_SELF, - /** - * The caller does not have the required permissions for this action. - */ - TOX_ERR_GROUP_MOD_REMOVE_BAN_PERMISSIONS, - - /** - * The ban entry could not be removed. This may occur if ban_id does not designate - * a valid ban entry. - */ - TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_ACTION, - - /** - * The packet failed to send. - */ - TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_SEND, - -} TOX_ERR_GROUP_MOD_REMOVE_BAN; +} TOX_ERR_GROUP_MOD_KICK_PEER; /** - * Removes a ban. + * Kick a peer. * - * This function removes a ban entry from the ban list, and sends a packet to the rest of - * the group requesting that they do the same. + * This function will remove a peer from the caller's peer list and send a packet to all + * group members requesting them to do the same. Note: This function will not trigger + * the `group_peer_exit` event for the caller. * - * @param groupnumber The group number of the group in which the ban is to be removed. - * @param ban_id The ID of the ban entry that shall be removed. + * @param group_number The group number of the group the action is intended for. + * @param peer_id The ID of the peer who will be kicked. * - * @return true on success + * @return true on success. */ -bool tox_group_mod_remove_ban(Tox *tox, uint32_t groupnumber, uint32_t ban_id, TOX_ERR_GROUP_MOD_REMOVE_BAN *error); +bool tox_group_mod_kick_peer(Tox *tox, uint32_t group_number, uint32_t peer_id, TOX_ERR_GROUP_MOD_KICK_PEER *error); /** * Represents moderation events. These should be used with the `group_moderation` event. @@ -4874,11 +5004,6 @@ typedef enum TOX_GROUP_MOD_EVENT { */ TOX_GROUP_MOD_EVENT_KICK, - /** - * A peer has been banned from the group. - */ - TOX_GROUP_MOD_EVENT_BAN, - /** * A peer as been given the observer role. */ @@ -4898,98 +5023,22 @@ typedef enum TOX_GROUP_MOD_EVENT { /** - * @param groupnumber The group number of the group the event is intended for. + * @param group_number The group number of the group the event is intended for. * @param source_peer_number The ID of the peer who initiated the event. * @param target_peer_number The ID of the peer who is the target of the event. * @param mod_type The type of event. */ -typedef void tox_group_moderation_cb(Tox *tox, uint32_t groupnumber, uint32_t source_peer_number, +typedef void tox_group_moderation_cb(Tox *tox, uint32_t group_number, uint32_t source_peer_number, uint32_t target_peer_number, TOX_GROUP_MOD_EVENT mod_type, void *user_data); /** * Set the callback for the `group_moderation` event. Pass NULL to unset. * - * This event is triggered when a moderator or founder executes a moderation event. - */ -void tox_callback_group_moderation(Tox *tox, tox_group_moderation_cb *callback, void *user_data); - - -/******************************************************************************* - * - * :: Group chat ban list queries - * - ******************************************************************************/ - - - -/** - * Error codes for group ban list queries. - */ -typedef enum TOX_ERR_GROUP_BAN_QUERY { - - /** - * The function returned successfully. - */ - TOX_ERR_GROUP_BAN_QUERY_OK, - - /** - * The group number passed did not designate a valid group. - */ - TOX_ERR_GROUP_BAN_QUERY_GROUP_NOT_FOUND, - - /** - * The ban_id does not designate a valid ban list entry. - */ - TOX_ERR_GROUP_BAN_QUERY_BAD_ID, - -} TOX_ERR_GROUP_BAN_QUERY; - - -/** - * Return the number of entries in the ban list for the group designated by - * the given group number. If the group number is invalid, the return value is unspecified. - */ -size_t tox_group_ban_get_list_size(const Tox *tox, uint32_t groupnumber, TOX_ERR_GROUP_BAN_QUERY *error); - -/** - * Copy a list of valid ban list ID's into an array. - * - * Call tox_group_ban_get_list_size to determine the number of elements to allocate. - * - * @param list A memory region with enough space to hold the ban list. If - * this parameter is NULL, this function has no effect. - * - * @return true on success. - */ -bool tox_group_ban_get_list(const Tox *tox, uint32_t groupnumber, uint32_t *list, TOX_ERR_GROUP_BAN_QUERY *error); - -/** - * Return the length of the name for the ban list entry designated by ban_id, in the - * group designated by the given group number. If either groupnumber or ban_id is invalid, - * the return value is unspecified. - */ -size_t tox_group_ban_get_name_size(const Tox *tox, uint32_t groupnumber, uint32_t ban_id, - TOX_ERR_GROUP_BAN_QUERY *error); - -/** - * Write the name of the ban entry designated by ban_id in the group designated by the - * given group number to a byte array. - * - * Call tox_group_ban_get_name_size to find out how much memory to allocate for the result. - * - * @return true on success. - */ -bool tox_group_ban_get_name(const Tox *tox, uint32_t groupnumber, uint32_t ban_id, uint8_t *name, - TOX_ERR_GROUP_BAN_QUERY *error); - -/** - * Return a time stamp indicating the time the ban was set, for the ban list entry - * designated by ban_id, in the group designated by the given group number. - * If either groupnumber or ban_id is invalid, the return value is unspecified. + * This event is triggered when a moderator or founder executes a moderation event, with the exception + * of the peer who initiates the event. */ -uint64_t tox_group_ban_get_time_set(const Tox *tox, uint32_t groupnumber, uint32_t ban_id, - TOX_ERR_GROUP_BAN_QUERY *error); +void tox_callback_group_moderation(Tox *tox, tox_group_moderation_cb *callback); #ifdef __cplusplus } @@ -5045,9 +5094,9 @@ typedef TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE Tox_Err_Group_Founder_Set_Privac typedef TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT Tox_Err_Group_Founder_Set_Peer_Limit; typedef TOX_ERR_GROUP_TOGGLE_IGNORE Tox_Err_Group_Toggle_Ignore; typedef TOX_ERR_GROUP_MOD_SET_ROLE Tox_Err_Group_Mod_Set_Role; -typedef TOX_ERR_GROUP_MOD_REMOVE_PEER Tox_Err_Group_Mod_Remove_Peer; -typedef TOX_ERR_GROUP_MOD_REMOVE_BAN Tox_Err_Group_Mod_Remove_Ban; -typedef TOX_ERR_GROUP_BAN_QUERY Tox_Err_Group_Ban_Query; +typedef TOX_ERR_GROUP_MOD_KICK_PEER Tox_Err_Group_Mod_Kick_Peer; +typedef TOX_ERR_GROUP_DISCONNECT Tox_Err_Group_Disconnect; +typedef TOX_ERR_GROUP_IS_CONNECTED Tox_Err_Group_Is_Connected; typedef TOX_USER_STATUS Tox_User_Status; typedef TOX_MESSAGE_TYPE Tox_Message_Type; typedef TOX_PROXY_TYPE Tox_Proxy_Type; diff --git a/toxcore/util.c b/toxcore/util.c index 7a9caf2831..a1f73ff25f 100644 --- a/toxcore/util.c +++ b/toxcore/util.c @@ -75,6 +75,11 @@ bool id_equal(const uint8_t *dest, const uint8_t *src) return public_key_cmp(dest, src) == 0; } +int id_cmp(const uint8_t *first_id, const uint8_t *second_id) +{ + return memcmp(first_id, second_id, ENC_PUBLIC_KEY); +} + bool chat_id_equal(const uint8_t *dest, const uint8_t *src) { return memcmp(dest, src, CHAT_ID_SIZE) == 0; @@ -86,15 +91,20 @@ uint32_t id_copy(uint8_t *dest, const uint8_t *src) return CRYPTO_PUBLIC_KEY_SIZE; } -char *id_toa(const uint8_t *id) +/* id_str should be of length at least IDSTRING_LEN */ +char *id_to_string(const uint8_t *pk, char *id_str, size_t length) { - char *str = (char *)malloc(CRYPTO_PUBLIC_KEY_SIZE * 2 + 1); + if (length < IDSTRING_LEN) { + snprintf(id_str, length, "Bad buf length"); + return id_str; + } - for (int i = 0; i < CRYPTO_PUBLIC_KEY_SIZE; ++i) { - sprintf(str + 2 * i, "%02x", id[i]); + for (uint32_t i = 0; i < CRYPTO_PUBLIC_KEY_SIZE; ++i) { + sprintf(&id_str[i * 2], "%02X", pk[i]); } - return str; + id_str[CRYPTO_PUBLIC_KEY_SIZE * 2] = 0; + return id_str; } void host_to_net(uint8_t *num, uint16_t numbytes) diff --git a/toxcore/util.h b/toxcore/util.h index 302e58bd09..d56b585248 100644 --- a/toxcore/util.h +++ b/toxcore/util.h @@ -15,6 +15,7 @@ #include #include "logger.h" +#include "crypto_core.h" #ifdef __cplusplus extern "C" { @@ -34,6 +35,8 @@ const uint8_t *get_chat_id(const uint8_t *key); /* id functions */ bool id_equal(const uint8_t *dest, const uint8_t *src); +int id_cmp(const uint8_t *first_id, const uint8_t *second_id); + /* compares two group chat_id's */ bool chat_id_equal(const uint8_t *dest, const uint8_t *src); @@ -74,6 +77,9 @@ uint64_t min_u64(uint64_t a, uint64_t b); /* Returns a 32-bit hash of key of size len */ uint32_t jenkins_one_at_a_time_hash(const uint8_t *key, size_t len); +#define IDSTRING_LEN (CRYPTO_PUBLIC_KEY_SIZE * 2 + 1) +char *id_to_string(const uint8_t *pk, char *id_str, size_t length); + #ifdef __cplusplus } // extern "C" #endif