Merge commit '227425b90e9a671118026689dd30967e127a1090' as 'external/toxcore/c-toxcore'

This commit is contained in:
2023-07-25 11:53:09 +02:00
467 changed files with 116591 additions and 0 deletions

View File

@ -0,0 +1,840 @@
load("@rules_cc//cc:defs.bzl", "cc_test")
load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test")
load("//tools:no_undefined.bzl", "cc_library")
package(features = ["layering_check"])
exports_files(
srcs = ["tox.h"],
visibility = ["//c-toxcore:__pkg__"],
)
cc_library(
name = "attributes",
hdrs = ["attributes.h"],
visibility = ["//c-toxcore:__subpackages__"],
)
cc_library(
name = "ccompat",
srcs = ["ccompat.c"],
hdrs = ["ccompat.h"],
visibility = ["//c-toxcore:__subpackages__"],
deps = [":attributes"],
)
cc_library(
name = "util",
srcs = ["util.c"],
hdrs = ["util.h"],
visibility = [
"//c-toxcore/auto_tests:__pkg__",
"//c-toxcore/other:__pkg__",
"//c-toxcore/other/bootstrap_daemon:__pkg__",
"//c-toxcore/testing/fuzzing:__pkg__",
"//c-toxcore/toxav:__pkg__",
],
deps = [
":attributes",
":ccompat",
"@pthread",
],
)
cc_test(
name = "util_test",
size = "small",
srcs = ["util_test.cc"],
deps = [
":crypto_core",
":util",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "bin_pack",
srcs = ["bin_pack.c"],
hdrs = ["bin_pack.h"],
visibility = ["//c-toxcore:__subpackages__"],
deps = [
":ccompat",
"//c-toxcore/third_party:cmp",
],
)
cc_library(
name = "bin_unpack",
srcs = ["bin_unpack.c"],
hdrs = ["bin_unpack.h"],
visibility = ["//c-toxcore:__subpackages__"],
deps = [
":ccompat",
"//c-toxcore/third_party:cmp",
],
)
cc_test(
name = "bin_pack_test",
size = "small",
srcs = ["bin_pack_test.cc"],
deps = [
":bin_pack",
":bin_unpack",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "crypto_core",
srcs = ["crypto_core.c"],
hdrs = ["crypto_core.h"],
visibility = ["//c-toxcore:__subpackages__"],
deps = [
":ccompat",
"@libsodium",
],
)
cc_test(
name = "crypto_core_test",
size = "small",
srcs = ["crypto_core_test.cc"],
flaky = True,
deps = [
":crypto_core",
":util",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "list",
srcs = ["list.c"],
hdrs = ["list.h"],
deps = [":ccompat"],
)
cc_test(
name = "list_test",
size = "small",
srcs = ["list_test.cc"],
deps = [
":list",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "logger",
srcs = ["logger.c"],
hdrs = ["logger.h"],
visibility = [
"//c-toxcore/auto_tests:__pkg__",
"//c-toxcore/other:__pkg__",
"//c-toxcore/other/bootstrap_daemon:__pkg__",
"//c-toxcore/toxav:__pkg__",
],
deps = [":ccompat"],
)
cc_library(
name = "state",
srcs = ["state.c"],
hdrs = ["state.h"],
deps = [
":ccompat",
":logger",
],
)
cc_library(
name = "mono_time",
srcs = ["mono_time.c"],
hdrs = ["mono_time.h"],
visibility = [
"//c-toxcore/auto_tests:__pkg__",
"//c-toxcore/other:__pkg__",
"//c-toxcore/other/bootstrap_daemon:__pkg__",
"//c-toxcore/testing:__pkg__",
"//c-toxcore/toxav:__pkg__",
],
deps = [
":ccompat",
"@pthread",
],
)
cc_test(
name = "mono_time_test",
size = "small",
srcs = ["mono_time_test.cc"],
deps = [
":mono_time",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "shared_key_cache",
srcs = ["shared_key_cache.c"],
hdrs = ["shared_key_cache.h"],
visibility = [
"//c-toxcore/auto_tests:__pkg__",
"//c-toxcore/other:__pkg__",
"//c-toxcore/other/bootstrap_daemon:__pkg__",
"//c-toxcore/testing:__pkg__",
"//c-toxcore/toxav:__pkg__",
],
deps = [
":ccompat",
":crypto_core",
":mono_time",
],
)
cc_library(
name = "network",
srcs = ["network.c"],
hdrs = ["network.h"],
visibility = [
"//c-toxcore/auto_tests:__pkg__",
"//c-toxcore/other:__pkg__",
"//c-toxcore/other/bootstrap_daemon:__pkg__",
"//c-toxcore/testing/fuzzing:__pkg__",
"//c-toxcore/toxav:__pkg__",
],
deps = [
":ccompat",
":crypto_core",
":logger",
":mono_time",
":util",
"@libsodium",
"@psocket",
"@pthread",
],
)
cc_test(
name = "network_test",
size = "small",
srcs = ["network_test.cc"],
deps = [
":network",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "timed_auth",
srcs = ["timed_auth.c"],
hdrs = ["timed_auth.h"],
deps = [
":ccompat",
":crypto_core",
":mono_time",
],
)
cc_library(
name = "ping_array",
srcs = ["ping_array.c"],
hdrs = ["ping_array.h"],
deps = [
":ccompat",
":crypto_core",
":mono_time",
":util",
],
)
cc_test(
name = "ping_array_test",
size = "small",
srcs = ["ping_array_test.cc"],
deps = [
":mono_time",
":ping_array",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "LAN_discovery",
srcs = ["LAN_discovery.c"],
hdrs = ["LAN_discovery.h"],
visibility = [
"//c-toxcore/other:__pkg__",
"//c-toxcore/other/bootstrap_daemon:__pkg__",
"//c-toxcore/testing:__pkg__",
],
deps = [
":ccompat",
":crypto_core",
":network",
":util",
],
)
cc_library(
name = "DHT",
srcs = [
"DHT.c",
"ping.c",
],
hdrs = [
"DHT.h",
"ping.h",
],
visibility = [
"//c-toxcore/other:__pkg__",
"//c-toxcore/other/bootstrap_daemon:__pkg__",
"//c-toxcore/testing:__pkg__",
],
deps = [
":LAN_discovery",
":ccompat",
":crypto_core",
":logger",
":mono_time",
":network",
":ping_array",
":shared_key_cache",
":state",
":util",
],
)
cc_test(
name = "DHT_test",
size = "small",
srcs = ["DHT_test.cc"],
deps = [
":DHT",
":crypto_core",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_fuzz_test(
name = "DHT_fuzz_test",
srcs = ["DHT_fuzz_test.cc"],
corpus = ["//tools/toktok-fuzzer/corpus:DHT_fuzz_test"],
deps = [
":DHT",
"//c-toxcore/testing/fuzzing:fuzz_support",
],
)
cc_library(
name = "onion",
srcs = ["onion.c"],
hdrs = ["onion.h"],
visibility = ["//c-toxcore/auto_tests:__pkg__"],
deps = [
":DHT",
":ccompat",
":crypto_core",
":mono_time",
":shared_key_cache",
":util",
],
)
cc_library(
name = "forwarding",
srcs = ["forwarding.c"],
hdrs = ["forwarding.h"],
visibility = ["//c-toxcore/auto_tests:__pkg__"],
deps = [
":DHT",
":ccompat",
":network",
":timed_auth",
],
)
cc_fuzz_test(
name = "forwarding_fuzz_test",
srcs = ["forwarding_fuzz_test.cc"],
#corpus = ["//tools/toktok-fuzzer/corpus:forwarding_fuzz_test"],
deps = [
":forwarding",
"//c-toxcore/testing/fuzzing:fuzz_support",
"//c-toxcore/testing/fuzzing:fuzz_tox",
],
)
cc_library(
name = "announce",
srcs = ["announce.c"],
hdrs = ["announce.h"],
visibility = [
"//c-toxcore/auto_tests:__pkg__",
"//c-toxcore/other/bootstrap_daemon:__pkg__",
],
deps = [
":LAN_discovery",
":ccompat",
":forwarding",
":shared_key_cache",
":timed_auth",
":util",
],
)
cc_library(
name = "TCP_common",
srcs = ["TCP_common.c"],
hdrs = ["TCP_common.h"],
visibility = ["//c-toxcore/auto_tests:__pkg__"],
deps = [
":ccompat",
":network",
],
)
cc_library(
name = "TCP_server",
srcs = ["TCP_server.c"],
hdrs = ["TCP_server.h"],
copts = select({
"//tools/config:linux": ["-DTCP_SERVER_USE_EPOLL=1"],
"//conditions:default": [],
}),
visibility = [
"//c-toxcore/auto_tests:__pkg__",
"//c-toxcore/other:__pkg__",
"//c-toxcore/other/bootstrap_daemon:__pkg__",
],
deps = [
":TCP_common",
":ccompat",
":crypto_core",
":forwarding",
":list",
":mono_time",
":onion",
":util",
],
)
cc_library(
name = "TCP_client",
srcs = ["TCP_client.c"],
hdrs = ["TCP_client.h"],
visibility = ["//c-toxcore/auto_tests:__pkg__"],
deps = [
":TCP_common",
":ccompat",
":forwarding",
":mono_time",
":util",
],
)
cc_library(
name = "TCP_connection",
srcs = ["TCP_connection.c"],
hdrs = ["TCP_connection.h"],
visibility = ["//c-toxcore/auto_tests:__pkg__"],
deps = [
":DHT",
":TCP_client",
":TCP_common",
":ccompat",
":crypto_core",
":list",
":mono_time",
":onion",
":util",
],
)
cc_test(
name = "TCP_connection_test",
size = "small",
srcs = ["TCP_connection_test.cc"],
deps = [
":TCP_connection",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "net_crypto",
srcs = ["net_crypto.c"],
hdrs = ["net_crypto.h"],
visibility = ["//c-toxcore/auto_tests:__pkg__"],
deps = [
":DHT",
":TCP_connection",
":ccompat",
":list",
":mono_time",
":util",
],
)
cc_library(
name = "onion_announce",
srcs = ["onion_announce.c"],
hdrs = ["onion_announce.h"],
visibility = [
"//c-toxcore/auto_tests:__pkg__",
"//c-toxcore/other:__pkg__",
"//c-toxcore/other/bootstrap_daemon:__pkg__",
],
deps = [
":DHT",
":LAN_discovery",
":ccompat",
":mono_time",
":onion",
":shared_key_cache",
":timed_auth",
":util",
],
)
cc_library(
name = "group_announce",
srcs = ["group_announce.c"],
hdrs = ["group_announce.h"],
visibility = [
"//c-toxcore/auto_tests:__pkg__",
"//c-toxcore/other:__pkg__",
"//c-toxcore/other/bootstrap_daemon:__pkg__",
],
deps = [
":DHT",
":LAN_discovery",
":ccompat",
":mono_time",
":util",
],
)
cc_test(
name = "group_announce_test",
size = "small",
srcs = ["group_announce_test.cc"],
deps = [
":group_announce",
":mono_time",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "group_onion_announce",
srcs = ["group_onion_announce.c"],
hdrs = ["group_onion_announce.h"],
visibility = [
"//c-toxcore/auto_tests:__pkg__",
"//c-toxcore/other:__pkg__",
"//c-toxcore/other/bootstrap_daemon:__pkg__",
],
deps = [
":ccompat",
":group_announce",
":onion_announce",
],
)
cc_fuzz_test(
name = "group_announce_fuzz_test",
srcs = ["group_announce_fuzz_test.cc"],
#corpus = ["//tools/toktok-fuzzer/corpus:group_announce_fuzz_test"],
deps = [
":group_announce",
"//c-toxcore/testing/fuzzing:fuzz_support",
],
)
cc_library(
name = "onion_client",
srcs = ["onion_client.c"],
hdrs = ["onion_client.h"],
visibility = ["//c-toxcore/auto_tests:__pkg__"],
deps = [
":DHT",
":LAN_discovery",
":ccompat",
":group_onion_announce",
":mono_time",
":net_crypto",
":network",
":onion_announce",
":util",
],
)
cc_library(
name = "friend_connection",
srcs = ["friend_connection.c"],
hdrs = ["friend_connection.h"],
visibility = ["//c-toxcore/auto_tests:__pkg__"],
deps = [
":DHT",
":ccompat",
":mono_time",
":net_crypto",
":onion_client",
":util",
],
)
cc_test(
name = "friend_connection_test",
size = "small",
srcs = ["friend_connection_test.cc"],
deps = [
":friend_connection",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "friend_requests",
srcs = ["friend_requests.c"],
hdrs = ["friend_requests.h"],
visibility = [
"//c-toxcore/auto_tests:__pkg__",
"//c-toxcore/other:__pkg__",
"//c-toxcore/testing:__pkg__",
],
deps = [
":ccompat",
":friend_connection",
":network",
":util",
],
)
cc_library(
name = "group_moderation",
srcs = ["group_moderation.c"],
hdrs = ["group_moderation.h"],
deps = [
":DHT",
":ccompat",
":crypto_core",
":logger",
":mono_time",
":network",
":util",
"@libsodium",
],
)
cc_test(
name = "group_moderation_test",
size = "small",
srcs = ["group_moderation_test.cc"],
deps = [
":crypto_core",
":group_moderation",
":logger",
":util",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_fuzz_test(
name = "group_moderation_fuzz_test",
srcs = ["group_moderation_fuzz_test.cc"],
corpus = ["//tools/toktok-fuzzer/corpus:group_moderation_fuzz_test"],
deps = [
":group_moderation",
"//c-toxcore/testing/fuzzing:fuzz_support",
],
)
cc_library(
name = "Messenger",
srcs = [
"Messenger.c",
"group_chats.c",
"group_connection.c",
"group_pack.c",
],
hdrs = [
"Messenger.h",
"group_chats.h",
"group_common.h",
"group_connection.h",
"group_pack.h",
],
visibility = [
"//c-toxcore/auto_tests:__pkg__",
"//c-toxcore/other:__pkg__",
"//c-toxcore/testing:__pkg__",
"//c-toxcore/toxav:__pkg__",
],
deps = [
":DHT",
":LAN_discovery",
":TCP_connection",
":TCP_server",
":announce",
":bin_pack",
":bin_unpack",
":ccompat",
":crypto_core",
":forwarding",
":friend_connection",
":friend_requests",
":group_moderation",
":group_onion_announce",
":logger",
":mono_time",
":net_crypto",
":network",
":onion_announce",
":state",
":util",
"@libsodium",
],
)
cc_library(
name = "group",
srcs = ["group.c"],
hdrs = ["group.h"],
visibility = ["//c-toxcore/toxav:__pkg__"],
deps = [
":Messenger",
":ccompat",
":mono_time",
":state",
":util",
],
)
cc_library(
name = "tox",
srcs = [
"tox.c",
"tox_api.c",
"tox_private.c",
],
hdrs = [
"tox.h",
"tox_private.h",
"tox_struct.h",
],
visibility = ["//c-toxcore:__subpackages__"],
deps = [
":Messenger",
":ccompat",
":group",
":group_moderation",
":logger",
":mono_time",
":network",
"//c-toxcore/toxencryptsave:defines",
],
)
cc_test(
name = "tox_test",
size = "small",
srcs = ["tox_test.cc"],
deps = [
":crypto_core",
":tox",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "tox_unpack",
srcs = ["tox_unpack.c"],
hdrs = ["tox_unpack.h"],
visibility = ["//c-toxcore:__subpackages__"],
deps = [
":bin_unpack",
":ccompat",
":tox",
],
)
cc_library(
name = "tox_events",
srcs = ["tox_events.c"] + glob([
"events/*.c",
"events/*.h",
]),
hdrs = ["tox_events.h"],
visibility = ["//c-toxcore:__subpackages__"],
deps = [
":bin_pack",
":bin_unpack",
":ccompat",
":tox",
":tox_unpack",
"//c-toxcore/third_party:cmp",
],
)
cc_test(
name = "tox_events_test",
size = "small",
srcs = ["tox_events_test.cc"],
deps = [
":crypto_core",
":tox_events",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_fuzz_test(
name = "tox_events_fuzz_test",
srcs = ["tox_events_fuzz_test.cc"],
corpus = ["//tools/toktok-fuzzer/corpus:tox_events_fuzz_test"],
deps = [":tox_events"],
)
cc_library(
name = "tox_dispatch",
srcs = ["tox_dispatch.c"],
hdrs = ["tox_dispatch.h"],
visibility = ["//c-toxcore:__subpackages__"],
deps = [
":ccompat",
":tox_events",
],
)
alias(
name = "toxcore",
actual = ":tox_dispatch",
visibility = ["//c-toxcore:__subpackages__"],
)
sh_library(
name = "cimple_files",
srcs = glob([
"events/*.c",
"events/*.h",
"*.c",
"*.h",
]),
visibility = ["//c-toxcore/testing:__pkg__"],
)

3113
external/toxcore/c-toxcore/toxcore/DHT.c vendored Normal file

File diff suppressed because it is too large Load Diff

542
external/toxcore/c-toxcore/toxcore/DHT.h vendored Normal file
View File

@ -0,0 +1,542 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2013 Tox project.
*/
/** @file
* @brief An implementation of the DHT as seen in docs/updates/DHT.md
*/
#ifndef C_TOXCORE_TOXCORE_DHT_H
#define C_TOXCORE_TOXCORE_DHT_H
#include <stdbool.h>
#include "attributes.h"
#include "crypto_core.h"
#include "logger.h"
#include "mono_time.h"
#include "network.h"
#include "ping_array.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Encryption and signature keys definition */
#define ENC_PUBLIC_KEY_SIZE CRYPTO_PUBLIC_KEY_SIZE
#define ENC_SECRET_KEY_SIZE CRYPTO_SECRET_KEY_SIZE
#define SIG_PUBLIC_KEY_SIZE CRYPTO_SIGN_PUBLIC_KEY_SIZE
#define SIG_SECRET_KEY_SIZE CRYPTO_SIGN_SECRET_KEY_SIZE
/* Size of the group chat_id */
#define CHAT_ID_SIZE SIG_PUBLIC_KEY_SIZE
/* Extended keys for group chats */
#define EXT_SECRET_KEY_SIZE (ENC_SECRET_KEY_SIZE + SIG_SECRET_KEY_SIZE)
#define EXT_PUBLIC_KEY_SIZE (ENC_PUBLIC_KEY_SIZE + SIG_PUBLIC_KEY_SIZE)
/* Maximum size of a signature (may be smaller) */
#define SIGNATURE_SIZE CRYPTO_SIGNATURE_SIZE
/** Maximum number of clients stored per friend. */
#define MAX_FRIEND_CLIENTS 8
#define LCLIENT_NODES MAX_FRIEND_CLIENTS
#define LCLIENT_LENGTH 128
/** A list of the clients mathematically closest to ours. */
#define LCLIENT_LIST (LCLIENT_LENGTH * LCLIENT_NODES)
#define MAX_CLOSE_TO_BOOTSTRAP_NODES 8
/** The max number of nodes to send with send nodes. */
#define MAX_SENT_NODES 4
/** Ping timeout in seconds */
#define PING_TIMEOUT 5
/** size of DHT ping arrays. */
#define DHT_PING_ARRAY_SIZE 512
/** Ping interval in seconds for each node in our lists. */
#define PING_INTERVAL 60
/** The number of seconds for a non responsive node to become bad. */
#define PINGS_MISSED_NODE_GOES_BAD 1
#define PING_ROUNDTRIP 2
#define BAD_NODE_TIMEOUT (PING_INTERVAL + PINGS_MISSED_NODE_GOES_BAD * (PING_INTERVAL + PING_ROUNDTRIP))
/**
* The number of "fake" friends to add.
*
* (for optimization purposes and so our paths for the onion part are more random)
*/
#define DHT_FAKE_FRIEND_NUMBER 2
/** Maximum packet size for a DHT request packet. */
#define MAX_CRYPTO_REQUEST_SIZE 1024
#define CRYPTO_PACKET_FRIEND_REQ 32 // Friend request crypto packet ID.
#define CRYPTO_PACKET_DHTPK 156
#define CRYPTO_PACKET_NAT_PING 254 // NAT ping crypto packet ID.
/* Max size of a packed node for IPV4 and IPV6 respectively */
#define PACKED_NODE_SIZE_IP4 (1 + SIZE_IP4 + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE)
#define PACKED_NODE_SIZE_IP6 (1 + SIZE_IP6 + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE)
/**
* This define can eventually be removed; it is necessary if a significant
* proportion of dht nodes do not implement the dht announcements protocol.
*/
#define CHECK_ANNOUNCE_NODE
/**
* @brief Create a request to peer.
*
* Packs the data and sender public key and encrypts the packet.
*
* @param[in] send_public_key public key of the sender.
* @param[in] send_secret_key secret key of the sender.
* @param[out] packet an array of @ref MAX_CRYPTO_REQUEST_SIZE big.
* @param[in] recv_public_key public key of the receiver.
* @param[in] data represents the data we send with the request.
* @param[in] data_length the length of the data.
* @param[in] request_id the id of the request (32 = friend request, 254 = ping request).
*
* @attention Constraints:
* @code
* sizeof(packet) >= MAX_CRYPTO_REQUEST_SIZE
* @endcode
*
* @retval -1 on failure.
* @return the length of the created packet on success.
*/
non_null()
int create_request(const Random *rng, const uint8_t *send_public_key, const uint8_t *send_secret_key,
uint8_t *packet, const uint8_t *recv_public_key,
const uint8_t *data, uint32_t data_length, uint8_t request_id);
/**
* @brief Decrypts and unpacks a DHT request packet.
*
* Puts the senders public key in the request in @p public_key, the data from
* the request in @p data.
*
* @param[in] self_public_key public key of the receiver (us).
* @param[in] self_secret_key secret key of the receiver (us).
* @param[out] public_key public key of the sender, copied from the input packet.
* @param[out] data decrypted request data, copied from the input packet, must
* have room for @ref MAX_CRYPTO_REQUEST_SIZE bytes.
* @param[in] packet is the request packet.
* @param[in] packet_length length of the packet.
*
* @attention Constraints:
* @code
* sizeof(data) >= MAX_CRYPTO_REQUEST_SIZE
* @endcode
*
* @retval -1 if not valid request.
* @return the length of the unpacked data.
*/
non_null()
int handle_request(
const uint8_t *self_public_key, const uint8_t *self_secret_key, uint8_t *public_key, uint8_t *data,
uint8_t *request_id, const uint8_t *packet, uint16_t packet_length);
typedef struct IPPTs {
IP_Port ip_port;
uint64_t timestamp;
} IPPTs;
typedef struct IPPTsPng {
IP_Port ip_port;
uint64_t timestamp;
uint64_t last_pinged;
/* Returned by this node */
IP_Port ret_ip_port;
uint64_t ret_timestamp;
/* true if this ip_port is ours */
bool ret_ip_self;
} IPPTsPng;
typedef struct Client_data {
uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE];
IPPTsPng assoc4;
IPPTsPng assoc6;
#ifdef CHECK_ANNOUNCE_NODE
/* Responded to data search? */
bool announce_node;
#endif
} Client_data;
/*----------------------------------------------------------------------------------*/
typedef struct NAT {
/* true if currently hole punching */
bool hole_punching;
uint32_t punching_index;
uint32_t tries;
uint32_t punching_index2;
uint64_t punching_timestamp;
uint64_t recv_nat_ping_timestamp;
uint64_t nat_ping_id;
uint64_t nat_ping_timestamp;
} NAT;
typedef struct Node_format {
uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE];
IP_Port ip_port;
} Node_format;
extern const Node_format empty_node_format;
typedef struct DHT_Friend DHT_Friend;
non_null() const uint8_t *dht_friend_public_key(const DHT_Friend *dht_friend);
non_null() const Client_data *dht_friend_client(const DHT_Friend *dht_friend, size_t index);
/** @return packet size of packed node with ip_family on success.
* @retval -1 on failure.
*/
int packed_node_size(Family ip_family);
/** @brief Pack an IP_Port structure into data of max size length.
*
* Packed_length is the offset of data currently packed.
*
* @return size of packed IP_Port data on success.
* @retval -1 on failure.
*/
non_null()
int pack_ip_port(const Logger *logger, uint8_t *data, uint16_t length, const IP_Port *ip_port);
/** @brief Encrypt plain and write resulting DHT packet into packet with max size length.
*
* @return size of packet on success.
* @retval -1 on failure.
*/
non_null()
int dht_create_packet(const Random *rng,
const uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE],
const uint8_t *shared_key, const uint8_t type,
const uint8_t *plain, size_t plain_length,
uint8_t *packet, size_t length);
/** @brief Unpack IP_Port structure from data of max size length into ip_port.
*
* len_processed is the offset of data currently unpacked.
*
* @return size of unpacked ip_port on success.
* @retval -1 on failure.
*/
non_null()
int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool tcp_enabled);
/** @brief Pack number of nodes into data of maxlength length.
*
* @return length of packed nodes on success.
* @retval -1 on failure.
*/
non_null()
int pack_nodes(const Logger *logger, uint8_t *data, uint16_t length, const Node_format *nodes, uint16_t number);
/** @brief 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.
* @retval -1 on failure.
*/
non_null(1, 4) nullable(3)
int unpack_nodes(Node_format *nodes, uint16_t max_num_nodes, uint16_t *processed_data_len, const uint8_t *data,
uint16_t length, bool tcp_enabled);
/*----------------------------------------------------------------------------------*/
typedef int cryptopacket_handler_cb(void *object, const IP_Port *ip_port, const uint8_t *source_pubkey,
const uint8_t *data, uint16_t len, void *userdata);
typedef struct DHT DHT;
non_null() const uint8_t *dht_get_self_public_key(const DHT *dht);
non_null() const uint8_t *dht_get_self_secret_key(const DHT *dht);
non_null() void dht_set_self_public_key(DHT *dht, const uint8_t *key);
non_null() void dht_set_self_secret_key(DHT *dht, const uint8_t *key);
non_null() Networking_Core *dht_get_net(const DHT *dht);
non_null() struct Ping *dht_get_ping(const DHT *dht);
non_null() const Client_data *dht_get_close_clientlist(const DHT *dht);
non_null() const Client_data *dht_get_close_client(const DHT *dht, uint32_t client_num);
non_null() uint16_t dht_get_num_friends(const DHT *dht);
non_null() DHT_Friend *dht_get_friend(DHT *dht, uint32_t friend_num);
non_null() const uint8_t *dht_get_friend_public_key(const DHT *dht, uint32_t friend_num);
/*----------------------------------------------------------------------------------*/
/**
* Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key
* for packets that we receive.
*/
non_null()
const uint8_t *dht_get_shared_key_recv(DHT *dht, const uint8_t *public_key);
/**
* Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key
* for packets that we send.
*/
non_null()
const uint8_t *dht_get_shared_key_sent(DHT *dht, const uint8_t *public_key);
/**
* Sends a getnodes request to `ip_port` with the public key `public_key` for nodes
* that are close to `client_id`.
*
* @retval true on success.
*/
non_null()
bool dht_getnodes(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, const uint8_t *client_id);
typedef void dht_ip_cb(void *object, int32_t number, const IP_Port *ip_port);
typedef void dht_get_nodes_response_cb(const DHT *dht, const Node_format *node, void *user_data);
/** Sets the callback to be triggered on a getnodes response. */
non_null(1) nullable(2)
void dht_callback_get_nodes_response(DHT *dht, dht_get_nodes_response_cb *function);
/** @brief Add a new friend to the friends list.
* @param public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long.
*
* @param ip_callback is the callback of a function that will be called when the ip address
* is found along with arguments data and number.
* @param data User data for the callback
* @param number Will be passed to ip_callback
*
* @param lock_token will be set to a non zero number that must be passed to `dht_delfriend()`
* to properly remove the callback.
*
* @retval 0 if success.
* @retval -1 if failure (friends list is full).
*/
non_null(1, 2, 6) nullable(3, 4)
int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback,
void *data, int32_t number, uint32_t *lock_token);
/** @brief Delete a friend from the friends list.
* public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long.
* @param dht The DHT object
* @param public_key The public key of the friend
* @param lock_token The token received by dht_addfriend(...)
*
* @retval 0 if success.
* @retval -1 if failure (public_key not in friends list).
*/
non_null()
int dht_delfriend(DHT *dht, const uint8_t *public_key, uint32_t lock_token);
/** @brief Get ip of friend.
*
* @param public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long.
*
* @retval -1 if public_key does NOT refer to a friend
* @retval 0 if public_key refers to a friend and we failed to find the friend (yet)
* @retval 1 if public_key refers to a friend and we found him
*/
non_null()
int dht_getfriendip(const DHT *dht, const uint8_t *public_key, IP_Port *ip_port);
/** @brief Compares pk1 and pk2 with pk.
*
* @retval 0 if both are same distance.
* @retval 1 if pk1 is closer.
* @retval 2 if pk2 is closer.
*/
non_null()
int id_closest(const uint8_t *pk, const uint8_t *pk1, const uint8_t *pk2);
/** Return index of first unequal bit number between public keys pk1 and pk2. */
non_null()
unsigned int bit_by_bit_cmp(const uint8_t *pk1, const uint8_t *pk2);
/**
* Add node to the node list making sure only the nodes closest to cmp_pk are in the list.
*
* @return true iff the node was added to the list.
*/
non_null()
bool add_to_list(
Node_format *nodes_list, uint32_t length, const uint8_t *pk, const IP_Port *ip_port, const uint8_t *cmp_pk);
/** Return 1 if node can be added to close list, 0 if it can't. */
non_null()
bool node_addable_to_close_list(DHT *dht, const uint8_t *public_key, const IP_Port *ip_port);
#ifdef CHECK_ANNOUNCE_NODE
/** Set node as announce node. */
non_null()
void set_announce_node(DHT *dht, const uint8_t *public_key);
#endif
/**
* Get the (maximum MAX_SENT_NODES) closest nodes to public_key we know
* and put them in nodes_list (must be MAX_SENT_NODES big).
*
* sa_family = family (IPv4 or IPv6) (0 if we don't care)?
* is_LAN = return some LAN ips (true or false)
* want_announce: return only nodes which implement the dht announcements protocol.
*
* @return the number of nodes returned.
*/
non_null()
int get_close_nodes(const DHT *dht, const uint8_t *public_key, Node_format *nodes_list, Family sa_family,
bool is_LAN, bool want_announce);
/** @brief Put up to max_num nodes in nodes from the random friends.
*
* Important: this function relies on the first two DHT friends *not* being real
* friends to avoid leaking information about real friends into the onion paths.
*
* @return the number of nodes.
*/
non_null()
uint16_t randfriends_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num);
/** @brief Put up to max_num nodes in nodes from the closelist.
*
* @return the number of nodes.
*/
non_null()
uint16_t closelist_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num);
/** Run this function at least a couple times per second (It's the main loop). */
non_null()
void do_dht(DHT *dht);
/*
* Use these two functions to bootstrap the client.
*/
/**
* @brief Sends a "get nodes" request to the given node with ip, port and public_key
* to setup connections
*/
non_null()
bool dht_bootstrap(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key);
/** @brief Resolves address into an IP address.
*
* If successful, sends a "get nodes" request to the given node with ip, port
* and public_key to setup connections
*
* @param address can be a hostname or an IP address (IPv4 or IPv6).
* @param ipv6enabled if false, the resolving sticks STRICTLY to IPv4 addresses.
* Otherwise, the resolving looks for IPv6 addresses first, then IPv4 addresses.
*
* @retval 1 if the address could be converted into an IP address
* @retval 0 otherwise
*/
non_null()
int dht_bootstrap_from_address(DHT *dht, const char *address, bool ipv6enabled,
uint16_t port, const uint8_t *public_key);
/** @brief Start sending packets after DHT loaded_friends_list and loaded_clients_list are set.
*
* @retval 0 if successful
* @retval -1 otherwise
*/
non_null()
int dht_connect_after_load(DHT *dht);
/* ROUTING FUNCTIONS */
/** @brief Send the given packet to node with public_key.
*
* @return number of bytes sent.
* @retval -1 if failure.
*/
non_null()
int route_packet(const DHT *dht, const uint8_t *public_key, const uint8_t *packet, uint16_t length);
/**
* Send the following packet to everyone who tells us they are connected to friend_id.
*
* @return ip for friend.
* @return number of nodes the packet was sent to. (Only works if more than (MAX_FRIEND_CLIENTS / 4).
*/
non_null()
uint32_t route_to_friend(const DHT *dht, const uint8_t *friend_id, const Packet *packet);
/** Function to handle crypto packets. */
non_null(1) nullable(3, 4)
void cryptopacket_registerhandler(DHT *dht, uint8_t byte, cryptopacket_handler_cb *cb, void *object);
/* SAVE/LOAD functions */
/** Get the size of the DHT (for saving). */
non_null()
uint32_t dht_size(const DHT *dht);
/** Save the DHT in data where data is an array of size `dht_size()`. */
non_null()
void dht_save(const DHT *dht, uint8_t *data);
/** @brief Load the DHT from data of size size.
*
* @retval -1 if failure.
* @retval 0 if success.
*/
non_null()
int dht_load(DHT *dht, const uint8_t *data, uint32_t length);
/** Initialize DHT. */
non_null()
DHT *new_dht(const Logger *log, const Random *rng, const Network *ns, Mono_Time *mono_time, Networking_Core *net,
bool hole_punching_enabled, bool lan_discovery_enabled);
nullable(1)
void kill_dht(DHT *dht);
/**
* @retval false if we are not connected to the DHT.
* @retval true if we are.
*/
non_null()
bool dht_isconnected(const DHT *dht);
/**
* @retval false if we are not connected or only connected to lan peers with the DHT.
* @retval true if we are.
*/
non_null()
bool dht_non_lan_connected(const DHT *dht);
/** @brief Attempt to add client with ip_port and public_key to the friends client list
* and close_clientlist.
*
* @return 1+ if the item is used in any list, 0 else
*/
non_null()
uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key);
/** @brief Copies our own ip_port structure to `dest`.
*
* WAN addresses take priority over LAN addresses.
*
* This function will zero the `dest` buffer before use.
*
* @retval 0 if our ip port can't be found (this usually means we're not connected to the DHT).
* @retval 1 if IP is a WAN address.
* @retval 2 if IP is a LAN address.
*/
non_null()
unsigned int ipport_self_copy(const DHT *dht, IP_Port *dest);
#ifdef __cplusplus
} // extern "C"
#endif
#endif

View File

@ -0,0 +1,49 @@
#include "DHT.h"
#include <cstdlib>
#include <vector>
#include "../testing/fuzzing/fuzz_support.h"
namespace {
void TestHandleRequest(Fuzz_Data &input)
{
CONSUME_OR_RETURN(const uint8_t *self_public_key, input, CRYPTO_PUBLIC_KEY_SIZE);
CONSUME_OR_RETURN(const uint8_t *self_secret_key, input, CRYPTO_SECRET_KEY_SIZE);
uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t request[MAX_CRYPTO_REQUEST_SIZE];
uint8_t request_id;
handle_request(
self_public_key, self_secret_key, public_key, request, &request_id, input.data, input.size);
}
void TestUnpackNodes(Fuzz_Data &input)
{
CONSUME1_OR_RETURN(const bool tcp_enabled, input);
const uint16_t node_count = 5;
Node_format nodes[node_count];
uint16_t processed_data_len;
const int packed_count
= unpack_nodes(nodes, node_count, &processed_data_len, input.data, input.size, tcp_enabled);
if (packed_count > 0) {
Logger *logger = logger_new();
std::vector<uint8_t> packed(packed_count * PACKED_NODE_SIZE_IP6);
const int packed_size
= pack_nodes(logger, packed.data(), packed.size(), nodes, packed_count);
LOGGER_ASSERT(logger, packed_size == processed_data_len,
"packed size (%d) != unpacked size (%d)", packed_size, processed_data_len);
logger_kill(logger);
}
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
fuzz_select_target(data, size, TestHandleRequest, TestUnpackNodes);
return 0;
}

View File

@ -0,0 +1,231 @@
#include "DHT.h"
#include <gtest/gtest.h>
#include <algorithm>
#include <array>
#include "crypto_core.h"
namespace {
using PublicKey = std::array<uint8_t, CRYPTO_PUBLIC_KEY_SIZE>;
using SecretKey = std::array<uint8_t, CRYPTO_SECRET_KEY_SIZE>;
struct KeyPair {
PublicKey pk;
SecretKey sk;
explicit KeyPair(const Random *rng) { crypto_new_keypair(rng, pk.data(), sk.data()); }
};
template <typename T, size_t N>
std::array<T, N> to_array(T const (&arr)[N])
{
std::array<T, N> stdarr;
std::copy(arr, arr + N, stdarr.begin());
return stdarr;
}
PublicKey random_pk(const Random *rng)
{
PublicKey pk;
random_bytes(rng, pk.data(), pk.size());
return pk;
}
TEST(IdClosest, IdenticalKeysAreSameDistance)
{
const Random *rng = system_random();
ASSERT_NE(rng, nullptr);
PublicKey pk0 = random_pk(rng);
PublicKey pk1 = random_pk(rng);
PublicKey pk2 = pk1;
EXPECT_EQ(id_closest(pk0.data(), pk1.data(), pk2.data()), 0);
}
TEST(IdClosest, DistanceIsCommutative)
{
const Random *rng = system_random();
ASSERT_NE(rng, nullptr);
for (uint32_t i = 0; i < 100; ++i) {
PublicKey pk0 = random_pk(rng);
PublicKey pk1 = random_pk(rng);
PublicKey pk2 = random_pk(rng);
ASSERT_NE(pk1, pk2); // RNG can't produce the same random key twice
// Two non-equal keys can't have the same distance from any given key.
EXPECT_NE(id_closest(pk0.data(), pk1.data(), pk2.data()), 0);
if (id_closest(pk0.data(), pk1.data(), pk2.data()) == 1) {
EXPECT_EQ(id_closest(pk0.data(), pk2.data(), pk1.data()), 2);
}
if (id_closest(pk0.data(), pk1.data(), pk2.data()) == 2) {
EXPECT_EQ(id_closest(pk0.data(), pk2.data(), pk1.data()), 1);
}
}
}
TEST(IdClosest, SmallXorDistanceIsCloser)
{
PublicKey const pk0 = {{0xaa}};
PublicKey const pk1 = {{0xa0}};
PublicKey const pk2 = {{0x0a}};
EXPECT_EQ(id_closest(pk0.data(), pk1.data(), pk2.data()), 1);
}
TEST(IdClosest, DistinctKeysCannotHaveTheSameDistance)
{
PublicKey const pk0 = {{0x06}};
PublicKey const pk1 = {{0x00}};
PublicKey pk2 = {{0x00}};
for (uint8_t i = 1; i < 0xff; ++i) {
pk2[0] = i;
EXPECT_NE(id_closest(pk0.data(), pk1.data(), pk2.data()), 0);
}
}
TEST(AddToList, OverridesKeysWithCloserKeys)
{
PublicKey const self_pk = {{0xaa}};
PublicKey const keys[] = {
{{0xa0}}, // closest
{{0x0a}}, //
{{0x0b}}, //
{{0x0c}}, //
{{0x0d}}, //
{{0xa1}}, // closer than the 4 keys above
};
std::array<Node_format, 4> nodes{};
IP_Port ip_port = {0};
EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[0].data(), &ip_port, self_pk.data()));
EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[1].data(), &ip_port, self_pk.data()));
EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[2].data(), &ip_port, self_pk.data()));
EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[3].data(), &ip_port, self_pk.data()));
EXPECT_EQ(to_array(nodes[0].public_key), keys[0]);
EXPECT_EQ(to_array(nodes[1].public_key), keys[1]);
EXPECT_EQ(to_array(nodes[2].public_key), keys[2]);
EXPECT_EQ(to_array(nodes[3].public_key), keys[3]);
// key 4 is less close than keys 0-3
EXPECT_FALSE(add_to_list(nodes.data(), nodes.size(), keys[4].data(), &ip_port, self_pk.data()));
// 5 is closer than all except key 0
EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[5].data(), &ip_port, self_pk.data()));
EXPECT_EQ(to_array(nodes[0].public_key), keys[0]);
EXPECT_EQ(to_array(nodes[1].public_key), keys[5]);
EXPECT_EQ(to_array(nodes[2].public_key), keys[1]);
EXPECT_EQ(to_array(nodes[3].public_key), keys[2]);
}
TEST(Request, CreateAndParse)
{
const Random *rng = system_random();
ASSERT_NE(rng, nullptr);
// Peers.
const KeyPair sender(rng);
const KeyPair receiver(rng);
const uint8_t sent_pkt_id = CRYPTO_PACKET_FRIEND_REQ;
// Encoded packet.
std::array<uint8_t, MAX_CRYPTO_REQUEST_SIZE> packet;
// Received components.
PublicKey pk;
std::array<uint8_t, MAX_CRYPTO_REQUEST_SIZE> incoming;
uint8_t recvd_pkt_id;
// Request data: maximum payload is 918 bytes, so create a payload 1 byte larger than max.
std::vector<uint8_t> outgoing(919);
random_bytes(rng, outgoing.data(), outgoing.size());
EXPECT_LT(create_request(rng, sender.pk.data(), sender.sk.data(), packet.data(),
receiver.pk.data(), outgoing.data(), outgoing.size(), sent_pkt_id),
0);
// Pop one element so the payload is 918 bytes. Packing should now succeed.
outgoing.pop_back();
const int max_sent_length = create_request(rng, sender.pk.data(), sender.sk.data(),
packet.data(), receiver.pk.data(), outgoing.data(), outgoing.size(), sent_pkt_id);
ASSERT_GT(max_sent_length, 0); // success.
// Check that handle_request rejects packets larger than the maximum created packet size.
EXPECT_LT(handle_request(receiver.pk.data(), receiver.sk.data(), pk.data(), incoming.data(),
&recvd_pkt_id, packet.data(), max_sent_length + 1),
0);
// Now try all possible packet sizes from max (918) to 0.
while (!outgoing.empty()) {
// Pack:
const int sent_length = create_request(rng, sender.pk.data(), sender.sk.data(),
packet.data(), receiver.pk.data(), outgoing.data(), outgoing.size(), sent_pkt_id);
ASSERT_GT(sent_length, 0);
// Unpack:
const int recvd_length = handle_request(receiver.pk.data(), receiver.sk.data(), pk.data(),
incoming.data(), &recvd_pkt_id, packet.data(), sent_length);
ASSERT_GE(recvd_length, 0);
EXPECT_EQ(
std::vector<uint8_t>(incoming.begin(), incoming.begin() + recvd_length), outgoing);
outgoing.pop_back();
}
}
TEST(AnnounceNodes, SetAndTest)
{
Logger *log = logger_new();
Mono_Time *mono_time = mono_time_new(nullptr, nullptr);
const Random *rng = system_random();
const Network *ns = system_network();
Networking_Core *net = new_networking_no_udp(log, ns);
DHT *dht = new_dht(log, rng, ns, mono_time, net, true, true);
ASSERT_NE(dht, nullptr);
uint8_t pk_data[CRYPTO_PUBLIC_KEY_SIZE];
memcpy(pk_data, dht_get_self_public_key(dht), sizeof(pk_data));
PublicKey self_pk = to_array(pk_data);
PublicKey pk1 = random_pk(rng);
ASSERT_NE(pk1, self_pk);
// Test with maximally close key to self
pk_data[CRYPTO_PUBLIC_KEY_SIZE - 1] = ~pk_data[CRYPTO_PUBLIC_KEY_SIZE - 1];
PublicKey pk2 = to_array(pk_data);
ASSERT_NE(pk2, pk1);
IP_Port ip_port = {0};
ip_port.ip.family = net_family_ipv4();
set_announce_node(dht, pk1.data());
set_announce_node(dht, pk2.data());
EXPECT_TRUE(addto_lists(dht, &ip_port, pk1.data()));
EXPECT_TRUE(addto_lists(dht, &ip_port, pk2.data()));
Node_format nodes[MAX_SENT_NODES];
EXPECT_EQ(0, get_close_nodes(dht, self_pk.data(), nodes, net_family_unspec(), true, true));
set_announce_node(dht, pk1.data());
set_announce_node(dht, pk2.data());
EXPECT_EQ(2, get_close_nodes(dht, self_pk.data(), nodes, net_family_unspec(), true, true));
kill_dht(dht);
kill_networking(net);
mono_time_free(mono_time);
logger_kill(log);
}
} // namespace

View File

@ -0,0 +1,387 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2013 Tox project.
*/
/**
* LAN discovery implementation.
*/
#include "LAN_discovery.h"
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
// The mingw32/64 Windows library warns about including winsock2.h after
// windows.h even though with the above it's a valid thing to do. So, to make
// mingw32 headers happy, we include winsock2.h first.
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#endif
#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#ifdef __linux__
#include <linux/netdevice.h>
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__)
#include <net/if.h>
#endif
#include "ccompat.h"
#include "crypto_core.h"
#include "util.h"
#define MAX_INTERFACES 16
struct Broadcast_Info {
uint32_t count;
IP ips[MAX_INTERFACES];
};
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
non_null()
static Broadcast_Info *fetch_broadcast_info(const Network *ns)
{
Broadcast_Info *broadcast = (Broadcast_Info *)calloc(1, sizeof(Broadcast_Info));
if (broadcast == nullptr) {
return nullptr;
}
IP_ADAPTER_INFO *pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO));
unsigned long ulOutBufLen = sizeof(IP_ADAPTER_INFO);
if (pAdapterInfo == nullptr) {
free(broadcast);
return nullptr;
}
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
free(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
if (pAdapterInfo == nullptr) {
free(broadcast);
return nullptr;
}
}
const int ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
if (ret == NO_ERROR) {
IP_ADAPTER_INFO *pAdapter = pAdapterInfo;
while (pAdapter != nullptr) {
IP gateway = {0};
IP subnet_mask = {0};
if (addr_parse_ip(pAdapter->IpAddressList.IpMask.String, &subnet_mask)
&& addr_parse_ip(pAdapter->GatewayList.IpAddress.String, &gateway)) {
if (net_family_is_ipv4(gateway.family) && net_family_is_ipv4(subnet_mask.family)) {
IP *ip = &broadcast->ips[broadcast->count];
ip->family = net_family_ipv4();
const uint32_t gateway_ip = net_ntohl(gateway.ip.v4.uint32);
const uint32_t subnet_ip = net_ntohl(subnet_mask.ip.v4.uint32);
const uint32_t broadcast_ip = gateway_ip + ~subnet_ip - 1;
ip->ip.v4.uint32 = net_htonl(broadcast_ip);
++broadcast->count;
if (broadcast->count >= MAX_INTERFACES) {
break;
}
}
}
pAdapter = pAdapter->Next;
}
}
if (pAdapterInfo != nullptr) {
free(pAdapterInfo);
}
return broadcast;
}
#elif !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && (defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__))
non_null()
static Broadcast_Info *fetch_broadcast_info(const Network *ns)
{
Broadcast_Info *broadcast = (Broadcast_Info *)calloc(1, sizeof(Broadcast_Info));
if (broadcast == nullptr) {
return nullptr;
}
/* Not sure how many platforms this will run on,
* so it's wrapped in `__linux__` for now.
* Definitely won't work like this on Windows...
*/
const Socket sock = net_socket(ns, net_family_ipv4(), TOX_SOCK_STREAM, 0);
if (!sock_valid(sock)) {
free(broadcast);
return nullptr;
}
/* Configure ifconf for the ioctl call. */
struct ifreq i_faces[MAX_INTERFACES];
memset(i_faces, 0, sizeof(struct ifreq) * MAX_INTERFACES);
struct ifconf ifc;
ifc.ifc_buf = (char *)i_faces;
ifc.ifc_len = sizeof(i_faces);
if (ioctl(sock.sock, SIOCGIFCONF, &ifc) < 0) {
kill_sock(ns, sock);
free(broadcast);
return nullptr;
}
/* `ifc.ifc_len` is set by the `ioctl()` to the actual length used.
* On usage of the complete array the call should be repeated with
* a larger array, not done (640kB and 16 interfaces shall be
* enough, for everybody!)
*/
const int n = ifc.ifc_len / sizeof(struct ifreq);
for (int i = 0; i < n; ++i) {
/* there are interfaces with are incapable of broadcast */
if (ioctl(sock.sock, SIOCGIFBRDADDR, &i_faces[i]) < 0) {
continue;
}
/* moot check: only AF_INET returned (backwards compat.) */
if (i_faces[i].ifr_broadaddr.sa_family != AF_INET) {
continue;
}
const struct sockaddr_in *sock4 = (const struct sockaddr_in *)(void *)&i_faces[i].ifr_broadaddr;
if (broadcast->count >= MAX_INTERFACES) {
break;
}
IP *ip = &broadcast->ips[broadcast->count];
ip->family = net_family_ipv4();
ip->ip.v4.uint32 = sock4->sin_addr.s_addr;
if (ip->ip.v4.uint32 == 0) {
continue;
}
++broadcast->count;
}
kill_sock(ns, sock);
return broadcast;
}
#else // TODO(irungentoo): Other platforms?
non_null()
static Broadcast_Info *fetch_broadcast_info(const Network *ns)
{
return (Broadcast_Info *)calloc(1, sizeof(Broadcast_Info));
}
#endif
/** @brief Send packet to all IPv4 broadcast addresses
*
* @retval true if sent to at least one broadcast target.
* @retval false on failure to find any valid broadcast target.
*/
non_null()
static bool send_broadcasts(const Networking_Core *net, const Broadcast_Info *broadcast, uint16_t port,
const uint8_t *data, uint16_t length)
{
if (broadcast->count == 0) {
return false;
}
for (uint32_t i = 0; i < broadcast->count; ++i) {
IP_Port ip_port;
ip_port.ip = broadcast->ips[i];
ip_port.port = port;
sendpacket(net, &ip_port, data, length);
}
return true;
}
/** Return the broadcast ip. */
static IP broadcast_ip(Family family_socket, Family family_broadcast)
{
IP ip;
ip_reset(&ip);
if (net_family_is_ipv6(family_socket)) {
if (net_family_is_ipv6(family_broadcast)) {
ip.family = net_family_ipv6();
/* `FF02::1` is - according to RFC 4291 - multicast all-nodes link-local */
/* `FE80::*:` MUST be exact, for that we would need to look over all
* interfaces and check in which status they are */
ip.ip.v6.uint8[ 0] = 0xFF;
ip.ip.v6.uint8[ 1] = 0x02;
ip.ip.v6.uint8[15] = 0x01;
} else if (net_family_is_ipv4(family_broadcast)) {
ip.family = net_family_ipv6();
ip.ip.v6 = ip6_broadcast;
}
} else if (net_family_is_ipv4(family_socket) && net_family_is_ipv4(family_broadcast)) {
ip.family = net_family_ipv4();
ip.ip.v4 = ip4_broadcast;
}
return ip;
}
non_null()
static bool ip4_is_local(const IP4 *ip4)
{
/* Loopback. */
return ip4->uint8[0] == 127;
}
/**
* Is IP a local ip or not.
*/
bool ip_is_local(const IP *ip)
{
if (net_family_is_ipv4(ip->family)) {
return ip4_is_local(&ip->ip.v4);
}
/* embedded IPv4-in-IPv6 */
if (ipv6_ipv4_in_v6(&ip->ip.v6)) {
IP4 ip4;
ip4.uint32 = ip->ip.v6.uint32[3];
return ip4_is_local(&ip4);
}
/* localhost in IPv6 (::1) */
return ip->ip.v6.uint64[0] == 0 && ip->ip.v6.uint32[2] == 0 && ip->ip.v6.uint32[3] == net_htonl(1);
}
non_null()
static bool ip4_is_lan(const IP4 *ip4)
{
/* 10.0.0.0 to 10.255.255.255 range. */
if (ip4->uint8[0] == 10) {
return true;
}
/* 172.16.0.0 to 172.31.255.255 range. */
if (ip4->uint8[0] == 172 && ip4->uint8[1] >= 16 && ip4->uint8[1] <= 31) {
return true;
}
/* 192.168.0.0 to 192.168.255.255 range. */
if (ip4->uint8[0] == 192 && ip4->uint8[1] == 168) {
return true;
}
/* 169.254.1.0 to 169.254.254.255 range. */
if (ip4->uint8[0] == 169 && ip4->uint8[1] == 254 && ip4->uint8[2] != 0
&& ip4->uint8[2] != 255) {
return true;
}
/* RFC 6598: 100.64.0.0 to 100.127.255.255 (100.64.0.0/10)
* (shared address space to stack another layer of NAT) */
return (ip4->uint8[0] == 100) && ((ip4->uint8[1] & 0xC0) == 0x40);
}
bool ip_is_lan(const IP *ip)
{
if (ip_is_local(ip)) {
return true;
}
if (net_family_is_ipv4(ip->family)) {
return ip4_is_lan(&ip->ip.v4);
}
if (net_family_is_ipv6(ip->family)) {
/* autogenerated for each interface: `FE80::*` (up to `FEBF::*`)
* `FF02::1` is - according to RFC 4291 - multicast all-nodes link-local */
if (((ip->ip.v6.uint8[0] == 0xFF) && (ip->ip.v6.uint8[1] < 3) && (ip->ip.v6.uint8[15] == 1)) ||
((ip->ip.v6.uint8[0] == 0xFE) && ((ip->ip.v6.uint8[1] & 0xC0) == 0x80))) {
return true;
}
/* embedded IPv4-in-IPv6 */
if (ipv6_ipv4_in_v6(&ip->ip.v6)) {
IP4 ip4;
ip4.uint32 = ip->ip.v6.uint32[3];
return ip4_is_lan(&ip4);
}
}
return false;
}
bool lan_discovery_send(const Networking_Core *net, const Broadcast_Info *broadcast, const uint8_t *dht_pk,
uint16_t port)
{
if (broadcast == nullptr) {
return false;
}
uint8_t data[CRYPTO_PUBLIC_KEY_SIZE + 1];
data[0] = NET_PACKET_LAN_DISCOVERY;
pk_copy(data + 1, dht_pk);
send_broadcasts(net, broadcast, port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE);
bool res = false;
IP_Port ip_port;
ip_port.port = port;
/* IPv6 multicast */
if (net_family_is_ipv6(net_family(net))) {
ip_port.ip = broadcast_ip(net_family_ipv6(), net_family_ipv6());
if (ip_isset(&ip_port.ip) && sendpacket(net, &ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE) > 0) {
res = true;
}
}
/* IPv4 broadcast (has to be IPv4-in-IPv6 mapping if socket is IPv6 */
ip_port.ip = broadcast_ip(net_family(net), net_family_ipv4());
if (ip_isset(&ip_port.ip) && sendpacket(net, &ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE) > 0) {
res = true;
}
return res;
}
Broadcast_Info *lan_discovery_init(const Network *ns)
{
return fetch_broadcast_info(ns);
}
void lan_discovery_kill(Broadcast_Info *broadcast)
{
free(broadcast);
}

View File

@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2013 Tox project.
*/
/**
* LAN discovery implementation.
*/
#ifndef C_TOXCORE_TOXCORE_LAN_DISCOVERY_H
#define C_TOXCORE_TOXCORE_LAN_DISCOVERY_H
#include "network.h"
/**
* Interval in seconds between LAN discovery packet sending.
*/
#define LAN_DISCOVERY_INTERVAL 10
typedef struct Broadcast_Info Broadcast_Info;
/**
* Send a LAN discovery pcaket to the broadcast address with port port.
*
* @return true on success, false on failure.
*/
non_null()
bool lan_discovery_send(const Networking_Core *net, const Broadcast_Info *broadcast, const uint8_t *dht_pk,
uint16_t port);
/**
* Discovers broadcast devices and IP addresses.
*/
non_null()
Broadcast_Info *lan_discovery_init(const Network *ns);
/**
* Free all resources associated with the broadcast info.
*/
nullable(1)
void lan_discovery_kill(Broadcast_Info *broadcast);
/**
* Is IP a local ip or not.
*/
non_null()
bool ip_is_local(const IP *ip);
/**
* Checks if a given IP isn't routable.
*
* @return true if ip is a LAN ip, false if it is not.
*/
non_null()
bool ip_is_lan(const IP *ip);
#endif // C_TOXCORE_TOXCORE_LAN_DISCOVERY_H

View File

@ -0,0 +1,165 @@
lib_LTLIBRARIES += libtoxcore.la
libtoxcore_la_include_HEADERS = \
../toxcore/tox.h
libtoxcore_la_includedir = $(includedir)/tox
libtoxcore_la_SOURCES = ../third_party/cmp/cmp.c \
../third_party/cmp/cmp.h \
../toxcore/attributes.h \
../toxcore/bin_pack.c \
../toxcore/bin_pack.h \
../toxcore/bin_unpack.c \
../toxcore/bin_unpack.h \
../toxcore/ccompat.c \
../toxcore/ccompat.h \
../toxcore/events/conference_connected.c \
../toxcore/events/conference_invite.c \
../toxcore/events/conference_message.c \
../toxcore/events/conference_peer_list_changed.c \
../toxcore/events/conference_peer_name.c \
../toxcore/events/conference_title.c \
../toxcore/events/file_chunk_request.c \
../toxcore/events/file_recv.c \
../toxcore/events/file_recv_chunk.c \
../toxcore/events/file_recv_control.c \
../toxcore/events/friend_connection_status.c \
../toxcore/events/friend_lossless_packet.c \
../toxcore/events/friend_lossy_packet.c \
../toxcore/events/friend_message.c \
../toxcore/events/friend_name.c \
../toxcore/events/friend_read_receipt.c \
../toxcore/events/friend_request.c \
../toxcore/events/friend_status.c \
../toxcore/events/friend_status_message.c \
../toxcore/events/friend_typing.c \
../toxcore/events/events_alloc.c \
../toxcore/events/events_alloc.h \
../toxcore/events/self_connection_status.c \
../toxcore/events/group_custom_packet.c \
../toxcore/events/group_custom_private_packet.c \
../toxcore/events/group_invite.c \
../toxcore/events/group_join_fail.c \
../toxcore/events/group_message.c \
../toxcore/events/group_moderation.c \
../toxcore/events/group_password.c \
../toxcore/events/group_peer_exit.c \
../toxcore/events/group_peer_join.c \
../toxcore/events/group_peer_limit.c \
../toxcore/events/group_peer_name.c \
../toxcore/events/group_peer_status.c \
../toxcore/events/group_privacy_state.c \
../toxcore/events/group_private_message.c \
../toxcore/events/group_self_join.c \
../toxcore/events/group_topic.c \
../toxcore/events/group_topic_lock.c \
../toxcore/events/group_voice_state.c \
../toxcore/DHT.h \
../toxcore/DHT.c \
../toxcore/mono_time.h \
../toxcore/mono_time.c \
../toxcore/network.h \
../toxcore/network.c \
../toxcore/crypto_core.h \
../toxcore/crypto_core.c \
../toxcore/timed_auth.h \
../toxcore/timed_auth.c \
../toxcore/ping_array.h \
../toxcore/ping_array.c \
../toxcore/net_crypto.h \
../toxcore/net_crypto.c \
../toxcore/friend_requests.h \
../toxcore/friend_requests.c \
../toxcore/LAN_discovery.h \
../toxcore/LAN_discovery.c \
../toxcore/friend_connection.h \
../toxcore/friend_connection.c \
../toxcore/Messenger.h \
../toxcore/Messenger.c \
../toxcore/ping.h \
../toxcore/ping.c \
../toxcore/shared_key_cache.h \
../toxcore/shared_key_cache.c \
../toxcore/state.h \
../toxcore/state.c \
../toxcore/tox.h \
../toxcore/tox.c \
../toxcore/tox_dispatch.h \
../toxcore/tox_dispatch.c \
../toxcore/tox_events.h \
../toxcore/tox_events.c \
../toxcore/tox_unpack.h \
../toxcore/tox_unpack.c \
../toxcore/tox_private.c \
../toxcore/tox_private.h \
../toxcore/tox_struct.h \
../toxcore/tox_api.c \
../toxcore/util.h \
../toxcore/util.c \
../toxcore/group.h \
../toxcore/group.c \
../toxcore/group_announce.h \
../toxcore/group_announce.c \
../toxcore/group_onion_announce.c \
../toxcore/group_onion_announce.h \
../toxcore/group_chats.h \
../toxcore/group_chats.c \
../toxcore/group_common.h \
../toxcore/group_connection.c \
../toxcore/group_connection.h \
../toxcore/group_pack.c \
../toxcore/group_pack.h \
../toxcore/group_moderation.c \
../toxcore/group_moderation.h \
../toxcore/onion.h \
../toxcore/onion.c \
../toxcore/logger.h \
../toxcore/logger.c \
../toxcore/onion_announce.h \
../toxcore/onion_announce.c \
../toxcore/onion_client.h \
../toxcore/onion_client.c \
../toxcore/announce.h \
../toxcore/announce.c \
../toxcore/forwarding.h \
../toxcore/forwarding.c \
../toxcore/TCP_client.h \
../toxcore/TCP_client.c \
../toxcore/TCP_common.h \
../toxcore/TCP_common.c \
../toxcore/TCP_server.h \
../toxcore/TCP_server.c \
../toxcore/TCP_connection.h \
../toxcore/TCP_connection.c \
../toxcore/list.c \
../toxcore/list.h
libtoxcore_la_CFLAGS = -I$(top_srcdir) \
-I$(top_srcdir)/toxcore \
$(LIBSODIUM_CFLAGS) \
$(NACL_CFLAGS) \
$(MSGPACK_CFLAGS) \
$(PTHREAD_CFLAGS) \
-DCMP_NO_FLOAT=1
libtoxcore_la_LDFLAGS = $(LT_LDFLAGS) \
$(EXTRA_LT_LDFLAGS) \
$(LIBSODIUM_LDFLAGS) \
$(NACL_LDFLAGS) \
$(MSGPACK_LDFLAGS) \
$(MATH_LDFLAGS) \
$(RT_LIBS) \
$(WINSOCK2_LIBS)
libtoxcore_la_LIBADD = $(LIBSODIUM_LIBS) \
$(NACL_OBJECTS) \
$(NACL_LIBS) \
$(MSGPACK_LIBS) \
$(PTHREAD_LIBS)
if SET_SO_VERSION
EXTRA_libtoxcore_la_DEPENDENCIES = ../so.version
endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,891 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2013 Tox project.
*/
/**
* An implementation of a simple text chat only messenger on the tox network
* core.
*/
#ifndef C_TOXCORE_TOXCORE_MESSENGER_H
#define C_TOXCORE_TOXCORE_MESSENGER_H
#include "TCP_server.h"
#include "announce.h"
#include "forwarding.h"
#include "friend_connection.h"
#include "friend_requests.h"
#include "group_announce.h"
#include "group_common.h"
#include "logger.h"
#include "net_crypto.h"
#include "state.h"
#define MAX_NAME_LENGTH 128
/* TODO(irungentoo): this must depend on other variable. */
#define MAX_STATUSMESSAGE_LENGTH 1007
/* Used for TCP relays in Messenger struct (may need to be `% 2 == 0`)*/
#define NUM_SAVED_TCP_RELAYS 8
/* This cannot be bigger than 256 */
#define MAX_CONCURRENT_FILE_PIPES 256
#define FRIEND_ADDRESS_SIZE (CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint32_t) + sizeof(uint16_t))
typedef enum Message_Type {
MESSAGE_NORMAL,
MESSAGE_ACTION,
} Message_Type;
// TODO(Jfreegman, Iphy): Remove this before merge
#ifndef MESSENGER_DEFINED
#define MESSENGER_DEFINED
typedef struct Messenger Messenger;
#endif // MESSENGER_DEFINED
// Returns the size of the data
typedef uint32_t m_state_size_cb(const Messenger *m);
// Returns the new pointer to data
typedef uint8_t *m_state_save_cb(const Messenger *m, uint8_t *data);
// Returns if there were any erros during loading
typedef State_Load_Status m_state_load_cb(Messenger *m, const uint8_t *data, uint32_t length);
typedef struct Messenger_State_Plugin {
State_Type type;
m_state_size_cb *size;
m_state_save_cb *save;
m_state_load_cb *load;
} Messenger_State_Plugin;
typedef struct Messenger_Options {
bool ipv6enabled;
bool udp_disabled;
TCP_Proxy_Info proxy_info;
uint16_t port_range[2];
uint16_t tcp_server_port;
bool hole_punching_enabled;
bool local_discovery_enabled;
bool dht_announcements_enabled;
logger_cb *log_callback;
void *log_context;
void *log_user_data;
Messenger_State_Plugin *state_plugins;
uint8_t state_plugins_length;
} Messenger_Options;
struct Receipts {
uint32_t packet_num;
uint32_t msg_id;
struct Receipts *next;
};
/** Status definitions. */
typedef enum Friend_Status {
NOFRIEND,
FRIEND_ADDED,
FRIEND_REQUESTED,
FRIEND_CONFIRMED,
FRIEND_ONLINE,
} Friend_Status;
/** @brief Errors for m_addfriend
*
* FAERR - Friend Add Error
*/
typedef enum Friend_Add_Error {
FAERR_TOOLONG = -1,
FAERR_NOMESSAGE = -2,
FAERR_OWNKEY = -3,
FAERR_ALREADYSENT = -4,
FAERR_BADCHECKSUM = -6,
FAERR_SETNEWNOSPAM = -7,
FAERR_NOMEM = -8,
} Friend_Add_Error;
/** Default start timeout in seconds between friend requests. */
#define FRIENDREQUEST_TIMEOUT 5
typedef enum Connection_Status {
CONNECTION_NONE,
CONNECTION_TCP,
CONNECTION_UDP,
} Connection_Status;
/**
* Represents userstatuses someone can have.
*/
typedef enum Userstatus {
USERSTATUS_NONE,
USERSTATUS_AWAY,
USERSTATUS_BUSY,
USERSTATUS_INVALID,
} Userstatus;
#define FILE_ID_LENGTH 32
struct File_Transfers {
uint64_t size;
uint64_t transferred;
uint8_t status; /* 0 == no transfer, 1 = not accepted, 3 = transferring, 4 = broken, 5 = finished */
uint8_t paused; /* 0: not paused, 1 = paused by us, 2 = paused by other, 3 = paused by both. */
uint32_t last_packet_number; /* number of the last packet sent. */
uint64_t requested; /* total data requested by the request chunk callback */
uint8_t id[FILE_ID_LENGTH];
};
typedef enum Filestatus {
FILESTATUS_NONE,
FILESTATUS_NOT_ACCEPTED,
FILESTATUS_TRANSFERRING,
// FILESTATUS_BROKEN,
FILESTATUS_FINISHED,
} Filestatus;
typedef enum File_Pause {
FILE_PAUSE_NOT,
FILE_PAUSE_US,
FILE_PAUSE_OTHER,
FILE_PAUSE_BOTH,
} File_Pause;
typedef enum Filecontrol {
FILECONTROL_ACCEPT,
FILECONTROL_PAUSE,
FILECONTROL_KILL,
FILECONTROL_SEEK,
} Filecontrol;
typedef enum Filekind {
FILEKIND_DATA,
FILEKIND_AVATAR,
} Filekind;
typedef void m_self_connection_status_cb(Messenger *m, Onion_Connection_Status connection_status, void *user_data);
typedef void m_friend_status_cb(Messenger *m, uint32_t friend_number, unsigned int status, void *user_data);
typedef void m_friend_connection_status_cb(Messenger *m, uint32_t friend_number, unsigned int connection_status,
void *user_data);
typedef void m_friend_message_cb(Messenger *m, uint32_t friend_number, unsigned int message_type,
const uint8_t *message, size_t length, void *user_data);
typedef void m_file_recv_control_cb(Messenger *m, uint32_t friend_number, uint32_t file_number, unsigned int control,
void *user_data);
typedef void m_friend_request_cb(Messenger *m, const uint8_t *public_key, const uint8_t *message, size_t length,
void *user_data);
typedef void m_friend_name_cb(Messenger *m, uint32_t friend_number, const uint8_t *name, size_t length,
void *user_data);
typedef void m_friend_status_message_cb(Messenger *m, uint32_t friend_number, const uint8_t *message, size_t length,
void *user_data);
typedef void m_friend_typing_cb(Messenger *m, uint32_t friend_number, bool is_typing, void *user_data);
typedef void m_friend_read_receipt_cb(Messenger *m, uint32_t friend_number, uint32_t message_id, void *user_data);
typedef void m_file_recv_cb(Messenger *m, uint32_t friend_number, uint32_t file_number, uint32_t kind,
uint64_t file_size, const uint8_t *filename, size_t filename_length, void *user_data);
typedef void m_file_chunk_request_cb(Messenger *m, uint32_t friend_number, uint32_t file_number, uint64_t position,
size_t length, void *user_data);
typedef void m_file_recv_chunk_cb(Messenger *m, uint32_t friend_number, uint32_t file_number, uint64_t position,
const uint8_t *data, size_t length, void *user_data);
typedef void m_friend_lossy_packet_cb(Messenger *m, uint32_t friend_number, uint8_t packet_id, const uint8_t *data,
size_t length, void *user_data);
typedef void m_friend_lossless_packet_cb(Messenger *m, uint32_t friend_number, uint8_t packet_id, const uint8_t *data,
size_t length, void *user_data);
typedef void m_friend_connectionstatuschange_internal_cb(Messenger *m, uint32_t friend_number,
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(const 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);
typedef struct RTP_Packet_Handler {
m_lossy_rtp_packet_cb *function;
void *object;
} RTP_Packet_Handler;
typedef struct Friend {
uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
int friendcon_id;
uint64_t friendrequest_lastsent; // Time at which the last friend request was sent.
uint32_t friendrequest_timeout; // The timeout between successful friendrequest sending attempts.
uint8_t status; // 0 if no friend, 1 if added, 2 if friend request sent, 3 if confirmed friend, 4 if online.
uint8_t info[MAX_FRIEND_REQUEST_DATA_SIZE]; // the data that is sent during the friend requests we do.
uint8_t name[MAX_NAME_LENGTH];
uint16_t name_length;
bool name_sent; // false if we didn't send our name to this friend, true if we have.
uint8_t statusmessage[MAX_STATUSMESSAGE_LENGTH];
uint16_t statusmessage_length;
bool statusmessage_sent;
Userstatus userstatus;
bool userstatus_sent;
bool user_istyping;
bool user_istyping_sent;
bool is_typing;
uint16_t info_size; // Length of the info.
uint32_t message_id; // a semi-unique id used in read receipts.
uint32_t friendrequest_nospam; // The nospam number used in the friend request.
uint64_t last_seen_time;
Connection_Status last_connection_udp_tcp;
struct File_Transfers file_sending[MAX_CONCURRENT_FILE_PIPES];
uint32_t num_sending_files;
struct File_Transfers file_receiving[MAX_CONCURRENT_FILE_PIPES];
RTP_Packet_Handler lossy_rtp_packethandlers[PACKET_ID_RANGE_LOSSY_AV_SIZE];
struct Receipts *receipts_start;
struct Receipts *receipts_end;
} Friend;
struct Messenger {
Logger *log;
Mono_Time *mono_time;
const Random *rng;
const Network *ns;
Networking_Core *net;
Net_Crypto *net_crypto;
DHT *dht;
Forwarding *forwarding;
Announcements *announce;
Onion *onion;
Onion_Announce *onion_a;
Onion_Client *onion_c;
Friend_Connections *fr_c;
TCP_Server *tcp_server;
Friend_Requests *fr;
uint8_t name[MAX_NAME_LENGTH];
uint16_t name_length;
uint8_t statusmessage[MAX_STATUSMESSAGE_LENGTH];
uint16_t statusmessage_length;
Userstatus userstatus;
Friend *friendlist;
uint32_t numfriends;
uint64_t lastdump;
uint8_t is_receiving_file;
GC_Session *group_handler;
GC_Announces_List *group_announce;
bool has_added_relays; // If the first connection has occurred in do_messenger
uint16_t num_loaded_relays;
Node_format loaded_relays[NUM_SAVED_TCP_RELAYS]; // Relays loaded from config
m_friend_request_cb *friend_request;
m_friend_message_cb *friend_message;
m_friend_name_cb *friend_namechange;
m_friend_status_message_cb *friend_statusmessagechange;
m_friend_status_cb *friend_userstatuschange;
m_friend_typing_cb *friend_typingchange;
m_friend_read_receipt_cb *read_receipt;
m_friend_connection_status_cb *friend_connectionstatuschange;
m_friend_connectionstatuschange_internal_cb *friend_connectionstatuschange_internal;
void *friend_connectionstatuschange_internal_userdata;
struct Group_Chats *conferences_object; /* Set by new_groupchats()*/
m_conference_invite_cb *conference_invite;
m_group_invite_cb *group_invite;
m_file_recv_cb *file_sendrequest;
m_file_recv_control_cb *file_filecontrol;
m_file_recv_chunk_cb *file_filedata;
m_file_chunk_request_cb *file_reqchunk;
m_msi_packet_cb *msi_packet;
void *msi_packet_userdata;
m_friend_lossy_packet_cb *lossy_packethandler;
m_friend_lossless_packet_cb *lossless_packethandler;
m_self_connection_status_cb *core_connection_change;
Onion_Connection_Status last_connection_status;
Messenger_Options options;
};
/**
* Determines if the friendnumber passed is valid in the Messenger object.
*
* @param friendnumber The index in the friend list.
*/
non_null()
bool friend_is_valid(const Messenger *m, int32_t friendnumber);
/**
* Format: `[real_pk (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]`
*
* @param[out] address FRIEND_ADDRESS_SIZE byte address to give to others.
*/
non_null()
void getaddress(const Messenger *m, uint8_t *address);
/**
* Add a friend.
*
* Set the data that will be sent along with friend request.
*
* @param address is the address of the friend (returned by getaddress of the friend
* you wish to add) it must be FRIEND_ADDRESS_SIZE bytes.
* TODO(irungentoo): add checksum.
* @param data is the data.
* @param length is the length.
*
* @return the friend number if success.
* @retval FA_TOOLONG if message length is too long.
* @retval FAERR_NOMESSAGE if no message (message length must be >= 1 byte).
* @retval FAERR_OWNKEY if user's own key.
* @retval FAERR_ALREADYSENT if friend request already sent or already a friend.
* @retval FAERR_BADCHECKSUM if bad checksum in address.
* @retval FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different.
* (the nospam for that friend was set to the new one).
* @retval FAERR_NOMEM if increasing the friend list size fails.
*/
non_null()
int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, uint16_t length);
/** @brief Add a friend without sending a friendrequest.
* @return the friend number if success.
* @retval -3 if user's own key.
* @retval -4 if friend request already sent or already a friend.
* @retval -6 if bad checksum in address.
* @retval -8 if increasing the friend list size fails.
*/
non_null()
int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk);
/** @brief Initializes the friend connection and onion connection for a groupchat.
*
* @retval true on success.
*/
non_null()
bool m_create_group_connection(Messenger *m, GC_Chat *chat);
/*
* Kills the friend connection for a groupchat.
*/
non_null()
void m_kill_group_connection(Messenger *m, const GC_Chat *chat);
/** @return the friend number associated to that public key.
* @retval -1 if no such friend.
*/
non_null()
int32_t getfriend_id(const Messenger *m, const uint8_t *real_pk);
/** @brief 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.
*
* @retval 0 if success.
* @retval -1 if failure.
*/
non_null()
int get_real_pk(const Messenger *m, int32_t friendnumber, uint8_t *real_pk);
/** @return friend connection id on success.
* @retval -1 if failure.
*/
non_null()
int getfriendcon_id(const Messenger *m, int32_t friendnumber);
/** @brief Remove a friend.
*
* @retval 0 if success.
* @retval -1 if failure.
*/
non_null()
int m_delfriend(Messenger *m, int32_t friendnumber);
/** @brief Checks friend's connection status.
*
* @retval CONNECTION_UDP (2) if friend is directly connected to us (Online UDP).
* @retval CONNECTION_TCP (1) if friend is connected to us (Online TCP).
* @retval CONNECTION_NONE (0) if friend is not connected to us (Offline).
* @retval -1 on failure.
*/
non_null()
int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber);
/**
* Checks if there exists a friend with given friendnumber.
*
* @param friendnumber The index in the friend list.
*
* @retval true if friend exists.
* @retval false if friend doesn't exist.
*/
non_null()
bool m_friend_exists(const Messenger *m, int32_t friendnumber);
/** @brief Send a message of type to an online friend.
*
* @retval -1 if friend not valid.
* @retval -2 if too large.
* @retval -3 if friend not online.
* @retval -4 if send failed (because queue is full).
* @retval -5 if bad type.
* @retval 0 if success.
*
* The value in message_id will be passed to your read_receipt callback when the other receives the message.
*/
non_null(1, 4) nullable(6)
int m_send_message_generic(Messenger *m, int32_t friendnumber, uint8_t type, const uint8_t *message, uint32_t length,
uint32_t *message_id);
/** @brief Set the name and name_length of a friend.
*
* name must be a string of maximum MAX_NAME_LENGTH length.
* length must be at least 1 byte.
* length is the length of name with the NULL terminator.
*
* @retval 0 if success.
* @retval -1 if failure.
*/
non_null()
int setfriendname(Messenger *m, int32_t friendnumber, const uint8_t *name, uint16_t length);
/** @brief Set our nickname.
*
* name must be a string of maximum MAX_NAME_LENGTH length.
* length must be at least 1 byte.
* length is the length of name with the NULL terminator.
*
* @retval 0 if success.
* @retval -1 if failure.
*/
non_null()
int setname(Messenger *m, const uint8_t *name, uint16_t length);
/**
* @brief Get your nickname.
*
* m - The messenger context to use.
* name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH bytes.
*
* @return length of the name.
* @retval 0 on error.
*/
non_null()
uint16_t getself_name(const Messenger *m, uint8_t *name);
/** @brief Get name of friendnumber and put it in name.
*
* name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes.
*
* @return length of name if success.
* @retval -1 if failure.
*/
non_null()
int getname(const Messenger *m, int32_t friendnumber, uint8_t *name);
/** @return the length of name, including null on success.
* @retval -1 on failure.
*/
non_null() int m_get_name_size(const Messenger *m, int32_t friendnumber);
non_null() int m_get_self_name_size(const Messenger *m);
/** @brief Set our user status.
* You are responsible for freeing status after.
*
* @retval 0 on success.
* @retval -1 on failure.
*/
non_null() int m_set_statusmessage(Messenger *m, const uint8_t *status, uint16_t length);
non_null() int m_set_userstatus(Messenger *m, uint8_t status);
/**
* Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH.
*
* @return the length of friendnumber's status message, including null on success.
* @retval -1 on failure.
*/
non_null() int m_get_statusmessage_size(const Messenger *m, int32_t friendnumber);
non_null() int m_get_self_statusmessage_size(const Messenger *m);
/** @brief Copy friendnumber's status message into buf, truncating if size is over maxlen.
*
* Get the size you need to allocate from m_get_statusmessage_size.
* The self variant will copy our own status message.
*
* @return the length of the copied data on success
* @retval -1 on failure.
*/
non_null() int m_copy_statusmessage(const Messenger *m, int32_t friendnumber, uint8_t *buf, uint32_t maxlen);
non_null() int m_copy_self_statusmessage(const Messenger *m, uint8_t *buf);
/** @brief return one of Userstatus values.
*
* Values unknown to your application should be represented as USERSTATUS_NONE.
* As above, the self variant will return our own Userstatus.
* If friendnumber is invalid, this shall return USERSTATUS_INVALID.
*/
non_null() uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber);
non_null() uint8_t m_get_self_userstatus(const Messenger *m);
/** @brief returns timestamp of last time friendnumber was seen online or 0 if never seen.
* if friendnumber is invalid this function will return UINT64_MAX.
*/
non_null() uint64_t m_get_last_online(const Messenger *m, int32_t friendnumber);
/** @brief Set our typing status for a friend.
* You are responsible for turning it on or off.
*
* @retval 0 on success.
* @retval -1 on failure.
*/
non_null()
int m_set_usertyping(Messenger *m, int32_t friendnumber, bool is_typing);
/** @brief Get the typing status of a friend.
*
* @retval -1 if friend number is invalid.
* @retval 0 if friend is not typing.
* @retval 1 if friend is typing.
*/
non_null()
int m_get_istyping(const Messenger *m, int32_t friendnumber);
/** Set the function that will be executed when a friend request is received. */
non_null(1) nullable(2)
void m_callback_friendrequest(Messenger *m, m_friend_request_cb *function);
/** Set the function that will be executed when a message from a friend is received. */
non_null() void m_callback_friendmessage(Messenger *m, m_friend_message_cb *function);
/** @brief Set the callback for name changes.
* You are not responsible for freeing newname.
*/
non_null() void m_callback_namechange(Messenger *m, m_friend_name_cb *function);
/** @brief Set the callback for status message changes.
*
* You are not responsible for freeing newstatus
*/
non_null() void m_callback_statusmessage(Messenger *m, m_friend_status_message_cb *function);
/** @brief Set the callback for status type changes. */
non_null() void m_callback_userstatus(Messenger *m, m_friend_status_cb *function);
/** @brief Set the callback for typing changes. */
non_null() void m_callback_typingchange(Messenger *m, m_friend_typing_cb *function);
/** @brief Set the callback for read receipts.
*
* If you are keeping a record of returns from m_sendmessage,
* receipt might be one of those values, meaning the message
* has been received on the other side.
* Since core doesn't track ids for you, receipt may not correspond to any message.
* In that case, you should discard it.
*/
non_null() void m_callback_read_receipt(Messenger *m, m_friend_read_receipt_cb *function);
/** @brief Set the callback for connection status changes.
*
* Status:
* - 0: friend went offline after being previously online.
* - 1: friend went online.
*
* Note that this callback is not called when adding friends, thus the
* "after being previously online" part.
* It's assumed that when adding friends, their connection status is offline.
*/
non_null() void m_callback_connectionstatus(Messenger *m, m_friend_connection_status_cb *function);
/** Same as previous but for internal A/V core usage only */
non_null() void m_callback_connectionstatus_internal_av(
Messenger *m, m_friend_connectionstatuschange_internal_cb *function, void *userdata);
/** @brief Set the callback for typing changes. */
non_null() void m_callback_core_connection(Messenger *m, m_self_connection_status_cb *function);
/*** CONFERENCES */
/** @brief Set the callback for conference invites. */
non_null(1) nullable(2)
void m_callback_conference_invite(Messenger *m, m_conference_invite_cb *function);
/* Set the callback for group invites.
*/
non_null(1) nullable(2)
void m_callback_group_invite(Messenger *m, m_group_invite_cb *function);
/** @brief Send a conference invite packet.
*
* return true on success
* return false on failure
*/
non_null()
bool 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 true on success
*/
non_null()
bool send_group_invite_packet(const Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length);
/*** FILE SENDING */
/** @brief Set the callback for file send requests. */
non_null() void callback_file_sendrequest(Messenger *m, m_file_recv_cb *function);
/** @brief Set the callback for file control requests. */
non_null() void callback_file_control(Messenger *m, m_file_recv_control_cb *function);
/** @brief Set the callback for file data. */
non_null() void callback_file_data(Messenger *m, m_file_recv_chunk_cb *function);
/** @brief Set the callback for file request chunk. */
non_null() void callback_file_reqchunk(Messenger *m, m_file_chunk_request_cb *function);
/** @brief Copy the file transfer file id to file_id
*
* @retval 0 on success.
* @retval -1 if friend not valid.
* @retval -2 if filenumber not valid
*/
non_null()
int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint8_t *file_id);
/** @brief Send a file send request.
*
* Maximum filename length is 255 bytes.
*
* @return file number on success
* @retval -1 if friend not found.
* @retval -2 if filename length invalid.
* @retval -3 if no more file sending slots left.
* @retval -4 if could not send packet (friend offline).
*/
non_null()
long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_type, uint64_t filesize,
const uint8_t *file_id, const uint8_t *filename, uint16_t filename_length);
/** @brief Send a file control request.
*
* @retval 0 on success
* @retval -1 if friend not valid.
* @retval -2 if friend not online.
* @retval -3 if file number invalid.
* @retval -4 if file control is bad.
* @retval -5 if file already paused.
* @retval -6 if resume file failed because it was only paused by the other.
* @retval -7 if resume file failed because it wasn't paused.
* @retval -8 if packet failed to send.
*/
non_null()
int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, unsigned int control);
/** @brief Send a seek file control request.
*
* @retval 0 on success
* @retval -1 if friend not valid.
* @retval -2 if friend not online.
* @retval -3 if file number invalid.
* @retval -4 if not receiving file.
* @retval -5 if file status wrong.
* @retval -6 if position bad.
* @retval -8 if packet failed to send.
*/
non_null()
int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position);
/** @brief Send file data.
*
* @retval 0 on success
* @retval -1 if friend not valid.
* @retval -2 if friend not online.
* @retval -3 if filenumber invalid.
* @retval -4 if file transfer not transferring.
* @retval -5 if bad data size.
* @retval -6 if packet queue full.
* @retval -7 if wrong position.
*/
non_null(1) nullable(5)
int send_file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position,
const uint8_t *data, uint16_t length);
/*** A/V related */
/** @brief Set the callback for msi packets. */
non_null(1) nullable(2, 3)
void m_callback_msi_packet(Messenger *m, m_msi_packet_cb *function, void *userdata);
/** @brief Send an msi packet.
*
* @retval true on success
* @retval false on failure
*/
non_null()
bool m_msi_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length);
/** @brief Set handlers for lossy rtp packets.
*
* @retval -1 on failure.
* @retval 0 on success.
*/
non_null(1) nullable(4, 5)
int m_callback_rtp_packet(Messenger *m, int32_t friendnumber, uint8_t byte,
m_lossy_rtp_packet_cb *function, void *object);
/*** CUSTOM PACKETS */
/** @brief Set handlers for custom lossy packets. */
non_null() void custom_lossy_packet_registerhandler(Messenger *m, m_friend_lossy_packet_cb *lossy_packethandler);
/** @brief High level function to send custom lossy packets.
*
* TODO(oxij): this name is confusing, because this function sends both av and custom lossy packets.
* Meanwhile, m_handle_lossy_packet routes custom packets to custom_lossy_packet_registerhandler
* as you would expect from its name.
*
* I.e. custom_lossy_packet_registerhandler's "custom lossy packet" and this "custom lossy packet"
* are not the same set of packets.
*
* @retval -1 if friend invalid.
* @retval -2 if length wrong.
* @retval -3 if first byte invalid.
* @retval -4 if friend offline.
* @retval -5 if packet failed to send because of other error.
* @retval 0 on success.
*/
non_null()
int m_send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length);
/** @brief Set handlers for custom lossless packets. */
non_null()
void custom_lossless_packet_registerhandler(Messenger *m, m_friend_lossless_packet_cb *lossless_packethandler);
/** @brief High level function to send custom lossless packets.
*
* @retval -1 if friend invalid.
* @retval -2 if length wrong.
* @retval -3 if first byte invalid.
* @retval -4 if friend offline.
* @retval -5 if packet failed to send because of other error.
* @retval 0 on success.
*/
non_null()
int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length);
/*** Messenger constructor/destructor/operations. */
typedef enum Messenger_Error {
MESSENGER_ERROR_NONE,
MESSENGER_ERROR_PORT,
MESSENGER_ERROR_TCP_SERVER,
MESSENGER_ERROR_OTHER,
} Messenger_Error;
/** @brief Run this at startup.
*
* @return allocated instance of Messenger on success.
* @retval 0 if there are problems.
*
* if error is not NULL it will be set to one of the values in the enum above.
*/
non_null()
Messenger *new_messenger(Mono_Time *mono_time, const Random *rng, const Network *ns, Messenger_Options *options, Messenger_Error *error);
/** @brief Run this before closing shop.
*
* Free all datastructures.
*/
nullable(1)
void kill_messenger(Messenger *m);
/** @brief The main loop that needs to be run at least 20 times per second. */
non_null(1) nullable(2)
void do_messenger(Messenger *m, void *userdata);
/**
* @brief Return the time in milliseconds before `do_messenger()` should be called again
* for optimal performance.
*
* @return time (in ms) before the next `do_messenger()` needs to be run on success.
*/
non_null()
uint32_t messenger_run_interval(const Messenger *m);
/* SAVING AND LOADING FUNCTIONS: */
/** @brief Registers a state plugin for saving, loading, and getting the size of a section of the save.
*
* @retval true on success
* @retval false on error
*/
non_null()
bool m_register_state_plugin(Messenger *m, State_Type type,
m_state_size_cb *size_callback,
m_state_load_cb *load_callback,
m_state_save_cb *save_callback);
/** return size of the messenger data (for saving). */
non_null()
uint32_t messenger_size(const Messenger *m);
/** Save the messenger in data (must be allocated memory of size at least `Messenger_size()`) */
non_null()
uint8_t *messenger_save(const Messenger *m, uint8_t *data);
/** @brief Load a state section.
*
* @param data Data to load.
* @param length Length of data.
* @param type Type of section (`STATE_TYPE_*`).
* @param status Result of loading section is stored here if the section is handled.
* @return true iff section handled.
*/
non_null()
bool messenger_load_state_section(Messenger *m, const uint8_t *data, uint32_t length, uint16_t type,
State_Load_Status *status);
/** @brief Return the number of friends in the instance m.
*
* You should use this to determine how much memory to allocate
* for copy_friendlist.
*/
non_null()
uint32_t count_friendlist(const Messenger *m);
/** @brief Copy a list of valid friend IDs into the array out_list.
* If out_list is NULL, returns 0.
* Otherwise, returns the number of elements copied.
* If the array was too small, the contents
* of out_list will be truncated to list_size.
*/
non_null()
uint32_t copy_friendlist(const Messenger *m, uint32_t *out_list, uint32_t list_size);
non_null()
bool m_is_receiving_file(Messenger *m);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,154 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2014 Tox project.
*/
/**
* Implementation of the TCP relay client part of Tox.
*/
#ifndef C_TOXCORE_TOXCORE_TCP_CLIENT_H
#define C_TOXCORE_TOXCORE_TCP_CLIENT_H
#include "crypto_core.h"
#include "forwarding.h"
#include "mono_time.h"
#include "network.h"
#define TCP_CONNECTION_TIMEOUT 10
typedef enum TCP_Proxy_Type {
TCP_PROXY_NONE,
TCP_PROXY_HTTP,
TCP_PROXY_SOCKS5,
} TCP_Proxy_Type;
typedef struct TCP_Proxy_Info {
IP_Port ip_port;
uint8_t proxy_type; // a value from TCP_PROXY_TYPE
} TCP_Proxy_Info;
typedef enum TCP_Client_Status {
TCP_CLIENT_NO_STATUS,
TCP_CLIENT_PROXY_HTTP_CONNECTING,
TCP_CLIENT_PROXY_SOCKS5_CONNECTING,
TCP_CLIENT_PROXY_SOCKS5_UNCONFIRMED,
TCP_CLIENT_CONNECTING,
TCP_CLIENT_UNCONFIRMED,
TCP_CLIENT_CONFIRMED,
TCP_CLIENT_DISCONNECTED,
} TCP_Client_Status;
typedef struct TCP_Client_Connection TCP_Client_Connection;
non_null()
const uint8_t *tcp_con_public_key(const TCP_Client_Connection *con);
non_null()
IP_Port tcp_con_ip_port(const TCP_Client_Connection *con);
non_null()
TCP_Client_Status tcp_con_status(const TCP_Client_Connection *con);
non_null()
void *tcp_con_custom_object(const TCP_Client_Connection *con);
non_null()
uint32_t tcp_con_custom_uint(const TCP_Client_Connection *con);
non_null()
void tcp_con_set_custom_object(TCP_Client_Connection *con, void *object);
non_null()
void tcp_con_set_custom_uint(TCP_Client_Connection *con, uint32_t value);
/** Create new TCP connection to ip_port/public_key */
non_null(1, 2, 3, 4, 5, 6, 7, 8) nullable(9)
TCP_Client_Connection *new_TCP_connection(
const Logger *logger, const Mono_Time *mono_time, const Random *rng, const Network *ns, const IP_Port *ip_port,
const uint8_t *public_key, const uint8_t *self_public_key, const uint8_t *self_secret_key,
const TCP_Proxy_Info *proxy_info);
/** Run the TCP connection */
non_null(1, 2, 3) nullable(4)
void do_TCP_connection(const Logger *logger, const Mono_Time *mono_time,
TCP_Client_Connection *tcp_connection, void *userdata);
/** Kill the TCP connection */
nullable(1)
void kill_TCP_connection(TCP_Client_Connection *tcp_connection);
typedef int tcp_onion_response_cb(void *object, const uint8_t *data, uint16_t length, void *userdata);
/**
* @retval 1 on success.
* @retval 0 if could not send packet.
* @retval -1 on failure (connection must be killed).
*/
non_null()
int send_onion_request(const Logger *logger, TCP_Client_Connection *con, const uint8_t *data, uint16_t length);
non_null()
void onion_response_handler(TCP_Client_Connection *con, tcp_onion_response_cb *onion_callback, void *object);
non_null()
int send_forward_request_tcp(const Logger *logger, TCP_Client_Connection *con, const IP_Port *dest, const uint8_t *data,
uint16_t length);
non_null()
void forwarding_handler(TCP_Client_Connection *con, forwarded_response_cb *forwarded_response_callback, void *object);
typedef int tcp_routing_response_cb(void *object, uint8_t connection_id, const uint8_t *public_key);
typedef int tcp_routing_status_cb(void *object, uint32_t number, uint8_t connection_id, uint8_t status);
/**
* @retval 1 on success.
* @retval 0 if could not send packet.
* @retval -1 on failure (connection must be killed).
*/
non_null()
int send_routing_request(const Logger *logger, TCP_Client_Connection *con, const uint8_t *public_key);
non_null()
void routing_response_handler(TCP_Client_Connection *con, tcp_routing_response_cb *response_callback, void *object);
non_null()
void routing_status_handler(TCP_Client_Connection *con, tcp_routing_status_cb *status_callback, void *object);
/**
* @retval 1 on success.
* @retval 0 if could not send packet.
* @retval -1 on failure (connection must be killed).
*/
non_null()
int send_disconnect_request(const Logger *logger, TCP_Client_Connection *con, uint8_t con_id);
/** @brief Set the number that will be used as an argument in the callbacks related to con_id.
*
* When not set by this function, the number is -1.
*
* return 0 on success.
* return -1 on failure.
*/
non_null()
int set_tcp_connection_number(TCP_Client_Connection *con, uint8_t con_id, uint32_t number);
typedef int tcp_routing_data_cb(void *object, uint32_t number, uint8_t connection_id, const uint8_t *data,
uint16_t length, void *userdata);
/**
* @retval 1 on success.
* @retval 0 if could not send packet.
* @retval -1 on failure.
*/
non_null()
int send_data(const Logger *logger, TCP_Client_Connection *con, uint8_t con_id, const uint8_t *data, uint16_t length);
non_null()
void routing_data_handler(TCP_Client_Connection *con, tcp_routing_data_cb *data_callback, void *object);
typedef int tcp_oob_data_cb(void *object, const uint8_t *public_key, const uint8_t *data, uint16_t length,
void *userdata);
/**
* @retval 1 on success.
* @retval 0 if could not send packet.
* @retval -1 on failure.
*/
non_null()
int send_oob_packet(const Logger *logger, TCP_Client_Connection *con, const uint8_t *public_key, const uint8_t *data,
uint16_t length);
non_null()
void oob_data_handler(TCP_Client_Connection *con, tcp_oob_data_cb *oob_data_callback, void *object);
#endif

View File

@ -0,0 +1,305 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2014 Tox project.
*/
#include "TCP_common.h"
#include <stdlib.h>
#include <string.h>
#include "ccompat.h"
void wipe_priority_list(TCP_Priority_List *p)
{
while (p != nullptr) {
TCP_Priority_List *pp = p;
p = p->next;
free(pp->data);
free(pp);
}
}
/**
* @retval 0 if pending data was sent completely
* @retval -1 if it wasn't
*/
int send_pending_data_nonpriority(const Logger *logger, TCP_Connection *con)
{
if (con->last_packet_length == 0) {
return 0;
}
const uint16_t left = con->last_packet_length - con->last_packet_sent;
const int len = net_send(con->ns, logger, con->sock, con->last_packet + con->last_packet_sent, left, &con->ip_port);
if (len <= 0) {
return -1;
}
if (len == left) {
con->last_packet_length = 0;
con->last_packet_sent = 0;
return 0;
}
con->last_packet_sent += len;
return -1;
}
/**
* @retval 0 if pending data was sent completely
* @retval -1 if it wasn't
*/
int send_pending_data(const Logger *logger, TCP_Connection *con)
{
/* finish sending current non-priority packet */
if (send_pending_data_nonpriority(logger, con) == -1) {
return -1;
}
TCP_Priority_List *p = con->priority_queue_start;
while (p != nullptr) {
const uint16_t left = p->size - p->sent;
const int len = net_send(con->ns, logger, con->sock, p->data + p->sent, left, &con->ip_port);
if (len != left) {
if (len > 0) {
p->sent += len;
}
break;
}
TCP_Priority_List *pp = p;
p = p->next;
free(pp->data);
free(pp);
}
con->priority_queue_start = p;
if (p == nullptr) {
con->priority_queue_end = nullptr;
return 0;
}
return -1;
}
/**
* @retval false on failure (only if calloc fails)
* @retval true on success
*/
non_null()
static bool add_priority(TCP_Connection *con, const uint8_t *packet, uint16_t size, uint16_t sent)
{
TCP_Priority_List *p = con->priority_queue_end;
TCP_Priority_List *new_list = (TCP_Priority_List *)calloc(1, sizeof(TCP_Priority_List));
if (new_list == nullptr) {
return false;
}
new_list->next = nullptr;
new_list->size = size;
new_list->sent = sent;
new_list->data = (uint8_t *)malloc(size);
if (new_list->data == nullptr) {
free(new_list);
return false;
}
memcpy(new_list->data, packet, size);
if (p != nullptr) {
p->next = new_list;
} else {
con->priority_queue_start = new_list;
}
con->priority_queue_end = new_list;
return true;
}
/**
* @retval 1 on success.
* @retval 0 if could not send packet.
* @retval -1 on failure (connection must be killed).
*/
int write_packet_TCP_secure_connection(const Logger *logger, TCP_Connection *con, const uint8_t *data, uint16_t length,
bool priority)
{
if (length + CRYPTO_MAC_SIZE > MAX_PACKET_SIZE) {
return -1;
}
bool sendpriority = true;
if (send_pending_data(logger, con) == -1) {
if (priority) {
sendpriority = false;
} else {
return 0;
}
}
VLA(uint8_t, packet, sizeof(uint16_t) + length + CRYPTO_MAC_SIZE);
uint16_t c_length = net_htons(length + CRYPTO_MAC_SIZE);
memcpy(packet, &c_length, sizeof(uint16_t));
int len = encrypt_data_symmetric(con->shared_key, con->sent_nonce, data, length, packet + sizeof(uint16_t));
if ((unsigned int)len != (SIZEOF_VLA(packet) - sizeof(uint16_t))) {
return -1;
}
if (priority) {
len = sendpriority ? net_send(con->ns, logger, con->sock, packet, SIZEOF_VLA(packet), &con->ip_port) : 0;
if (len <= 0) {
len = 0;
}
increment_nonce(con->sent_nonce);
if ((unsigned int)len == SIZEOF_VLA(packet)) {
return 1;
}
return add_priority(con, packet, SIZEOF_VLA(packet), len) ? 1 : 0;
}
len = net_send(con->ns, logger, con->sock, packet, SIZEOF_VLA(packet), &con->ip_port);
if (len <= 0) {
return 0;
}
increment_nonce(con->sent_nonce);
if ((unsigned int)len == SIZEOF_VLA(packet)) {
return 1;
}
memcpy(con->last_packet, packet, SIZEOF_VLA(packet));
con->last_packet_length = SIZEOF_VLA(packet);
con->last_packet_sent = len;
return 1;
}
/** @brief Read length bytes from socket.
*
* return length on success
* return -1 on failure/no data in buffer.
*/
int read_TCP_packet(const Logger *logger, const Network *ns, Socket sock, uint8_t *data, uint16_t length, const IP_Port *ip_port)
{
const uint16_t count = net_socket_data_recv_buffer(ns, sock);
if (count < length) {
LOGGER_TRACE(logger, "recv buffer has %d bytes, but requested %d bytes", count, length);
return -1;
}
const int len = net_recv(ns, logger, sock, data, length, ip_port);
if (len != length) {
LOGGER_ERROR(logger, "FAIL recv packet");
return -1;
}
return len;
}
/** @brief Read the next two bytes in TCP stream then convert them to
* length (host byte order).
*
* return length on success
* return 0 if nothing has been read from socket.
* return -1 on failure.
*/
non_null()
static uint16_t read_TCP_length(const Logger *logger, const Network *ns, Socket sock, const IP_Port *ip_port)
{
const uint16_t count = net_socket_data_recv_buffer(ns, sock);
if (count >= sizeof(uint16_t)) {
uint8_t length_buf[sizeof(uint16_t)];
const int len = net_recv(ns, logger, sock, length_buf, sizeof(length_buf), ip_port);
if (len != sizeof(uint16_t)) {
LOGGER_ERROR(logger, "FAIL recv packet");
return 0;
}
uint16_t length;
net_unpack_u16(length_buf, &length);
if (length > MAX_PACKET_SIZE) {
LOGGER_ERROR(logger, "TCP packet too large: %d > %d", length, MAX_PACKET_SIZE);
return -1;
}
return length;
}
return 0;
}
/**
* @return length of received packet on success.
* @retval 0 if could not read any packet.
* @retval -1 on failure (connection must be killed).
*/
int read_packet_TCP_secure_connection(
const Logger *logger, const Network *ns, Socket sock, uint16_t *next_packet_length,
const uint8_t *shared_key, uint8_t *recv_nonce, uint8_t *data,
uint16_t max_len, const IP_Port *ip_port)
{
if (*next_packet_length == 0) {
const uint16_t len = read_TCP_length(logger, ns, sock, ip_port);
if (len == (uint16_t) -1) {
return -1;
}
if (len == 0) {
return 0;
}
*next_packet_length = len;
}
if (max_len + CRYPTO_MAC_SIZE < *next_packet_length) {
LOGGER_DEBUG(logger, "packet too large");
return -1;
}
VLA(uint8_t, data_encrypted, (int) *next_packet_length);
const int len_packet = read_TCP_packet(logger, ns, sock, data_encrypted, *next_packet_length, ip_port);
if (len_packet == -1) {
return 0;
}
if (len_packet != *next_packet_length) {
LOGGER_WARNING(logger, "invalid packet length: %d, expected %d", len_packet, *next_packet_length);
return 0;
}
*next_packet_length = 0;
const int len = decrypt_data_symmetric(shared_key, recv_nonce, data_encrypted, len_packet, data);
if (len + CRYPTO_MAC_SIZE != len_packet) {
LOGGER_ERROR(logger, "decrypted length %d does not match expected length %d", len + CRYPTO_MAC_SIZE, len_packet);
return -1;
}
increment_nonce(recv_nonce);
return len;
}

View File

@ -0,0 +1,124 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2014 Tox project.
*/
#ifndef C_TOXCORE_TOXCORE_TCP_COMMON_H
#define C_TOXCORE_TOXCORE_TCP_COMMON_H
#include "crypto_core.h"
#include "network.h"
typedef struct TCP_Priority_List TCP_Priority_List;
struct TCP_Priority_List {
TCP_Priority_List *next;
uint16_t size;
uint16_t sent;
uint8_t *data;
};
nullable(1)
void wipe_priority_list(TCP_Priority_List *p);
#define NUM_RESERVED_PORTS 16
#define NUM_CLIENT_CONNECTIONS (256 - NUM_RESERVED_PORTS)
#ifdef USE_TEST_NETWORK
#define TCP_PACKET_FORWARD_REQUEST 11
#define TCP_PACKET_FORWARDING 10
#define TCP_PACKET_ROUTING_REQUEST 9
#define TCP_PACKET_ROUTING_RESPONSE 8
#define TCP_PACKET_CONNECTION_NOTIFICATION 7
#define TCP_PACKET_DISCONNECT_NOTIFICATION 6
#define TCP_PACKET_PING 5
#define TCP_PACKET_PONG 4
#define TCP_PACKET_OOB_SEND 3
#define TCP_PACKET_OOB_RECV 2
#define TCP_PACKET_ONION_REQUEST 1
#define TCP_PACKET_ONION_RESPONSE 0
#else
#define TCP_PACKET_ROUTING_REQUEST 0
#define TCP_PACKET_ROUTING_RESPONSE 1
#define TCP_PACKET_CONNECTION_NOTIFICATION 2
#define TCP_PACKET_DISCONNECT_NOTIFICATION 3
#define TCP_PACKET_PING 4
#define TCP_PACKET_PONG 5
#define TCP_PACKET_OOB_SEND 6
#define TCP_PACKET_OOB_RECV 7
#define TCP_PACKET_ONION_REQUEST 8
#define TCP_PACKET_ONION_RESPONSE 9
#define TCP_PACKET_FORWARD_REQUEST 10
#define TCP_PACKET_FORWARDING 11
#endif // test network
#define TCP_HANDSHAKE_PLAIN_SIZE (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE)
#define TCP_SERVER_HANDSHAKE_SIZE (CRYPTO_NONCE_SIZE + TCP_HANDSHAKE_PLAIN_SIZE + CRYPTO_MAC_SIZE)
#define TCP_CLIENT_HANDSHAKE_SIZE (CRYPTO_PUBLIC_KEY_SIZE + TCP_SERVER_HANDSHAKE_SIZE)
#define TCP_MAX_OOB_DATA_LENGTH 1024
/** frequency to ping connected nodes and timeout in seconds */
#define TCP_PING_FREQUENCY 30
#define TCP_PING_TIMEOUT 10
#define MAX_PACKET_SIZE 2048
typedef struct TCP_Connection {
const Random *rng;
const Network *ns;
Socket sock;
IP_Port ip_port; // for debugging.
uint8_t sent_nonce[CRYPTO_NONCE_SIZE]; /* Nonce of sent packets. */
uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE];
uint8_t last_packet[2 + MAX_PACKET_SIZE];
uint16_t last_packet_length;
uint16_t last_packet_sent;
TCP_Priority_List *priority_queue_start;
TCP_Priority_List *priority_queue_end;
} TCP_Connection;
/**
* @retval 0 if pending data was sent completely
* @retval -1 if it wasn't
*/
non_null()
int send_pending_data_nonpriority(const Logger *logger, TCP_Connection *con);
/**
* @retval 0 if pending data was sent completely
* @retval -1 if it wasn't
*/
non_null()
int send_pending_data(const Logger *logger, TCP_Connection *con);
/**
* @retval 1 on success.
* @retval 0 if could not send packet.
* @retval -1 on failure (connection must be killed).
*/
non_null()
int write_packet_TCP_secure_connection(
const Logger *logger, TCP_Connection *con, const uint8_t *data, uint16_t length,
bool priority);
/** @brief Read length bytes from socket.
*
* return length on success
* return -1 on failure/no data in buffer.
*/
non_null()
int read_TCP_packet(
const Logger *logger, const Network *ns, Socket sock, uint8_t *data, uint16_t length, const IP_Port *ip_port);
/**
* @return length of received packet on success.
* @retval 0 if could not read any packet.
* @retval -1 on failure (connection must be killed).
*/
non_null()
int read_packet_TCP_secure_connection(
const Logger *logger, const Network *ns, Socket sock, uint16_t *next_packet_length,
const uint8_t *shared_key, uint8_t *recv_nonce, uint8_t *data,
uint16_t max_len, const IP_Port *ip_port);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,314 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2015 Tox project.
*/
/**
* Handles TCP relay connections between two Tox clients.
*/
#ifndef C_TOXCORE_TOXCORE_TCP_CONNECTION_H
#define C_TOXCORE_TOXCORE_TCP_CONNECTION_H
#include <stdbool.h>
#include "DHT.h" // for Node_format
#include "TCP_client.h"
#include "TCP_common.h"
#define TCP_CONN_NONE 0
#define TCP_CONN_VALID 1
/** NOTE: only used by TCP_con */
#define TCP_CONN_CONNECTED 2
/** Connection is not connected but can be quickly reconnected in case it is needed. */
#define TCP_CONN_SLEEPING 3
#define TCP_CONNECTIONS_STATUS_NONE 0
#define TCP_CONNECTIONS_STATUS_REGISTERED 1
#define TCP_CONNECTIONS_STATUS_ONLINE 2
#define MAX_FRIEND_TCP_CONNECTIONS 6
/** Time until connection to friend gets killed (if it doesn't get locked within that time) */
#define TCP_CONNECTION_ANNOUNCE_TIMEOUT TCP_CONNECTION_TIMEOUT
/** @brief The amount of recommended connections for each friend
* NOTE: Must be at most (MAX_FRIEND_TCP_CONNECTIONS / 2)
*/
#define RECOMMENDED_FRIEND_TCP_CONNECTIONS (MAX_FRIEND_TCP_CONNECTIONS / 2)
/** Number of TCP connections used for onion purposes. */
#define NUM_ONION_TCP_CONNECTIONS RECOMMENDED_FRIEND_TCP_CONNECTIONS
typedef struct TCP_Conn_to {
uint32_t tcp_connection;
uint8_t status;
uint8_t connection_id;
} TCP_Conn_to;
typedef struct TCP_Connection_to {
uint8_t status;
uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; /* The dht public key of the peer */
TCP_Conn_to connections[MAX_FRIEND_TCP_CONNECTIONS];
int id; /* id used in callbacks. */
} TCP_Connection_to;
typedef struct TCP_con {
uint8_t status;
TCP_Client_Connection *connection;
uint64_t connected_time;
uint32_t lock_count;
uint32_t sleep_count;
bool onion;
/* Only used when connection is sleeping. */
IP_Port ip_port;
uint8_t relay_pk[CRYPTO_PUBLIC_KEY_SIZE];
bool unsleep; /* set to 1 to unsleep connection. */
} TCP_con;
typedef struct TCP_Connections TCP_Connections;
non_null()
const uint8_t *tcp_connections_public_key(const TCP_Connections *tcp_c);
non_null()
uint32_t tcp_connections_count(const TCP_Connections *tcp_c);
/** @brief Returns the number of connected TCP relays. */
non_null()
uint32_t tcp_connected_relays_count(const TCP_Connections *tcp_c);
/** @brief Returns true if we know of a valid TCP relay with the passed public key. */
non_null()
bool tcp_relay_is_valid(const TCP_Connections *tcp_c, const uint8_t *relay_pk);
/** @brief Send a packet to the TCP connection.
*
* return -1 on failure.
* return 0 on success.
*/
non_null()
int send_packet_tcp_connection(const TCP_Connections *tcp_c, int connections_number, const uint8_t *packet,
uint16_t length);
/** @brief Return a TCP connection number for use in send_tcp_onion_request.
*
* TODO(irungentoo): This number is just the index of an array that the elements
* can change without warning.
*
* return TCP connection number on success.
* return -1 on failure.
*/
non_null()
int get_random_tcp_onion_conn_number(const TCP_Connections *tcp_c);
/** @brief Put IP_Port of a random onion TCP connection in ip_port.
*
* return true on success.
* return false on failure.
*/
non_null()
bool tcp_get_random_conn_ip_port(const TCP_Connections *tcp_c, IP_Port *ip_port);
/** @brief Send an onion packet via the TCP relay corresponding to tcp_connections_number.
*
* return 0 on success.
* return -1 on failure.
*/
non_null()
int tcp_send_onion_request(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *data,
uint16_t length);
/** @brief Set if we want TCP_connection to allocate some connection for onion use.
*
* If status is 1, allocate some connections. if status is 0, don't.
*
* return 0 on success.
* return -1 on failure.
*/
non_null()
int set_tcp_onion_status(TCP_Connections *tcp_c, bool status);
/**
* Send a forward request to the TCP relay with IP_Port tcp_forwarder,
* requesting to forward data via a chain of dht nodes starting with dht_node.
* A chain_length of 0 means that dht_node is the final destination of data.
*
* return 0 on success.
* return -1 on failure.
*/
non_null()
int tcp_send_forward_request(const Logger *logger, TCP_Connections *tcp_c, const IP_Port *tcp_forwarder,
const IP_Port *dht_node,
const uint8_t *chain_keys, uint16_t chain_length,
const uint8_t *data, uint16_t data_length);
/** @brief Send an oob packet via the TCP relay corresponding to tcp_connections_number.
*
* return 0 on success.
* return -1 on failure.
*/
non_null()
int tcp_send_oob_packet(const TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *public_key,
const uint8_t *packet, uint16_t length);
typedef int tcp_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata);
non_null()
int tcp_send_oob_packet_using_relay(const TCP_Connections *tcp_c, const uint8_t *relay_pk, const uint8_t *public_key,
const uint8_t *packet, uint16_t length);
/** @brief Set the callback for TCP data packets. */
non_null()
void set_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_data_cb *tcp_data_callback, void *object);
typedef int tcp_onion_cb(void *object, const uint8_t *data, uint16_t length, void *userdata);
/** @brief Set the callback for TCP onion packets. */
non_null(1) nullable(2, 3)
void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_onion_cb *tcp_onion_callback, void *object);
/** @brief Set the callback for TCP forwarding packets. */
non_null(1) nullable(2, 3)
void set_forwarding_packet_tcp_connection_callback(TCP_Connections *tcp_c,
forwarded_response_cb *tcp_forwarded_response_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);
/** @brief Set the callback for TCP oob data packets. */
non_null()
void set_oob_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_oob_cb *tcp_oob_callback, void *object);
/** @brief Encode tcp_connections_number as a custom ip_port.
*
* return ip_port.
*/
IP_Port tcp_connections_number_to_ip_port(unsigned int tcp_connections_number);
/** @brief Decode ip_port created by tcp_connections_number_to_ip_port to tcp_connections_number.
*
* return true on success.
* return false if ip_port is invalid.
*/
non_null()
bool ip_port_to_tcp_connections_number(const IP_Port *ip_port, unsigned int *tcp_connections_number);
/** @brief Create a new TCP connection to public_key.
*
* public_key must be the counterpart to the secret key that the other peer used with `new_tcp_connections()`.
*
* id is the id in the callbacks for that connection.
*
* return connections_number on success.
* return -1 on failure.
*/
non_null()
int new_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key, int id);
/**
* @retval 0 on success.
* @retval -1 on failure.
*/
non_null()
int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number);
/** @brief Set connection status.
*
* status of 1 means we are using the connection.
* status of 0 means we are not using it.
*
* Unused tcp connections will be disconnected from but kept in case they are needed.
*
* return 0 on success.
* return -1 on failure.
*/
non_null()
int set_tcp_connection_to_status(const TCP_Connections *tcp_c, int connections_number, bool status);
/**
* @return number of online tcp relays tied to the connection on success.
* @retval 0 on failure.
*/
non_null()
uint32_t tcp_connection_to_online_tcp_relays(const TCP_Connections *tcp_c, int connections_number);
/** @brief Add a TCP relay tied to a connection.
*
* NOTE: This can only be used during the tcp_oob_callback.
*
* return 0 on success.
* return -1 on failure.
*/
non_null()
int add_tcp_number_relay_connection(const TCP_Connections *tcp_c, int connections_number,
unsigned int tcp_connections_number);
/** @brief Add a TCP relay tied to a connection.
*
* This should be called with the same relay by two peers who want to create a TCP connection with each other.
*
* return 0 on success.
* return -1 on failure.
*/
non_null()
int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, const IP_Port *ip_port,
const uint8_t *relay_pk);
/** @brief Add a TCP relay to the TCP_Connections instance.
*
* return 0 on success.
* return -1 on failure.
*/
non_null()
int add_tcp_relay_global(TCP_Connections *tcp_c, const IP_Port *ip_port, const uint8_t *relay_pk);
/** @brief Copy a maximum of max_num TCP relays we are connected to to tcp_relays.
*
* NOTE that the family of the copied ip ports will be set to TCP_INET or TCP_INET6.
*
* return number of relays copied to tcp_relays on success.
* return 0 on failure.
*/
non_null()
uint32_t tcp_copy_connected_relays(const TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num);
/** @brief Copy a maximum of `max_num` TCP relays we are connected to starting at idx.
*
* @param idx is the index in the TCP relay array for `tcp_c` designated.
* If idx is greater than the array length a modulo operation is performed.
*
* Returns the number of relays successfully copied.
*/
non_null()
uint32_t tcp_copy_connected_relays_index(const TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num,
uint32_t idx);
/** @brief Returns a new TCP_Connections object associated with the secret_key.
*
* In order for others to connect to this instance `new_tcp_connection_to()` must be called with the
* public_key associated with secret_key.
*
* Returns NULL on failure.
*/
non_null()
TCP_Connections *new_tcp_connections(
const Logger *logger, const Random *rng, const Network *ns, Mono_Time *mono_time,
const uint8_t *secret_key, const TCP_Proxy_Info *proxy_info);
non_null()
int kill_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number);
non_null(1, 2) nullable(3)
void do_tcp_connections(const Logger *logger, TCP_Connections *tcp_c, void *userdata);
nullable(1)
void kill_tcp_connections(TCP_Connections *tcp_c);
#endif

View File

@ -0,0 +1,10 @@
#include "TCP_connection.h"
#include <gtest/gtest.h>
namespace {
// TODO(Jfreegman) make this useful or remove it after NGC is merged
TEST(TCP_connection, NullTest) { (void)tcp_send_oob_packet_using_relay; }
} // namespace

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2014 Tox project.
*/
/**
* Implementation of the TCP relay server part of Tox.
*/
#ifndef C_TOXCORE_TOXCORE_TCP_SERVER_H
#define C_TOXCORE_TOXCORE_TCP_SERVER_H
#include "crypto_core.h"
#include "forwarding.h"
#include "onion.h"
#define MAX_INCOMING_CONNECTIONS 256
#define TCP_MAX_BACKLOG MAX_INCOMING_CONNECTIONS
#define ARRAY_ENTRY_SIZE 6
typedef enum TCP_Status {
TCP_STATUS_NO_STATUS,
TCP_STATUS_CONNECTED,
TCP_STATUS_UNCONFIRMED,
TCP_STATUS_CONFIRMED,
} TCP_Status;
typedef struct TCP_Server TCP_Server;
non_null()
const uint8_t *tcp_server_public_key(const TCP_Server *tcp_server);
non_null()
size_t tcp_server_listen_count(const TCP_Server *tcp_server);
/** Create new TCP server instance. */
non_null(1, 2, 3, 6, 7) nullable(8, 9)
TCP_Server *new_TCP_server(const Logger *logger, const Random *rng, const Network *ns,
bool ipv6_enabled, uint16_t num_sockets, const uint16_t *ports,
const uint8_t *secret_key, Onion *onion, Forwarding *forwarding);
/** Run the TCP_server */
non_null()
void do_TCP_server(TCP_Server *tcp_server, const Mono_Time *mono_time);
/** Kill the TCP server */
nullable(1)
void kill_TCP_server(TCP_Server *tcp_server);
#endif

View File

@ -0,0 +1,700 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2020-2021 The TokTok team.
*/
/**
* "Server side" of the DHT announcements protocol.
*/
#include "announce.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "LAN_discovery.h"
#include "ccompat.h"
#include "shared_key_cache.h"
#include "timed_auth.h"
#include "util.h"
// Settings for the shared key cache
#define MAX_KEYS_PER_SLOT 4
#define KEYS_TIMEOUT 600
uint8_t announce_response_of_request_type(uint8_t request_type)
{
switch (request_type) {
case NET_PACKET_DATA_SEARCH_REQUEST:
return NET_PACKET_DATA_SEARCH_RESPONSE;
case NET_PACKET_DATA_RETRIEVE_REQUEST:
return NET_PACKET_DATA_RETRIEVE_RESPONSE;
case NET_PACKET_STORE_ANNOUNCE_REQUEST:
return NET_PACKET_STORE_ANNOUNCE_RESPONSE;
default: {
assert(false);
return NET_PACKET_MAX;
}
}
}
typedef struct Announce_Entry {
uint64_t store_until;
uint8_t data_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t *data;
uint32_t length;
} Announce_Entry;
struct Announcements {
const Logger *log;
const Random *rng;
Forwarding *forwarding;
const Mono_Time *mono_time;
DHT *dht;
Networking_Core *net;
const uint8_t *public_key;
const uint8_t *secret_key;
Shared_Key_Cache *shared_keys;
uint8_t hmac_key[CRYPTO_HMAC_KEY_SIZE];
int32_t synch_offset;
uint64_t start_time;
Announce_Entry entries[ANNOUNCE_BUCKETS * ANNOUNCE_BUCKET_SIZE];
};
void announce_set_synch_offset(Announcements *announce, int32_t synch_offset)
{
announce->synch_offset = synch_offset;
}
/**
* An entry is considered to be "deleted" for the purposes of the protocol
* once it has timed out.
*/
non_null()
static bool entry_is_empty(const Announcements *announce, const Announce_Entry *entry)
{
return mono_time_get(announce->mono_time) >= entry->store_until;
}
non_null()
static void delete_entry(Announce_Entry *entry)
{
entry->store_until = 0;
}
/** Return bits (at most 8) from pk starting at index as uint8_t */
non_null()
static uint8_t truncate_pk_at_index(const uint8_t *pk, uint16_t index, uint16_t bits)
{
assert(bits < 8);
const uint8_t i = index / 8;
const uint8_t j = index % 8;
return ((uint8_t)((i < CRYPTO_PUBLIC_KEY_SIZE ? pk[i] : 0) << j) >> (8 - bits)) |
((i + 1 < CRYPTO_PUBLIC_KEY_SIZE ? pk[i + 1] : 0) >> (16 - bits - j));
}
uint16_t announce_get_bucketnum(const uint8_t *base, const uint8_t *pk)
{
const uint16_t index = bit_by_bit_cmp(base, pk);
return truncate_pk_at_index(base, index + 1, ANNOUNCE_BUCKET_PREFIX_LENGTH) ^
truncate_pk_at_index(pk, index + 1, ANNOUNCE_BUCKET_PREFIX_LENGTH);
}
non_null()
static Announce_Entry *bucket_of_key(Announcements *announce, const uint8_t *pk)
{
return &announce->entries[announce_get_bucketnum(announce->public_key, pk) * ANNOUNCE_BUCKET_SIZE];
}
non_null()
static Announce_Entry *get_stored(Announcements *announce, const uint8_t *data_public_key)
{
Announce_Entry *const bucket = bucket_of_key(announce, data_public_key);
for (uint32_t i = 0; i < ANNOUNCE_BUCKET_SIZE; ++i) {
if (pk_equal(bucket[i].data_public_key, data_public_key)) {
if (entry_is_empty(announce, &bucket[i])) {
break;
}
return &bucket[i];
}
}
return nullptr;
}
non_null()
static const Announce_Entry *bucket_of_key_const(const Announcements *announce, const uint8_t *pk)
{
return &announce->entries[announce_get_bucketnum(announce->public_key, pk) * ANNOUNCE_BUCKET_SIZE];
}
non_null()
static const Announce_Entry *get_stored_const(const Announcements *announce, const uint8_t *data_public_key)
{
const Announce_Entry *const bucket = bucket_of_key_const(announce, data_public_key);
for (uint32_t i = 0; i < ANNOUNCE_BUCKET_SIZE; ++i) {
if (pk_equal(bucket[i].data_public_key, data_public_key)) {
if (entry_is_empty(announce, &bucket[i])) {
break;
}
return &bucket[i];
}
}
return nullptr;
}
bool announce_on_stored(const Announcements *announce, const uint8_t *data_public_key,
announce_on_retrieve_cb *on_retrieve_callback, void *object)
{
const Announce_Entry *const entry = get_stored_const(announce, data_public_key);
if (entry == nullptr || entry->data == nullptr) {
return false;
}
if (on_retrieve_callback != nullptr) {
on_retrieve_callback(object, entry->data, entry->length);
}
return true;
}
/**
* Return existing entry for this key if it exists, else an empty
* slot in the key's bucket if one exists, else an entry in the key's bucket
* of greatest 2-adic distance greater than that of the key bucket if one
* exists, else nullptr.
*/
non_null()
static Announce_Entry *find_entry_slot(Announcements *announce, const uint8_t *data_public_key)
{
Announce_Entry *const bucket = bucket_of_key(announce, data_public_key);
Announce_Entry *slot = nullptr;
uint16_t min_index = bit_by_bit_cmp(announce->public_key, data_public_key);
for (uint32_t i = 0; i < ANNOUNCE_BUCKET_SIZE; ++i) {
if (pk_equal(bucket[i].data_public_key, data_public_key)) {
return &bucket[i];
}
if (entry_is_empty(announce, &bucket[i])) {
slot = &bucket[i];
min_index = 0;
continue;
}
const uint16_t index = bit_by_bit_cmp(announce->public_key, bucket[i].data_public_key);
if (index < min_index) {
slot = &bucket[i];
min_index = index;
}
}
return slot;
}
non_null()
static bool would_accept_store_request(Announcements *announce, const uint8_t *data_public_key)
{
return find_entry_slot(announce, data_public_key) != nullptr;
}
bool announce_store_data(Announcements *announce, const uint8_t *data_public_key,
const uint8_t *data, uint32_t length, uint32_t timeout)
{
if (length > MAX_ANNOUNCEMENT_SIZE) {
return false;
}
Announce_Entry *entry = find_entry_slot(announce, data_public_key);
if (entry == nullptr) {
return false;
}
if (length > 0) {
assert(data != nullptr);
if (entry->data != nullptr) {
free(entry->data);
}
entry->data = (uint8_t *)malloc(length);
if (entry->data == nullptr) {
return false;
}
memcpy(entry->data, data, length);
}
entry->length = length;
memcpy(entry->data_public_key, data_public_key, CRYPTO_PUBLIC_KEY_SIZE);
entry->store_until = mono_time_get(announce->mono_time) + timeout;
return true;
}
non_null()
static uint32_t calculate_timeout(const Announcements *announce, uint32_t requested_timeout)
{
const uint64_t uptime = mono_time_get(announce->mono_time) - announce->start_time;
const uint32_t max_announcement_timeout = max_u32(
(uint32_t)min_u64(
MAX_MAX_ANNOUNCEMENT_TIMEOUT,
uptime / MAX_ANNOUNCEMENT_TIMEOUT_UPTIME_RATIO),
MIN_MAX_ANNOUNCEMENT_TIMEOUT);
return min_u32(max_announcement_timeout, requested_timeout);
}
#define DATA_SEARCH_TO_AUTH_MAX_SIZE (CRYPTO_PUBLIC_KEY_SIZE * 2 + MAX_PACKED_IPPORT_SIZE + MAX_SENDBACK_SIZE)
non_null(1, 2, 3, 4, 7) nullable(5)
static int create_data_search_to_auth(const Logger *logger, const uint8_t *data_public_key,
const uint8_t *requester_key,
const IP_Port *source, const uint8_t *sendback, uint16_t sendback_length,
uint8_t *dest, uint16_t max_length)
{
if (max_length < DATA_SEARCH_TO_AUTH_MAX_SIZE
|| sendback_length > MAX_SENDBACK_SIZE) {
return -1;
}
memcpy(dest, data_public_key, CRYPTO_PUBLIC_KEY_SIZE);
memcpy(dest + CRYPTO_PUBLIC_KEY_SIZE, requester_key, CRYPTO_PUBLIC_KEY_SIZE);
const int ipport_length = pack_ip_port(logger, dest + CRYPTO_PUBLIC_KEY_SIZE * 2, MAX_PACKED_IPPORT_SIZE, source);
if (ipport_length == -1) {
return -1;
}
if (sendback_length > 0) {
assert(sendback != nullptr);
memcpy(dest + CRYPTO_PUBLIC_KEY_SIZE * 2 + ipport_length, sendback, sendback_length);
}
return CRYPTO_PUBLIC_KEY_SIZE * 2 + ipport_length + sendback_length;
}
#define DATA_SEARCH_TIMEOUT 60
non_null()
static int create_reply_plain_data_search_request(Announcements *announce,
const IP_Port *source,
const uint8_t *data, uint16_t length,
uint8_t *reply, uint16_t reply_max_length,
uint8_t *to_auth, uint16_t to_auth_length)
{
if (length != CRYPTO_PUBLIC_KEY_SIZE &&
length != CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA256_SIZE) {
return -1;
}
const uint8_t *const data_public_key = data;
const uint8_t *previous_hash = nullptr;
if (length == CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA256_SIZE) {
previous_hash = data + CRYPTO_PUBLIC_KEY_SIZE;
}
const int nodes_max_length = (int)reply_max_length -
(CRYPTO_PUBLIC_KEY_SIZE + 1 + CRYPTO_SHA256_SIZE + TIMED_AUTH_SIZE + 1 + 1);
if (nodes_max_length < 0) {
return -1;
}
uint8_t *p = reply;
memcpy(p, data_public_key, CRYPTO_PUBLIC_KEY_SIZE);
p += CRYPTO_PUBLIC_KEY_SIZE;
const Announce_Entry *const stored = get_stored_const(announce, data_public_key);
if (stored == nullptr) {
*p = 0;
++p;
} else {
*p = 1;
++p;
crypto_sha256(p, stored->data, stored->length);
p += CRYPTO_SHA256_SIZE;
}
generate_timed_auth(announce->mono_time, DATA_SEARCH_TIMEOUT, announce->hmac_key,
to_auth, to_auth_length, p);
p += TIMED_AUTH_SIZE;
*p = would_accept_store_request(announce, data_public_key);
++p;
Node_format nodes_list[MAX_SENT_NODES];
const int num_nodes = get_close_nodes(announce->dht, data_public_key, nodes_list,
net_family_unspec(), ip_is_lan(&source->ip), true);
if (num_nodes < 0 || num_nodes > MAX_SENT_NODES) {
return -1;
}
*p = num_nodes;
++p;
p += pack_nodes(announce->log, p, nodes_max_length, nodes_list, num_nodes);
const uint32_t reply_len = p - reply;
if (previous_hash != nullptr) {
uint8_t hash[CRYPTO_SHA256_SIZE];
crypto_sha256(hash, reply, reply_len);
if (crypto_sha256_eq(hash, previous_hash)) {
return CRYPTO_PUBLIC_KEY_SIZE;
}
}
return reply_len;
}
non_null()
static int create_reply_plain_data_retrieve_request(Announcements *announce,
const IP_Port *source,
const uint8_t *data, uint16_t length,
uint8_t *reply, uint16_t reply_max_length,
uint8_t *to_auth, uint16_t to_auth_length)
{
if (length != CRYPTO_PUBLIC_KEY_SIZE + 1 + TIMED_AUTH_SIZE) {
return -1;
}
if (data[CRYPTO_PUBLIC_KEY_SIZE] != 0) {
return -1;
}
const uint8_t *const data_public_key = data;
const uint8_t *const auth = data + CRYPTO_PUBLIC_KEY_SIZE + 1;
if (!check_timed_auth(announce->mono_time, DATA_SEARCH_TIMEOUT, announce->hmac_key,
to_auth, to_auth_length, auth)) {
return -1;
}
const Announce_Entry *const entry = get_stored_const(announce, data_public_key);
if (entry == nullptr) {
return -1;
}
const uint16_t reply_len = CRYPTO_PUBLIC_KEY_SIZE + 1 + entry->length;
if (reply_max_length < reply_len) {
return -1;
}
memcpy(reply, data_public_key, CRYPTO_PUBLIC_KEY_SIZE);
reply[CRYPTO_PUBLIC_KEY_SIZE] = 1;
memcpy(reply + CRYPTO_PUBLIC_KEY_SIZE + 1, entry->data, entry->length);
return reply_len;
}
non_null()
static int create_reply_plain_store_announce_request(Announcements *announce,
const IP_Port *source,
const uint8_t *data, uint16_t length,
uint8_t *reply, uint16_t reply_max_length,
uint8_t *to_auth, uint16_t to_auth_length)
{
const int plain_len = (int)length - (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE);
const int announcement_len = (int)plain_len - (TIMED_AUTH_SIZE + sizeof(uint32_t) + 1);
const uint8_t *const data_public_key = data;
if (announcement_len < 0) {
return -1;
}
VLA(uint8_t, plain, plain_len);
const uint8_t* shared_key = shared_key_cache_lookup(announce->shared_keys, data_public_key);
if (shared_key == nullptr) {
/* Error looking up/deriving the shared key */
return -1;
}
if (decrypt_data_symmetric(shared_key,
data + CRYPTO_PUBLIC_KEY_SIZE,
data + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE,
plain_len + CRYPTO_MAC_SIZE,
plain) != plain_len) {
return -1;
}
const uint8_t *const auth = plain;
uint32_t requested_timeout;
net_unpack_u32(plain + TIMED_AUTH_SIZE, &requested_timeout);
const uint32_t timeout = calculate_timeout(announce, requested_timeout);
const uint8_t announcement_type = plain[TIMED_AUTH_SIZE + sizeof(uint32_t)];
const uint8_t *announcement = plain + TIMED_AUTH_SIZE + sizeof(uint32_t) + 1;
if (!check_timed_auth(announce->mono_time, DATA_SEARCH_TIMEOUT, announce->hmac_key,
to_auth, to_auth_length, auth)) {
return -1;
}
if (announcement_type > 1) {
return -1;
}
if (announcement_type == 1) {
if (announcement_len != CRYPTO_SHA256_SIZE) {
return -1;
}
Announce_Entry *stored = get_stored(announce, data_public_key);
if (stored == nullptr) {
return -1;
}
uint8_t stored_hash[CRYPTO_SHA256_SIZE];
crypto_sha256(stored_hash, stored->data, stored->length);
if (!crypto_sha256_eq(announcement, stored_hash)) {
delete_entry(stored);
return -1;
} else {
stored->store_until = mono_time_get(announce->mono_time) + timeout;
}
} else {
if (!announce_store_data(announce, data_public_key, announcement, announcement_len, timeout)) {
return -1;
}
}
const uint16_t reply_len = CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint32_t) + sizeof(uint64_t);
if (reply_max_length < reply_len) {
return -1;
}
memcpy(reply, data_public_key, CRYPTO_PUBLIC_KEY_SIZE);
net_pack_u32(reply + CRYPTO_PUBLIC_KEY_SIZE, timeout);
net_pack_u64(reply + CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint32_t),
mono_time_get(announce->mono_time) + announce->synch_offset);
return reply_len;
}
non_null(1, 2, 3, 7, 9) nullable(5)
static int create_reply_plain(Announcements *announce,
const uint8_t *requester_key, const IP_Port *source, uint8_t type,
const uint8_t *sendback, uint16_t sendback_length,
const uint8_t *data, uint16_t length,
uint8_t *reply, uint16_t reply_max_length)
{
if (length < CRYPTO_PUBLIC_KEY_SIZE) {
return -1;
}
const uint8_t *const data_public_key = data;
uint8_t to_auth[DATA_SEARCH_TO_AUTH_MAX_SIZE];
const int to_auth_length = create_data_search_to_auth(announce->log, data_public_key, requester_key, source,
sendback, sendback_length, to_auth, DATA_SEARCH_TO_AUTH_MAX_SIZE);
if (to_auth_length == -1) {
return -1;
}
switch (type) {
case NET_PACKET_DATA_SEARCH_REQUEST:
return create_reply_plain_data_search_request(announce, source, data, length, reply, reply_max_length, to_auth,
(uint16_t)to_auth_length);
case NET_PACKET_DATA_RETRIEVE_REQUEST:
return create_reply_plain_data_retrieve_request(announce, source, data, length, reply, reply_max_length, to_auth,
(uint16_t)to_auth_length);
case NET_PACKET_STORE_ANNOUNCE_REQUEST:
return create_reply_plain_store_announce_request(announce, source, data, length, reply, reply_max_length, to_auth,
(uint16_t)to_auth_length);
default:
return -1;
}
}
non_null(1, 2, 5, 7) nullable(3)
static int create_reply(Announcements *announce, const IP_Port *source,
const uint8_t *sendback, uint16_t sendback_length,
const uint8_t *data, uint16_t length,
uint8_t *reply, uint16_t reply_max_length)
{
const int plain_len = (int)length - (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE);
if (plain_len < (int)sizeof(uint64_t)) {
return -1;
}
VLA(uint8_t, plain, plain_len);
const uint8_t *shared_key = dht_get_shared_key_recv(announce->dht, data + 1);
if (decrypt_data_symmetric(shared_key,
data + 1 + CRYPTO_PUBLIC_KEY_SIZE,
data + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE,
plain_len + CRYPTO_MAC_SIZE,
plain) != plain_len) {
return -1;
}
const int plain_reply_max_len = (int)reply_max_length -
(1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE);
if (plain_reply_max_len < sizeof(uint64_t)) {
return -1;
}
VLA(uint8_t, plain_reply, plain_reply_max_len);
const int plain_reply_noping_len = create_reply_plain(announce,
data + 1, source, data[0],
sendback, sendback_length,
plain, plain_len - sizeof(uint64_t),
plain_reply, plain_reply_max_len - sizeof(uint64_t));
if (plain_reply_noping_len == -1) {
return -1;
}
memcpy(plain_reply + plain_reply_noping_len,
plain + (plain_len - sizeof(uint64_t)), sizeof(uint64_t));
const uint16_t plain_reply_len = plain_reply_noping_len + sizeof(uint64_t);
const uint8_t response_type = announce_response_of_request_type(data[0]);
return dht_create_packet(announce->rng, announce->public_key, shared_key, response_type,
plain_reply, plain_reply_len, reply, reply_max_length);
}
non_null(1, 2, 3, 5) nullable(7)
static void forwarded_request_callback(void *object, const IP_Port *forwarder,
const uint8_t *sendback, uint16_t sendback_length,
const uint8_t *data, uint16_t length, void *userdata)
{
Announcements *announce = (Announcements *) object;
uint8_t reply[MAX_FORWARD_DATA_SIZE];
const int len = create_reply(announce, forwarder,
sendback, sendback_length,
data, length, reply, sizeof(reply));
if (len == -1) {
return;
}
forward_reply(announce->net, forwarder, sendback, sendback_length, reply, len);
}
non_null(1, 2, 3) nullable(5)
static int handle_dht_announce_request(void *object, const IP_Port *source,
const uint8_t *data, uint16_t length, void *userdata)
{
Announcements *announce = (Announcements *) object;
uint8_t reply[MAX_FORWARD_DATA_SIZE];
const int len = create_reply(announce, source,
nullptr, 0,
data, length, reply, sizeof(reply));
if (len == -1) {
return -1;
}
return sendpacket(announce->net, source, reply, len) == len ? 0 : -1;
}
Announcements *new_announcements(const Logger *log, const Random *rng, const Mono_Time *mono_time,
Forwarding *forwarding)
{
if (log == nullptr || mono_time == nullptr || forwarding == nullptr) {
return nullptr;
}
Announcements *announce = (Announcements *)calloc(1, sizeof(Announcements));
if (announce == nullptr) {
return nullptr;
}
announce->log = log;
announce->rng = rng;
announce->forwarding = forwarding;
announce->mono_time = mono_time;
announce->dht = forwarding_get_dht(forwarding);
announce->net = dht_get_net(announce->dht);
announce->public_key = dht_get_self_public_key(announce->dht);
announce->secret_key = dht_get_self_secret_key(announce->dht);
new_hmac_key(announce->rng, announce->hmac_key);
announce->shared_keys = shared_key_cache_new(mono_time, announce->secret_key, KEYS_TIMEOUT, MAX_KEYS_PER_SLOT);
if (announce->shared_keys == nullptr) {
free(announce);
return nullptr;
}
announce->start_time = mono_time_get(announce->mono_time);
set_callback_forwarded_request(forwarding, forwarded_request_callback, announce);
networking_registerhandler(announce->net, NET_PACKET_DATA_SEARCH_REQUEST, handle_dht_announce_request, announce);
networking_registerhandler(announce->net, NET_PACKET_DATA_RETRIEVE_REQUEST, handle_dht_announce_request, announce);
networking_registerhandler(announce->net, NET_PACKET_STORE_ANNOUNCE_REQUEST, handle_dht_announce_request, announce);
return announce;
}
void kill_announcements(Announcements *announce)
{
if (announce == nullptr) {
return;
}
set_callback_forwarded_request(announce->forwarding, nullptr, nullptr);
networking_registerhandler(announce->net, NET_PACKET_DATA_SEARCH_REQUEST, nullptr, nullptr);
networking_registerhandler(announce->net, NET_PACKET_DATA_RETRIEVE_REQUEST, nullptr, nullptr);
networking_registerhandler(announce->net, NET_PACKET_STORE_ANNOUNCE_REQUEST, nullptr, nullptr);
crypto_memzero(announce->hmac_key, CRYPTO_HMAC_KEY_SIZE);
shared_key_cache_free(announce->shared_keys);
for (uint32_t i = 0; i < ANNOUNCE_BUCKETS * ANNOUNCE_BUCKET_SIZE; ++i) {
if (announce->entries[i].data != nullptr) {
free(announce->entries[i].data);
}
}
free(announce);
}

View File

@ -0,0 +1,67 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2020-2021 The TokTok team.
*/
#ifndef C_TOXCORE_TOXCORE_ANNOUNCE_H
#define C_TOXCORE_TOXCORE_ANNOUNCE_H
#include "forwarding.h"
#define MAX_ANNOUNCEMENT_SIZE 512
typedef void announce_on_retrieve_cb(void *object, const uint8_t *data, uint16_t length);
uint8_t announce_response_of_request_type(uint8_t request_type);
typedef struct Announcements Announcements;
non_null()
Announcements *new_announcements(const Logger *log, const Random *rng, const Mono_Time *mono_time, Forwarding *forwarding);
/**
* @brief If data is stored, run `on_retrieve_callback` on it.
*
* @return true if data is stored, false otherwise.
*/
non_null(1, 2) nullable(3, 4)
bool announce_on_stored(const Announcements *announce, const uint8_t *data_public_key,
announce_on_retrieve_cb *on_retrieve_callback, void *object);
non_null()
void announce_set_synch_offset(Announcements *announce, int32_t synch_offset);
nullable(1)
void kill_announcements(Announcements *announce);
/* The declarations below are not public, they are exposed only for tests. */
/** @private
* Return xor of first ANNOUNCE_BUCKET_PREFIX_LENGTH bits from one bit after
* base and pk first differ
*/
non_null()
uint16_t announce_get_bucketnum(const uint8_t *base, const uint8_t *pk);
/** @private */
non_null(1, 2) nullable(3)
bool announce_store_data(Announcements *announce, const uint8_t *data_public_key,
const uint8_t *data, uint32_t length, uint32_t timeout);
/** @private */
#define MAX_MAX_ANNOUNCEMENT_TIMEOUT 900
#define MIN_MAX_ANNOUNCEMENT_TIMEOUT 10
#define MAX_ANNOUNCEMENT_TIMEOUT_UPTIME_RATIO 4
/** @private
* For efficient lookup and updating, entries are stored as a hash table keyed
* to the first ANNOUNCE_BUCKET_PREFIX_LENGTH bits starting from one bit after
* the first bit in which data public key first differs from the dht key, with
* (2-adically) closest keys preferentially stored within a given bucket. A
* given key appears at most once (even if timed out).
*/
#define ANNOUNCE_BUCKET_SIZE 8
#define ANNOUNCE_BUCKET_PREFIX_LENGTH 5
#define ANNOUNCE_BUCKETS 32 // ANNOUNCE_BUCKETS = 2 ** ANNOUNCE_BUCKET_PREFIX_LENGTH
#endif

View File

@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
/**
* printf and nonnull attributes for GCC/Clang and Cimple.
*/
#ifndef C_TOXCORE_TOXCORE_ATTRIBUTES_H
#define C_TOXCORE_TOXCORE_ATTRIBUTES_H
/* No declarations here. */
//!TOKSTYLE-
#ifdef __GNUC__
#define GNU_PRINTF(f, a) __attribute__((__format__(__printf__, f, a)))
#else
#define GNU_PRINTF(f, a)
#endif
#if defined(__GNUC__) && defined(_DEBUG) && !defined(__OPTIMIZE__)
#define non_null(...) __attribute__((__nonnull__(__VA_ARGS__)))
#else
#define non_null(...)
#endif
#define nullable(...)
//!TOKSTYLE+
#endif // C_TOXCORE_TOXCORE_ATTRIBUTES_H

View File

@ -0,0 +1,167 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "bin_pack.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../third_party/cmp/cmp.h"
#include "ccompat.h"
struct Bin_Pack {
uint8_t *bytes;
uint32_t bytes_size;
uint32_t bytes_pos;
cmp_ctx_t ctx;
};
non_null()
static bool null_reader(cmp_ctx_t *ctx, void *data, size_t limit)
{
assert(limit == 0);
return false;
}
non_null()
static bool null_skipper(cmp_ctx_t *ctx, size_t limit)
{
assert(limit == 0);
return false;
}
non_null()
static size_t buf_writer(cmp_ctx_t *ctx, const void *data, size_t count)
{
Bin_Pack *bp = (Bin_Pack *)ctx->buf;
assert(bp != nullptr);
const uint32_t new_pos = bp->bytes_pos + count;
if (new_pos < bp->bytes_pos) {
// 32 bit overflow.
return 0;
}
if (bp->bytes != nullptr) {
if (new_pos > bp->bytes_size) {
// Buffer too small.
return 0;
}
memcpy(bp->bytes + bp->bytes_pos, data, count);
}
bp->bytes_pos += count;
return count;
}
non_null(1) nullable(2)
static void bin_pack_init(Bin_Pack *bp, uint8_t *buf, uint32_t buf_size)
{
bp->bytes = buf;
bp->bytes_size = buf_size;
bp->bytes_pos = 0;
cmp_init(&bp->ctx, bp, null_reader, null_skipper, buf_writer);
}
bool bin_pack_obj(bin_pack_cb *callback, const void *obj, uint8_t *buf, uint32_t buf_size)
{
Bin_Pack bp;
bin_pack_init(&bp, buf, buf_size);
return callback(&bp, obj);
}
uint32_t bin_pack_obj_size(bin_pack_cb *callback, const void *obj)
{
Bin_Pack bp;
bin_pack_init(&bp, nullptr, 0);
callback(&bp, obj);
return bp.bytes_pos;
}
Bin_Pack *bin_pack_new(uint8_t *buf, uint32_t buf_size)
{
Bin_Pack *bp = (Bin_Pack *)calloc(1, sizeof(Bin_Pack));
if (bp == nullptr) {
return nullptr;
}
bin_pack_init(bp, buf, buf_size);
return bp;
}
void bin_pack_free(Bin_Pack *bp)
{
free(bp);
}
bool bin_pack_array(Bin_Pack *bp, uint32_t size)
{
return cmp_write_array(&bp->ctx, size);
}
bool bin_pack_bool(Bin_Pack *bp, bool val)
{
return cmp_write_bool(&bp->ctx, val);
}
bool bin_pack_u08(Bin_Pack *bp, uint8_t val)
{
return cmp_write_uinteger(&bp->ctx, val);
}
bool bin_pack_u16(Bin_Pack *bp, uint16_t val)
{
return cmp_write_uinteger(&bp->ctx, val);
}
bool bin_pack_u32(Bin_Pack *bp, uint32_t val)
{
return cmp_write_uinteger(&bp->ctx, val);
}
bool bin_pack_u64(Bin_Pack *bp, uint64_t val)
{
return cmp_write_uinteger(&bp->ctx, val);
}
bool bin_pack_bin(Bin_Pack *bp, const uint8_t *data, uint32_t length)
{
return cmp_write_bin(&bp->ctx, data, length);
}
bool bin_pack_nil(Bin_Pack *bp)
{
return cmp_write_nil(&bp->ctx);
}
bool bin_pack_bin_marker(Bin_Pack *bp, uint32_t size)
{
return cmp_write_bin_marker(&bp->ctx, size);
}
bool bin_pack_u08_b(Bin_Pack *bp, uint8_t val)
{
return bp->ctx.write(&bp->ctx, &val, 1) == 1;
}
bool bin_pack_u16_b(Bin_Pack *bp, uint16_t val)
{
return bin_pack_u08_b(bp, (val >> 8) & 0xff)
&& bin_pack_u08_b(bp, val & 0xff);
}
bool bin_pack_u32_b(Bin_Pack *bp, uint32_t val)
{
return bin_pack_u16_b(bp, (val >> 16) & 0xffff)
&& bin_pack_u16_b(bp, val & 0xffff);
}
bool bin_pack_u64_b(Bin_Pack *bp, uint64_t val)
{
return bin_pack_u32_b(bp, (val >> 32) & 0xffffffff)
&& bin_pack_u32_b(bp, val & 0xffffffff);
}
bool bin_pack_bin_b(Bin_Pack *bp, const uint8_t *data, uint32_t length)
{
return bp->ctx.write(&bp->ctx, data, length) == length;
}

View File

@ -0,0 +1,123 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#ifndef C_TOXCORE_TOXCORE_BIN_PACK_H
#define C_TOXCORE_TOXCORE_BIN_PACK_H
#include <stdbool.h>
#include <stdint.h>
#include "attributes.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Binary serialisation object.
*/
typedef struct Bin_Pack Bin_Pack;
/** @brief Function used to pack an object.
*
* This function would typically cast the `void *` to the actual object pointer type and then call
* more appropriately typed packing functions.
*/
typedef bool bin_pack_cb(Bin_Pack *bp, const void *obj);
/** @brief Determine the serialised size of an object.
*
* @param callback The function called on the created packer and packed object.
* @param obj The object to be packed, passed as `obj` to the callback.
*
* @return The packed size of the passed object according to the callback. UINT32_MAX in case of
* errors such as buffer overflow.
*/
non_null(1) nullable(2)
uint32_t bin_pack_obj_size(bin_pack_cb *callback, const void *obj);
/** @brief Pack an object into a buffer of a given size.
*
* This function creates and initialises a `Bin_Pack` packer object, calls the callback with the
* packer object and the to-be-packed object, and then cleans up the packer object.
*
* You can use `bin_pack_obj_size` to determine the minimum required size of `buf`. If packing
* overflows `uint32_t`, this function returns `false`.
*
* @param callback The function called on the created packer and packed object.
* @param obj The object to be packed, passed as `obj` to the callback.
* @param buf A byte array large enough to hold the serialised representation of `obj`.
* @param buf_size The size of the byte array. Can be `UINT32_MAX` to disable bounds checking.
*
* @retval false if an error occurred (e.g. buffer overflow).
*/
non_null(1, 3) nullable(2)
bool bin_pack_obj(bin_pack_cb *callback, const void *obj, uint8_t *buf, uint32_t buf_size);
/** @brief Allocate a new packer object.
*
* This is the only function that allocates memory in this module.
*
* @param buf A byte array large enough to hold the serialised representation of `obj`.
* @param buf_size The size of the byte array. Can be `UINT32_MAX` to disable bounds checking.
*
* @retval nullptr on allocation failure.
*/
non_null()
Bin_Pack *bin_pack_new(uint8_t *buf, uint32_t buf_size);
/** @brief Deallocates a packer object.
*
* Does not deallocate the buffer inside.
*/
nullable(1)
void bin_pack_free(Bin_Pack *bp);
/** @brief Start packing a MessagePack array.
*
* A call to this function must be followed by exactly `size` calls to other functions below.
*/
non_null()
bool bin_pack_array(Bin_Pack *bp, uint32_t size);
/** @brief Pack a MessagePack bool. */
non_null() bool bin_pack_bool(Bin_Pack *bp, bool val);
/** @brief Pack a `uint8_t` as MessagePack positive integer. */
non_null() bool bin_pack_u08(Bin_Pack *bp, uint8_t val);
/** @brief Pack a `uint16_t` as MessagePack positive integer. */
non_null() bool bin_pack_u16(Bin_Pack *bp, uint16_t val);
/** @brief Pack a `uint32_t` as MessagePack positive integer. */
non_null() bool bin_pack_u32(Bin_Pack *bp, uint32_t val);
/** @brief Pack a `uint64_t` as MessagePack positive integer. */
non_null() bool bin_pack_u64(Bin_Pack *bp, uint64_t val);
/** @brief Pack an empty array member as a MessagePack nil value. */
non_null() bool bin_pack_nil(Bin_Pack *bp);
/** @brief Pack a byte array as MessagePack bin. */
non_null() bool bin_pack_bin(Bin_Pack *bp, const uint8_t *data, uint32_t length);
/** @brief Start packing a custom binary representation.
*
* A call to this function must be followed by exactly `size` bytes packed by functions below.
*/
non_null() bool bin_pack_bin_marker(Bin_Pack *bp, uint32_t size);
/** @brief Write a `uint8_t` directly to the packer in 1 byte. */
non_null() bool bin_pack_u08_b(Bin_Pack *bp, uint8_t val);
/** @brief Write a `uint16_t` as big endian 16 bit int in 2 bytes. */
non_null() bool bin_pack_u16_b(Bin_Pack *bp, uint16_t val);
/** @brief Write a `uint32_t` as big endian 32 bit int in 4 bytes. */
non_null() bool bin_pack_u32_b(Bin_Pack *bp, uint32_t val);
/** @brief Write a `uint64_t` as big endian 64 bit int in 8 bytes. */
non_null() bool bin_pack_u64_b(Bin_Pack *bp, uint64_t val);
/** @brief Write a byte array directly to the packer in `length` bytes.
*
* Note that unless you prepend the array length manually, there is no record of it in the resulting
* serialised representation.
*/
non_null() bool bin_pack_bin_b(Bin_Pack *bp, const uint8_t *data, uint32_t length);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // C_TOXCORE_TOXCORE_BIN_PACK_H

View File

@ -0,0 +1,134 @@
#include "bin_pack.h"
#include <gtest/gtest.h>
#include <array>
#include <memory>
#include <vector>
#include "bin_unpack.h"
namespace {
struct Bin_Pack_Deleter {
void operator()(Bin_Pack *bp) const { bin_pack_free(bp); }
};
using Bin_Pack_Ptr = std::unique_ptr<Bin_Pack, Bin_Pack_Deleter>;
struct Bin_Unpack_Deleter {
void operator()(Bin_Unpack *bu) const { bin_unpack_free(bu); }
};
using Bin_Unpack_Ptr = std::unique_ptr<Bin_Unpack, Bin_Unpack_Deleter>;
TEST(BinPack, TooSmallBufferIsNotExceeded)
{
std::array<uint8_t, 7> buf;
Bin_Pack_Ptr bp(bin_pack_new(buf.data(), buf.size()));
ASSERT_NE(bp, nullptr);
EXPECT_FALSE(bin_pack_u64_b(bp.get(), 1234567812345678LL));
}
TEST(BinPack, PackedUint64CanBeUnpacked)
{
std::array<uint8_t, 8> buf;
Bin_Pack_Ptr bp(bin_pack_new(buf.data(), buf.size()));
ASSERT_NE(bp, nullptr);
ASSERT_TRUE(bin_pack_u64_b(bp.get(), 1234567812345678LL));
Bin_Unpack_Ptr bu(bin_unpack_new(buf.data(), buf.size()));
ASSERT_NE(bu, nullptr);
uint64_t val;
ASSERT_TRUE(bin_unpack_u64_b(bu.get(), &val));
EXPECT_EQ(val, 1234567812345678LL);
}
TEST(BinPack, MsgPackedUint8CanBeUnpackedAsUint32)
{
std::array<uint8_t, 2> buf;
Bin_Pack_Ptr bp(bin_pack_new(buf.data(), buf.size()));
ASSERT_NE(bp, nullptr);
ASSERT_TRUE(bin_pack_u08(bp.get(), 123));
Bin_Unpack_Ptr bu(bin_unpack_new(buf.data(), buf.size()));
ASSERT_NE(bu, nullptr);
uint32_t val;
ASSERT_TRUE(bin_unpack_u32(bu.get(), &val));
EXPECT_EQ(val, 123);
}
TEST(BinPack, MsgPackedUint32CanBeUnpackedAsUint8IfSmallEnough)
{
std::array<uint8_t, 2> buf;
Bin_Pack_Ptr bp(bin_pack_new(buf.data(), buf.size()));
ASSERT_NE(bp, nullptr);
ASSERT_TRUE(bin_pack_u32(bp.get(), 123));
Bin_Unpack_Ptr bu(bin_unpack_new(buf.data(), buf.size()));
ASSERT_NE(bu, nullptr);
uint8_t val;
ASSERT_TRUE(bin_unpack_u08(bu.get(), &val));
EXPECT_EQ(val, 123);
}
TEST(BinPack, LargeMsgPackedUint32CannotBeUnpackedAsUint8)
{
std::array<uint8_t, 5> buf;
Bin_Pack_Ptr bp(bin_pack_new(buf.data(), buf.size()));
ASSERT_NE(bp, nullptr);
ASSERT_TRUE(bin_pack_u32(bp.get(), 1234567));
Bin_Unpack_Ptr bu(bin_unpack_new(buf.data(), buf.size()));
ASSERT_NE(bu, nullptr);
uint8_t val;
EXPECT_FALSE(bin_unpack_u08(bu.get(), &val));
}
TEST(BinPack, BinCanHoldPackedInts)
{
std::array<uint8_t, 12> buf;
Bin_Pack_Ptr bp(bin_pack_new(buf.data(), buf.size()));
ASSERT_NE(bp, nullptr);
ASSERT_TRUE(bin_pack_bin_marker(bp.get(), 8));
ASSERT_TRUE(bin_pack_u64_b(bp.get(), 1234567812345678LL));
ASSERT_TRUE(bin_pack_u16_b(bp.get(), 54321));
Bin_Unpack_Ptr bu(bin_unpack_new(buf.data(), buf.size()));
ASSERT_NE(bu, nullptr);
uint32_t size;
EXPECT_TRUE(bin_unpack_bin_size(bu.get(), &size));
EXPECT_EQ(size, 8);
uint64_t val1;
EXPECT_TRUE(bin_unpack_u64_b(bu.get(), &val1));
EXPECT_EQ(val1, 1234567812345678LL);
uint16_t val2;
EXPECT_TRUE(bin_unpack_u16_b(bu.get(), &val2));
EXPECT_EQ(val2, 54321);
}
TEST(BinPack, BinCanHoldArbitraryData)
{
std::array<uint8_t, 7> buf;
Bin_Pack_Ptr bp(bin_pack_new(buf.data(), buf.size()));
ASSERT_NE(bp, nullptr);
ASSERT_TRUE(bin_pack_bin_marker(bp.get(), 5));
ASSERT_TRUE(bin_pack_bin_b(bp.get(), reinterpret_cast<const uint8_t *>("hello"), 5));
Bin_Unpack_Ptr bu(bin_unpack_new(buf.data(), buf.size()));
ASSERT_NE(bu, nullptr);
std::array<uint8_t, 5> str;
EXPECT_TRUE(bin_unpack_bin_fixed(bu.get(), str.data(), str.size()));
EXPECT_EQ(str, (std::array<uint8_t, 5>{'h', 'e', 'l', 'l', 'o'}));
}
TEST(BinPack, OversizedArrayFailsUnpack)
{
std::array<uint8_t, 1> buf = {0x91};
Bin_Unpack_Ptr bu(bin_unpack_new(buf.data(), buf.size()));
uint32_t size;
EXPECT_FALSE(bin_unpack_array(bu.get(), &size));
}
} // namespace

View File

@ -0,0 +1,190 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "bin_unpack.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../third_party/cmp/cmp.h"
#include "ccompat.h"
struct Bin_Unpack {
const uint8_t *bytes;
uint32_t bytes_size;
cmp_ctx_t ctx;
};
non_null()
static bool buf_reader(cmp_ctx_t *ctx, void *data, size_t limit)
{
Bin_Unpack *reader = (Bin_Unpack *)ctx->buf;
assert(reader != nullptr && reader->bytes != nullptr);
if (limit > reader->bytes_size) {
return false;
}
memcpy(data, reader->bytes, limit);
reader->bytes += limit;
reader->bytes_size -= limit;
return true;
}
non_null()
static bool buf_skipper(cmp_ctx_t *ctx, size_t limit)
{
Bin_Unpack *reader = (Bin_Unpack *)ctx->buf;
assert(reader != nullptr && reader->bytes != nullptr);
if (limit > reader->bytes_size) {
return false;
}
reader->bytes += limit;
reader->bytes_size -= limit;
return true;
}
non_null()
static size_t null_writer(cmp_ctx_t *ctx, const void *data, size_t count)
{
assert(count == 0);
return 0;
}
Bin_Unpack *bin_unpack_new(const uint8_t *buf, uint32_t buf_size)
{
Bin_Unpack *bu = (Bin_Unpack *)calloc(1, sizeof(Bin_Unpack));
if (bu == nullptr) {
return nullptr;
}
bu->bytes = buf;
bu->bytes_size = buf_size;
cmp_init(&bu->ctx, bu, buf_reader, buf_skipper, null_writer);
return bu;
}
void bin_unpack_free(Bin_Unpack *bu)
{
free(bu);
}
bool bin_unpack_array(Bin_Unpack *bu, uint32_t *size)
{
return cmp_read_array(&bu->ctx, size) && *size <= bu->bytes_size;
}
bool bin_unpack_array_fixed(Bin_Unpack *bu, uint32_t required_size)
{
uint32_t size;
return cmp_read_array(&bu->ctx, &size) && size == required_size;
}
bool bin_unpack_bool(Bin_Unpack *bu, bool *val)
{
return cmp_read_bool(&bu->ctx, val);
}
bool bin_unpack_u08(Bin_Unpack *bu, uint8_t *val)
{
return cmp_read_uchar(&bu->ctx, val);
}
bool bin_unpack_u16(Bin_Unpack *bu, uint16_t *val)
{
return cmp_read_ushort(&bu->ctx, val);
}
bool bin_unpack_u32(Bin_Unpack *bu, uint32_t *val)
{
return cmp_read_uint(&bu->ctx, val);
}
bool bin_unpack_u64(Bin_Unpack *bu, uint64_t *val)
{
return cmp_read_ulong(&bu->ctx, val);
}
bool bin_unpack_nil(Bin_Unpack *bu)
{
return cmp_read_nil(&bu->ctx);
}
bool bin_unpack_bin(Bin_Unpack *bu, uint8_t **data_ptr, uint32_t *data_length_ptr)
{
uint32_t bin_size;
if (!bin_unpack_bin_size(bu, &bin_size) || bin_size > bu->bytes_size) {
// There aren't as many bytes as this bin claims to want to allocate.
return false;
}
uint8_t *const data = (uint8_t *)malloc(bin_size);
if (!bin_unpack_bin_b(bu, data, bin_size)) {
free(data);
return false;
}
*data_ptr = data;
*data_length_ptr = bin_size;
return true;
}
bool bin_unpack_bin_fixed(Bin_Unpack *bu, uint8_t *data, uint32_t data_length)
{
uint32_t bin_size;
if (!bin_unpack_bin_size(bu, &bin_size) || bin_size != data_length) {
return false;
}
return bin_unpack_bin_b(bu, data, bin_size);
}
bool bin_unpack_bin_size(Bin_Unpack *bu, uint32_t *size)
{
return cmp_read_bin_size(&bu->ctx, size);
}
bool bin_unpack_u08_b(Bin_Unpack *bu, uint8_t *val)
{
return bin_unpack_bin_b(bu, val, 1);
}
bool bin_unpack_u16_b(Bin_Unpack *bu, uint16_t *val)
{
uint8_t hi = 0;
uint8_t lo = 0;
if (!(bin_unpack_u08_b(bu, &hi)
&& bin_unpack_u08_b(bu, &lo))) {
return false;
}
*val = ((uint16_t)hi << 8) | lo;
return true;
}
bool bin_unpack_u32_b(Bin_Unpack *bu, uint32_t *val)
{
uint16_t hi = 0;
uint16_t lo = 0;
if (!(bin_unpack_u16_b(bu, &hi)
&& bin_unpack_u16_b(bu, &lo))) {
return false;
}
*val = ((uint32_t)hi << 16) | lo;
return true;
}
bool bin_unpack_u64_b(Bin_Unpack *bu, uint64_t *val)
{
uint32_t hi = 0;
uint32_t lo = 0;
if (!(bin_unpack_u32_b(bu, &hi)
&& bin_unpack_u32_b(bu, &lo))) {
return false;
}
*val = ((uint64_t)hi << 32) | lo;
return true;
}
bool bin_unpack_bin_b(Bin_Unpack *bu, uint8_t *data, uint32_t length)
{
return bu->ctx.read(&bu->ctx, data, length);
}

View File

@ -0,0 +1,103 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#ifndef C_TOXCORE_TOXCORE_BIN_UNPACK_H
#define C_TOXCORE_TOXCORE_BIN_UNPACK_H
#include <stdbool.h>
#include <stdint.h>
#include "attributes.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Binary deserialisation object.
*/
typedef struct Bin_Unpack Bin_Unpack;
/** @brief Allocate a new unpacker object.
*
* @param buf The byte array to unpack values from.
* @param buf_size The size of the byte array.
*
* @retval nullptr on allocation failure.
*/
non_null()
Bin_Unpack *bin_unpack_new(const uint8_t *buf, uint32_t buf_size);
/** @brief Deallocates an unpacker object.
*
* Does not deallocate the buffer inside.
*/
nullable(1)
void bin_unpack_free(Bin_Unpack *bu);
/** @brief Start unpacking a MessagePack array.
*
* A call to this function must be followed by exactly `size` calls to other functions below.
*
* @param size Will contain the number of array elements following the array marker.
*/
non_null() bool bin_unpack_array(Bin_Unpack *bu, uint32_t *size);
/** @brief Start unpacking a fixed size MessagePack array.
*
* @retval false if the packed array size is not exactly the required size.
*/
non_null() bool bin_unpack_array_fixed(Bin_Unpack *bu, uint32_t required_size);
/** @brief Unpack a MessagePack bool. */
non_null() bool bin_unpack_bool(Bin_Unpack *bu, bool *val);
/** @brief Unpack a MessagePack positive int into a `uint8_t`. */
non_null() bool bin_unpack_u08(Bin_Unpack *bu, uint8_t *val);
/** @brief Unpack a MessagePack positive int into a `uint16_t`. */
non_null() bool bin_unpack_u16(Bin_Unpack *bu, uint16_t *val);
/** @brief Unpack a MessagePack positive int into a `uint32_t`. */
non_null() bool bin_unpack_u32(Bin_Unpack *bu, uint32_t *val);
/** @brief Unpack a MessagePack positive int into a `uint64_t`. */
non_null() bool bin_unpack_u64(Bin_Unpack *bu, uint64_t *val);
/** @brief Unpack a Messagepack nil value. */
non_null() bool bin_unpack_nil(Bin_Unpack *bu);
/** @brief Unpack a MessagePack bin into a newly allocated byte array.
*
* Allocates a new byte array and stores it into `data_ptr` with its length stored in
* `data_length_ptr`. This function requires that the unpacking buffer has at least as many bytes
* remaining to be unpacked as the bin claims to need, so it's not possible to cause an arbitrarily
* large allocation unless the input array was already that large.
*/
non_null() bool bin_unpack_bin(Bin_Unpack *bu, uint8_t **data_ptr, uint32_t *data_length_ptr);
/** @brief Unpack a MessagePack bin of a fixed length into a pre-allocated byte array.
*
* Unlike the function above, this function does not allocate any memory, but requires the size to
* be known up front.
*/
non_null() bool bin_unpack_bin_fixed(Bin_Unpack *bu, uint8_t *data, uint32_t data_length);
/** @brief Start unpacking a custom binary representation.
*
* A call to this function must be followed by exactly `size` bytes packed by functions below.
*/
non_null() bool bin_unpack_bin_size(Bin_Unpack *bu, uint32_t *size);
/** @brief Read a `uint8_t` directly from the unpacker, consuming 1 byte. */
non_null() bool bin_unpack_u08_b(Bin_Unpack *bu, uint8_t *val);
/** @brief Read a `uint16_t` as big endian 16 bit int, consuming 2 bytes. */
non_null() bool bin_unpack_u16_b(Bin_Unpack *bu, uint16_t *val);
/** @brief Read a `uint32_t` as big endian 32 bit int, consuming 4 bytes. */
non_null() bool bin_unpack_u32_b(Bin_Unpack *bu, uint32_t *val);
/** @brief Read a `uint64_t` as big endian 64 bit int, consuming 8 bytes. */
non_null() bool bin_unpack_u64_b(Bin_Unpack *bu, uint64_t *val);
/** @brief Read a byte array directly from the packer, consuming `length` bytes. */
non_null() bool bin_unpack_bin_b(Bin_Unpack *bu, uint8_t *data, uint32_t length);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // C_TOXCORE_TOXCORE_BIN_UNPACK_H

View File

@ -0,0 +1,4 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "ccompat.h"

View File

@ -0,0 +1,87 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2021 The TokTok team.
*/
/**
* C language compatibility macros for varying compiler support.
*/
#ifndef C_TOXCORE_TOXCORE_CCOMPAT_H
#define C_TOXCORE_TOXCORE_CCOMPAT_H
#include <stddef.h> // NULL, size_t
#include "attributes.h"
//!TOKSTYLE-
// Variable length arrays.
// VLA(type, name, size) allocates a variable length array with automatic
// storage duration. VLA_SIZE(name) evaluates to the runtime size of that array
// in bytes.
//
// If C99 VLAs are not available, an emulation using alloca (stack allocation
// "function") is used. Note the semantic difference: alloca'd memory does not
// get freed at the end of the declaration's scope. Do not use VLA() in loops or
// you may run out of stack space.
#if !defined(DISABLE_VLA) && !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
// C99 VLAs.
#define ALLOC_VLA(type, name, size) type name[size]
#define SIZEOF_VLA sizeof
#else
// Emulation using alloca.
#ifdef _WIN32
#include <malloc.h>
#elif defined(__COMPCERT__)
// TODO(iphydf): This leaks memory like crazy, so compcert is useless for now.
// Once we're rid of VLAs, we can remove this and compcert becomes useful.
#define alloca malloc
#include <stdlib.h>
#elif defined(__linux__)
#include <alloca.h>
#else
#include <stdlib.h>
#if !defined(alloca) && defined(__GNUC__)
#define alloca __builtin_alloca
#endif
#endif
#define ALLOC_VLA(type, name, size) \
const size_t name##_vla_size = (size) * sizeof(type); \
type *const name = (type *)alloca(name##_vla_size)
#define SIZEOF_VLA(name) name##_vla_size
#endif
#ifdef MAX_VLA_SIZE
#include <assert.h>
#define VLA(type, name, size) \
ALLOC_VLA(type, name, size); \
assert((size_t)(size) * sizeof(type) <= MAX_VLA_SIZE)
#else
#define VLA ALLOC_VLA
#endif
#if !defined(__cplusplus) || __cplusplus < 201103L
#define nullptr NULL
#ifndef static_assert
#ifdef __GNUC__
// We'll just assume gcc and clang support C11 _Static_assert.
#define static_assert _Static_assert
#else // !__GNUC__
#define STATIC_ASSERT_(cond, msg, line) typedef int static_assert_##line[(cond) ? 1 : -1]
#define STATIC_ASSERT(cond, msg, line) STATIC_ASSERT_(cond, msg, line)
#define static_assert(cond, msg) STATIC_ASSERT(cond, msg, __LINE__)
#endif // !__GNUC__
#endif // !static_assert
#endif // !__cplusplus
#ifdef __GNUC__
#define GNU_PRINTF(f, a) __attribute__((__format__(__printf__, f, a)))
#else
#define GNU_PRINTF(f, a)
#endif
//!TOKSTYLE+
#endif // C_TOXCORE_TOXCORE_CCOMPAT_H

View File

@ -0,0 +1,582 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2013 Tox project.
*/
/**
* Functions for the core crypto.
*
* NOTE: This code has to be perfect. We don't mess around with encryption.
*/
#include "crypto_core.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#ifndef VANILLA_NACL
// We use libsodium by default.
#include <sodium.h>
#else
#include <crypto_auth.h>
#include <crypto_box.h>
#include <crypto_hash_sha256.h>
#include <crypto_hash_sha512.h>
#include <crypto_scalarmult_curve25519.h>
#include <crypto_verify_16.h>
#include <crypto_verify_32.h>
#include <randombytes.h>
#endif
#include "ccompat.h"
#ifndef crypto_box_MACBYTES
#define crypto_box_MACBYTES (crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES)
#endif
#ifndef VANILLA_NACL
// Need dht because of ENC_SECRET_KEY_SIZE and ENC_PUBLIC_KEY_SIZE
#define ENC_PUBLIC_KEY_SIZE CRYPTO_PUBLIC_KEY_SIZE
#define ENC_SECRET_KEY_SIZE CRYPTO_SECRET_KEY_SIZE
#endif
static_assert(CRYPTO_PUBLIC_KEY_SIZE == crypto_box_PUBLICKEYBYTES,
"CRYPTO_PUBLIC_KEY_SIZE should be equal to crypto_box_PUBLICKEYBYTES");
static_assert(CRYPTO_SECRET_KEY_SIZE == crypto_box_SECRETKEYBYTES,
"CRYPTO_SECRET_KEY_SIZE should be equal to crypto_box_SECRETKEYBYTES");
static_assert(CRYPTO_SHARED_KEY_SIZE == crypto_box_BEFORENMBYTES,
"CRYPTO_SHARED_KEY_SIZE should be equal to crypto_box_BEFORENMBYTES");
static_assert(CRYPTO_SYMMETRIC_KEY_SIZE == crypto_box_BEFORENMBYTES,
"CRYPTO_SYMMETRIC_KEY_SIZE should be equal to crypto_box_BEFORENMBYTES");
static_assert(CRYPTO_MAC_SIZE == crypto_box_MACBYTES,
"CRYPTO_MAC_SIZE should be equal to crypto_box_MACBYTES");
static_assert(CRYPTO_NONCE_SIZE == crypto_box_NONCEBYTES,
"CRYPTO_NONCE_SIZE should be equal to crypto_box_NONCEBYTES");
static_assert(CRYPTO_HMAC_SIZE == crypto_auth_BYTES,
"CRYPTO_HMAC_SIZE should be equal to crypto_auth_BYTES");
static_assert(CRYPTO_HMAC_KEY_SIZE == crypto_auth_KEYBYTES,
"CRYPTO_HMAC_KEY_SIZE should be equal to crypto_auth_KEYBYTES");
static_assert(CRYPTO_SHA256_SIZE == crypto_hash_sha256_BYTES,
"CRYPTO_SHA256_SIZE should be equal to crypto_hash_sha256_BYTES");
static_assert(CRYPTO_SHA512_SIZE == crypto_hash_sha512_BYTES,
"CRYPTO_SHA512_SIZE should be equal to crypto_hash_sha512_BYTES");
static_assert(CRYPTO_PUBLIC_KEY_SIZE == 32,
"CRYPTO_PUBLIC_KEY_SIZE is required to be 32 bytes for pk_equal to work");
#ifndef VANILLA_NACL
static_assert(CRYPTO_SIGNATURE_SIZE == crypto_sign_BYTES,
"CRYPTO_SIGNATURE_SIZE should be equal to crypto_sign_BYTES");
static_assert(CRYPTO_SIGN_PUBLIC_KEY_SIZE == crypto_sign_PUBLICKEYBYTES,
"CRYPTO_SIGN_PUBLIC_KEY_SIZE should be equal to crypto_sign_PUBLICKEYBYTES");
static_assert(CRYPTO_SIGN_SECRET_KEY_SIZE == crypto_sign_SECRETKEYBYTES,
"CRYPTO_SIGN_SECRET_KEY_SIZE should be equal to crypto_sign_SECRETKEYBYTES");
#endif /* VANILLA_NACL */
bool create_extended_keypair(uint8_t *pk, uint8_t *sk)
{
#ifdef VANILLA_NACL
return false;
#else
/* create signature key pair */
crypto_sign_keypair(pk + ENC_PUBLIC_KEY_SIZE, sk + ENC_SECRET_KEY_SIZE);
/* convert public signature key to public encryption key */
const int res1 = crypto_sign_ed25519_pk_to_curve25519(pk, pk + ENC_PUBLIC_KEY_SIZE);
/* convert secret signature key to secret encryption key */
const int res2 = crypto_sign_ed25519_sk_to_curve25519(sk, sk + ENC_SECRET_KEY_SIZE);
return res1 == 0 && res2 == 0;
#endif
}
const uint8_t *get_enc_key(const uint8_t *key)
{
return key;
}
const uint8_t *get_sig_pk(const uint8_t *key)
{
return key + ENC_PUBLIC_KEY_SIZE;
}
void set_sig_pk(uint8_t *key, const uint8_t *sig_pk)
{
memcpy(key + ENC_PUBLIC_KEY_SIZE, sig_pk, SIG_PUBLIC_KEY_SIZE);
}
const uint8_t *get_sig_sk(const uint8_t *key)
{
return key + ENC_SECRET_KEY_SIZE;
}
const uint8_t *get_chat_id(const uint8_t *key)
{
return key + ENC_PUBLIC_KEY_SIZE;
}
#if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
static uint8_t *crypto_malloc(size_t bytes)
{
uint8_t *ptr = (uint8_t *)malloc(bytes);
if (ptr != nullptr) {
crypto_memlock(ptr, bytes);
}
return ptr;
}
nullable(1)
static void crypto_free(uint8_t *ptr, size_t bytes)
{
if (ptr != nullptr) {
crypto_memzero(ptr, bytes);
crypto_memunlock(ptr, bytes);
}
free(ptr);
}
#endif // !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
void crypto_memzero(void *data, size_t length)
{
#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(VANILLA_NACL)
memset(data, 0, length);
#else
sodium_memzero(data, length);
#endif
}
bool crypto_memlock(void *data, size_t length)
{
#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(VANILLA_NACL)
return false;
#else
if (sodium_mlock(data, length) != 0) {
return false;
}
return true;
#endif
}
bool crypto_memunlock(void *data, size_t length)
{
#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(VANILLA_NACL)
return false;
#else
if (sodium_munlock(data, length) != 0) {
return false;
}
return true;
#endif
}
bool pk_equal(const uint8_t pk1[CRYPTO_PUBLIC_KEY_SIZE], const uint8_t pk2[CRYPTO_PUBLIC_KEY_SIZE])
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
// Hope that this is better for the fuzzer
return memcmp(pk1, pk2, CRYPTO_PUBLIC_KEY_SIZE) == 0;
#else
return crypto_verify_32(pk1, pk2) == 0;
#endif
}
void pk_copy(uint8_t dest[CRYPTO_PUBLIC_KEY_SIZE], const uint8_t src[CRYPTO_PUBLIC_KEY_SIZE])
{
memcpy(dest, src, CRYPTO_PUBLIC_KEY_SIZE);
}
bool crypto_sha512_eq(const uint8_t *cksum1, const uint8_t *cksum2)
{
#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
// Hope that this is better for the fuzzer
return memcmp(cksum1, cksum2, CRYPTO_SHA512_SIZE) == 0;
#elif defined(VANILLA_NACL)
const int lo = crypto_verify_32(cksum1, cksum2) == 0 ? 1 : 0;
const int hi = crypto_verify_32(cksum1 + 8, cksum2 + 8) == 0 ? 1 : 0;
return (lo & hi) == 1;
#else
return crypto_verify_64(cksum1, cksum2) == 0;
#endif
}
bool crypto_sha256_eq(const uint8_t *cksum1, const uint8_t *cksum2)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
// Hope that this is better for the fuzzer
return memcmp(cksum1, cksum2, CRYPTO_SHA256_SIZE) == 0;
#else
return crypto_verify_32(cksum1, cksum2) == 0;
#endif
}
uint8_t random_u08(const Random *rng)
{
uint8_t randnum;
random_bytes(rng, &randnum, 1);
return randnum;
}
uint16_t random_u16(const Random *rng)
{
uint16_t randnum;
random_bytes(rng, (uint8_t *)&randnum, sizeof(randnum));
return randnum;
}
uint32_t random_u32(const Random *rng)
{
uint32_t randnum;
random_bytes(rng, (uint8_t *)&randnum, sizeof(randnum));
return randnum;
}
uint64_t random_u64(const Random *rng)
{
uint64_t randnum;
random_bytes(rng, (uint8_t *)&randnum, sizeof(randnum));
return randnum;
}
uint32_t random_range_u32(const Random *rng, uint32_t upper_bound)
{
return rng->funcs->random_uniform(rng->obj, upper_bound);
}
bool crypto_signature_create(uint8_t *signature, const uint8_t *message, uint64_t message_length,
const uint8_t *secret_key)
{
#ifdef VANILLA_NACL
return false;
#else
return crypto_sign_detached(signature, nullptr, message, message_length, secret_key) == 0;
#endif // VANILLA_NACL
}
bool crypto_signature_verify(const uint8_t *signature, const uint8_t *message, uint64_t message_length,
const uint8_t *public_key)
{
#ifdef VANILLA_NACL
return false;
#else
return crypto_sign_verify_detached(signature, message, message_length, public_key) == 0;
#endif
}
bool public_key_valid(const uint8_t *public_key)
{
/* Last bit of key is always zero. */
return public_key[31] < 128;
}
int32_t encrypt_precompute(const uint8_t *public_key, const uint8_t *secret_key,
uint8_t *shared_key)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
memcpy(shared_key, public_key, CRYPTO_SHARED_KEY_SIZE);
return 0;
#else
return crypto_box_beforenm(shared_key, public_key, secret_key);
#endif
}
int32_t encrypt_data_symmetric(const uint8_t *shared_key, const uint8_t *nonce,
const uint8_t *plain, size_t length, uint8_t *encrypted)
{
if (length == 0 || shared_key == nullptr || nonce == nullptr || plain == nullptr || encrypted == nullptr) {
return -1;
}
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
// Don't encrypt anything.
memcpy(encrypted, plain, length);
// Zero MAC to avoid uninitialized memory reads.
memset(encrypted + length, 0, crypto_box_MACBYTES);
#else
const size_t size_temp_plain = length + crypto_box_ZEROBYTES;
const size_t size_temp_encrypted = length + crypto_box_MACBYTES + crypto_box_BOXZEROBYTES;
uint8_t *temp_plain = crypto_malloc(size_temp_plain);
uint8_t *temp_encrypted = crypto_malloc(size_temp_encrypted);
if (temp_plain == nullptr || temp_encrypted == nullptr) {
crypto_free(temp_plain, size_temp_plain);
crypto_free(temp_encrypted, size_temp_encrypted);
return -1;
}
// crypto_box_afternm requires the entire range of the output array be
// initialised with something. It doesn't matter what it's initialised with,
// so we'll pick 0x00.
memset(temp_encrypted, 0, size_temp_encrypted);
memset(temp_plain, 0, crypto_box_ZEROBYTES);
// Pad the message with 32 0 bytes.
memcpy(temp_plain + crypto_box_ZEROBYTES, plain, length);
if (crypto_box_afternm(temp_encrypted, temp_plain, length + crypto_box_ZEROBYTES, nonce,
shared_key) != 0) {
crypto_free(temp_plain, size_temp_plain);
crypto_free(temp_encrypted, size_temp_encrypted);
return -1;
}
// Unpad the encrypted message.
memcpy(encrypted, temp_encrypted + crypto_box_BOXZEROBYTES, length + crypto_box_MACBYTES);
crypto_free(temp_plain, size_temp_plain);
crypto_free(temp_encrypted, size_temp_encrypted);
#endif
assert(length < INT32_MAX - crypto_box_MACBYTES);
return (int32_t)(length + crypto_box_MACBYTES);
}
int32_t decrypt_data_symmetric(const uint8_t *shared_key, const uint8_t *nonce,
const uint8_t *encrypted, size_t length, uint8_t *plain)
{
if (length <= crypto_box_BOXZEROBYTES || shared_key == nullptr || nonce == nullptr || encrypted == nullptr
|| plain == nullptr) {
return -1;
}
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
assert(length >= crypto_box_MACBYTES);
memcpy(plain, encrypted, length - crypto_box_MACBYTES); // Don't encrypt anything
#else
const size_t size_temp_plain = length + crypto_box_ZEROBYTES;
const size_t size_temp_encrypted = length + crypto_box_BOXZEROBYTES;
uint8_t *temp_plain = crypto_malloc(size_temp_plain);
uint8_t *temp_encrypted = crypto_malloc(size_temp_encrypted);
if (temp_plain == nullptr || temp_encrypted == nullptr) {
crypto_free(temp_plain, size_temp_plain);
crypto_free(temp_encrypted, size_temp_encrypted);
return -1;
}
// crypto_box_open_afternm requires the entire range of the output array be
// initialised with something. It doesn't matter what it's initialised with,
// so we'll pick 0x00.
memset(temp_plain, 0, size_temp_plain);
memset(temp_encrypted, 0, crypto_box_BOXZEROBYTES);
// Pad the message with 16 0 bytes.
memcpy(temp_encrypted + crypto_box_BOXZEROBYTES, encrypted, length);
if (crypto_box_open_afternm(temp_plain, temp_encrypted, length + crypto_box_BOXZEROBYTES, nonce,
shared_key) != 0) {
crypto_free(temp_plain, size_temp_plain);
crypto_free(temp_encrypted, size_temp_encrypted);
return -1;
}
memcpy(plain, temp_plain + crypto_box_ZEROBYTES, length - crypto_box_MACBYTES);
crypto_free(temp_plain, size_temp_plain);
crypto_free(temp_encrypted, size_temp_encrypted);
#endif
assert(length > crypto_box_MACBYTES);
assert(length < INT32_MAX);
return (int32_t)(length - crypto_box_MACBYTES);
}
int32_t encrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *nonce,
const uint8_t *plain, size_t length, uint8_t *encrypted)
{
if (public_key == nullptr || secret_key == nullptr) {
return -1;
}
uint8_t k[crypto_box_BEFORENMBYTES];
encrypt_precompute(public_key, secret_key, k);
const int ret = encrypt_data_symmetric(k, nonce, plain, length, encrypted);
crypto_memzero(k, sizeof(k));
return ret;
}
int32_t decrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *nonce,
const uint8_t *encrypted, size_t length, uint8_t *plain)
{
if (public_key == nullptr || secret_key == nullptr) {
return -1;
}
uint8_t k[crypto_box_BEFORENMBYTES];
encrypt_precompute(public_key, secret_key, k);
const int ret = decrypt_data_symmetric(k, nonce, encrypted, length, plain);
crypto_memzero(k, sizeof(k));
return ret;
}
void increment_nonce(uint8_t *nonce)
{
/* TODO(irungentoo): use `increment_nonce_number(nonce, 1)` or
* sodium_increment (change to little endian).
*
* NOTE don't use breaks inside this loop.
* In particular, make sure, as far as possible,
* that loop bounds and their potential underflow or overflow
* are independent of user-controlled input (you may have heard of the Heartbleed bug).
*/
uint_fast16_t carry = 1U;
for (uint32_t i = crypto_box_NONCEBYTES; i != 0; --i) {
carry += (uint_fast16_t)nonce[i - 1];
nonce[i - 1] = (uint8_t)carry;
carry >>= 8;
}
}
void increment_nonce_number(uint8_t *nonce, uint32_t increment)
{
/* NOTE don't use breaks inside this loop
* In particular, make sure, as far as possible,
* that loop bounds and their potential underflow or overflow
* are independent of user-controlled input (you may have heard of the Heartbleed bug).
*/
uint8_t num_as_nonce[crypto_box_NONCEBYTES] = {0};
num_as_nonce[crypto_box_NONCEBYTES - 4] = increment >> 24;
num_as_nonce[crypto_box_NONCEBYTES - 3] = increment >> 16;
num_as_nonce[crypto_box_NONCEBYTES - 2] = increment >> 8;
num_as_nonce[crypto_box_NONCEBYTES - 1] = increment;
uint_fast16_t carry = 0U;
for (uint32_t i = crypto_box_NONCEBYTES; i != 0; --i) {
carry += (uint_fast16_t)nonce[i - 1] + (uint_fast16_t)num_as_nonce[i - 1];
nonce[i - 1] = (uint8_t)carry;
carry >>= 8;
}
}
void random_nonce(const Random *rng, uint8_t *nonce)
{
random_bytes(rng, nonce, crypto_box_NONCEBYTES);
}
void new_symmetric_key(const Random *rng, uint8_t *key)
{
random_bytes(rng, key, CRYPTO_SYMMETRIC_KEY_SIZE);
}
int32_t crypto_new_keypair(const Random *rng, uint8_t *public_key, uint8_t *secret_key)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
random_bytes(rng, secret_key, CRYPTO_SECRET_KEY_SIZE);
memset(public_key, 0, CRYPTO_PUBLIC_KEY_SIZE); // Make MSAN happy
crypto_derive_public_key(public_key, secret_key);
return 0;
#else
return crypto_box_keypair(public_key, secret_key);
#endif
}
void crypto_derive_public_key(uint8_t *public_key, const uint8_t *secret_key)
{
crypto_scalarmult_curve25519_base(public_key, secret_key);
}
void new_hmac_key(const Random *rng, uint8_t key[CRYPTO_HMAC_KEY_SIZE])
{
random_bytes(rng, key, CRYPTO_HMAC_KEY_SIZE);
}
void crypto_hmac(uint8_t auth[CRYPTO_HMAC_SIZE], const uint8_t key[CRYPTO_HMAC_KEY_SIZE], const uint8_t *data,
size_t length)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
memcpy(auth, key, 16);
memcpy(auth + 16, data, length < 16 ? length : 16);
#else
crypto_auth(auth, data, length, key);
#endif
}
bool crypto_hmac_verify(const uint8_t auth[CRYPTO_HMAC_SIZE], const uint8_t key[CRYPTO_HMAC_KEY_SIZE],
const uint8_t *data, size_t length)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
return memcmp(auth, key, 16) == 0 && memcmp(auth + 16, data, length < 16 ? length : 16) == 0;
#else
return crypto_auth_verify(auth, data, length, key) == 0;
#endif
}
void crypto_sha256(uint8_t *hash, const uint8_t *data, size_t length)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
memset(hash, 0, CRYPTO_SHA256_SIZE);
memcpy(hash, data, length < CRYPTO_SHA256_SIZE ? length : CRYPTO_SHA256_SIZE);
#else
crypto_hash_sha256(hash, data, length);
#endif
}
void crypto_sha512(uint8_t *hash, const uint8_t *data, size_t length)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
memset(hash, 0, CRYPTO_SHA512_SIZE);
memcpy(hash, data, length < CRYPTO_SHA512_SIZE ? length : CRYPTO_SHA512_SIZE);
#else
crypto_hash_sha512(hash, data, length);
#endif
}
non_null()
static void sys_random_bytes(void *obj, uint8_t *bytes, size_t length)
{
randombytes(bytes, length);
}
non_null()
static uint32_t sys_random_uniform(void *obj, uint32_t upper_bound)
{
#ifdef VANILLA_NACL
if (upper_bound == 0) {
return 0;
}
uint32_t randnum;
sys_random_bytes(obj, (uint8_t *)&randnum, sizeof(randnum));
return randnum % upper_bound;
#else
return randombytes_uniform(upper_bound);
#endif
}
static const Random_Funcs system_random_funcs = {
sys_random_bytes,
sys_random_uniform,
};
static const Random system_random_obj = {&system_random_funcs};
const Random *system_random(void)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if ((true)) {
return nullptr;
}
#endif
#ifndef VANILLA_NACL
// It is safe to call this function more than once and from different
// threads -- subsequent calls won't have any effects.
if (sodium_init() == -1) {
return nullptr;
}
#endif
return &system_random_obj;
}
void random_bytes(const Random *rng, uint8_t *bytes, size_t length)
{
rng->funcs->random_bytes(rng->obj, bytes, length);
}

View File

@ -0,0 +1,452 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2013 Tox project.
*/
/** @file
* @brief Functions for the core crypto.
*/
#ifndef C_TOXCORE_TOXCORE_CRYPTO_CORE_H
#define C_TOXCORE_TOXCORE_CRYPTO_CORE_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "attributes.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* The number of bytes in a signature.
*/
#define CRYPTO_SIGNATURE_SIZE 64
/**
* The number of bytes in a Tox public key used for signatures.
*/
#define CRYPTO_SIGN_PUBLIC_KEY_SIZE 32
/**
* The number of bytes in a Tox secret key used for signatures.
*/
#define CRYPTO_SIGN_SECRET_KEY_SIZE 64
/**
* @brief The number of bytes in a Tox public key used for encryption.
*/
#define CRYPTO_PUBLIC_KEY_SIZE 32
/**
* @brief The number of bytes in a Tox secret key used for encryption.
*/
#define CRYPTO_SECRET_KEY_SIZE 32
/**
* @brief The number of bytes in a shared key computed from public and secret keys.
*/
#define CRYPTO_SHARED_KEY_SIZE 32
/**
* @brief The number of bytes in a symmetric key.
*/
#define CRYPTO_SYMMETRIC_KEY_SIZE CRYPTO_SHARED_KEY_SIZE
/**
* @brief The number of bytes needed for the MAC (message authentication code) in an
* encrypted message.
*/
#define CRYPTO_MAC_SIZE 16
/**
* @brief The number of bytes in a nonce used for encryption/decryption.
*/
#define CRYPTO_NONCE_SIZE 24
/**
* @brief The number of bytes in a SHA256 hash.
*/
#define CRYPTO_SHA256_SIZE 32
/**
* @brief The number of bytes in a SHA512 hash.
*/
#define CRYPTO_SHA512_SIZE 64
typedef void crypto_random_bytes_cb(void *obj, uint8_t *bytes, size_t length);
typedef uint32_t crypto_random_uniform_cb(void *obj, uint32_t upper_bound);
typedef struct Random_Funcs {
crypto_random_bytes_cb *random_bytes;
crypto_random_uniform_cb *random_uniform;
} Random_Funcs;
typedef struct Random {
const Random_Funcs *funcs;
void *obj;
} Random;
const Random *system_random(void);
/**
* @brief The number of bytes in an encryption public key used by DHT group chats.
*/
#define ENC_PUBLIC_KEY_SIZE CRYPTO_PUBLIC_KEY_SIZE
/**
* @brief The number of bytes in an encryption secret key used by DHT group chats.
*/
#define ENC_SECRET_KEY_SIZE CRYPTO_SECRET_KEY_SIZE
/**
* @brief The number of bytes in a signature public key.
*/
#define SIG_PUBLIC_KEY_SIZE CRYPTO_SIGN_PUBLIC_KEY_SIZE
/**
* @brief The number of bytes in a signature secret key.
*/
#define SIG_SECRET_KEY_SIZE CRYPTO_SIGN_SECRET_KEY_SIZE
/**
* @brief The number of bytes in a DHT group chat public key identifier.
*/
#define CHAT_ID_SIZE SIG_PUBLIC_KEY_SIZE
/**
* @brief The number of bytes in an extended public key used by DHT group chats.
*/
#define EXT_PUBLIC_KEY_SIZE (ENC_PUBLIC_KEY_SIZE + SIG_PUBLIC_KEY_SIZE)
/**
* @brief The number of bytes in an extended secret key used by DHT group chats.
*/
#define EXT_SECRET_KEY_SIZE (ENC_SECRET_KEY_SIZE + SIG_SECRET_KEY_SIZE)
/**
* @brief The number of bytes in an HMAC authenticator.
*/
#define CRYPTO_HMAC_SIZE 32
/**
* @brief The number of bytes in an HMAC secret key.
*/
#define CRYPTO_HMAC_KEY_SIZE 32
/**
* @brief A `bzero`-like function which won't be optimised away by the compiler.
*
* Some compilers will inline `bzero` or `memset` if they can prove that there
* will be no reads to the written data. Use this function if you want to be
* sure the memory is indeed zeroed.
*/
non_null()
void crypto_memzero(void *data, size_t length);
/**
* @brief Compute a SHA256 hash (32 bytes).
*/
non_null()
void crypto_sha256(uint8_t *hash, const uint8_t *data, size_t length);
/**
* @brief Compute a SHA512 hash (64 bytes).
*/
non_null()
void crypto_sha512(uint8_t *hash, const uint8_t *data, size_t length);
/**
* @brief Compute an HMAC authenticator (32 bytes).
*
* @param auth Resulting authenticator.
* @param key Secret key, as generated by `new_hmac_key()`.
*/
non_null()
void crypto_hmac(uint8_t auth[CRYPTO_HMAC_SIZE], const uint8_t key[CRYPTO_HMAC_KEY_SIZE], const uint8_t *data,
size_t length);
/**
* @brief Verify an HMAC authenticator.
*/
non_null()
bool crypto_hmac_verify(const uint8_t auth[CRYPTO_HMAC_SIZE], const uint8_t key[CRYPTO_HMAC_KEY_SIZE],
const uint8_t *data, size_t length);
/**
* @brief Compare 2 public keys of length @ref CRYPTO_PUBLIC_KEY_SIZE, not vulnerable to
* timing attacks.
*
* @retval true if both mem locations of length are equal
* @retval false if they are not
*/
non_null()
bool pk_equal(const uint8_t pk1[CRYPTO_PUBLIC_KEY_SIZE], const uint8_t pk2[CRYPTO_PUBLIC_KEY_SIZE]);
/**
* @brief Copy a public key from `src` to `dest`.
*/
non_null()
void pk_copy(uint8_t dest[CRYPTO_PUBLIC_KEY_SIZE], const uint8_t src[CRYPTO_PUBLIC_KEY_SIZE]);
/**
* @brief Compare 2 SHA512 checksums of length CRYPTO_SHA512_SIZE, not vulnerable to
* timing attacks.
*
* @return true if both mem locations of length are equal, false if they are not.
*/
non_null()
bool crypto_sha512_eq(const uint8_t *cksum1, const uint8_t *cksum2);
/**
* @brief Compare 2 SHA256 checksums of length CRYPTO_SHA256_SIZE, not vulnerable to
* timing attacks.
*
* @return true if both mem locations of length are equal, false if they are not.
*/
non_null()
bool crypto_sha256_eq(const uint8_t *cksum1, const uint8_t *cksum2);
/**
* @brief Return a random 8 bit integer.
*/
non_null()
uint8_t random_u08(const Random *rng);
/**
* @brief Return a random 16 bit integer.
*/
non_null()
uint16_t random_u16(const Random *rng);
/**
* @brief Return a random 32 bit integer.
*/
non_null()
uint32_t random_u32(const Random *rng);
/**
* @brief Return a random 64 bit integer.
*/
non_null()
uint64_t random_u64(const Random *rng);
/**
* @brief Return a random 32 bit integer between 0 and upper_bound (excluded).
*
* On libsodium builds this function guarantees a uniform distribution of possible outputs.
* On vanilla NACL builds this function is equivalent to `random() % upper_bound`.
*/
non_null()
uint32_t random_range_u32(const Random *rng, uint32_t upper_bound);
/** @brief Cryptographically signs a message using the supplied secret key and puts the resulting signature
* in the supplied buffer.
*
* @param signature The buffer for the resulting signature, which must have room for at
* least CRYPTO_SIGNATURE_SIZE bytes.
* @param message The message being signed.
* @param message_length The length in bytes of the message being signed.
* @param secret_key The secret key used to create the signature. The key should be
* produced by either `create_extended_keypair` or the libsodium function `crypto_sign_keypair`.
*
* @retval true on success.
*/
non_null()
bool crypto_signature_create(uint8_t *signature, const uint8_t *message, uint64_t message_length,
const uint8_t *secret_key);
/** @brief Verifies that the given signature was produced by a given message and public key.
*
* @param signature The signature we wish to verify.
* @param message The message we wish to verify.
* @param message_length The length of the message.
* @param public_key The public key counterpart of the secret key that was used to
* create the signature.
*
* @retval true on success.
*/
non_null()
bool crypto_signature_verify(const uint8_t *signature, const uint8_t *message, uint64_t message_length,
const uint8_t *public_key);
/**
* @brief Fill the given nonce with random bytes.
*/
non_null()
void random_nonce(const Random *rng, uint8_t *nonce);
/**
* @brief Fill an array of bytes with random values.
*/
non_null()
void random_bytes(const Random *rng, uint8_t *bytes, size_t length);
/**
* @brief Check if a Tox public key CRYPTO_PUBLIC_KEY_SIZE is valid or not.
*
* This should only be used for input validation.
*
* @return false if it isn't, true if it is.
*/
non_null()
bool public_key_valid(const uint8_t *public_key);
/** @brief Creates an extended keypair: curve25519 and ed25519 for encryption and signing
* respectively. The Encryption keys are derived from the signature keys.
*
* @param pk The buffer where the public key will be stored. Must have room for EXT_PUBLIC_KEY_SIZE bytes.
* @param sk The buffer where the secret key will be stored. Must have room for EXT_SECRET_KEY_SIZE bytes.
*
* @retval true on success.
*/
non_null()
bool create_extended_keypair(uint8_t *pk, uint8_t *sk);
/** Functions for groupchat extended keys */
non_null() const uint8_t *get_enc_key(const uint8_t *key);
non_null() const uint8_t *get_sig_pk(const uint8_t *key);
non_null() void set_sig_pk(uint8_t *key, const uint8_t *sig_pk);
non_null() const uint8_t *get_sig_sk(const uint8_t *key);
non_null() const uint8_t *get_chat_id(const uint8_t *key);
/**
* @brief Generate a new random keypair.
*
* Every call to this function is likely to generate a different keypair.
*/
non_null()
int32_t crypto_new_keypair(const Random *rng, uint8_t *public_key, uint8_t *secret_key);
/**
* @brief Derive the public key from a given secret key.
*/
non_null()
void crypto_derive_public_key(uint8_t *public_key, const uint8_t *secret_key);
/**
* @brief Encrypt message to send from secret key to public key.
*
* Encrypt plain text of the given length to encrypted of
* `length + CRYPTO_MAC_SIZE` using the public key (@ref CRYPTO_PUBLIC_KEY_SIZE
* bytes) of the receiver and the secret key of the sender and a
* @ref CRYPTO_NONCE_SIZE byte nonce.
*
* @retval -1 if there was a problem.
* @return length of encrypted data if everything was fine.
*/
non_null()
int32_t encrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *nonce, const uint8_t *plain,
size_t length, uint8_t *encrypted);
/**
* @brief Decrypt message from public key to secret key.
*
* Decrypt encrypted text of the given @p length to plain text of the given
* `length - CRYPTO_MAC_SIZE` using the public key (@ref CRYPTO_PUBLIC_KEY_SIZE
* bytes) of the sender, the secret key of the receiver and a
* @ref CRYPTO_NONCE_SIZE byte nonce.
*
* @retval -1 if there was a problem (decryption failed).
* @return length of plain text data if everything was fine.
*/
non_null()
int32_t decrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *nonce,
const uint8_t *encrypted, size_t length, uint8_t *plain);
/**
* @brief Fast encrypt/decrypt operations.
*
* Use if this is not a one-time communication. @ref encrypt_precompute does the
* shared-key generation once so it does not have to be performed on every
* encrypt/decrypt.
*/
non_null()
int32_t encrypt_precompute(const uint8_t *public_key, const uint8_t *secret_key, uint8_t *shared_key);
/**
* @brief Encrypt message with precomputed shared key.
*
* Encrypts plain of length length to encrypted of length + @ref CRYPTO_MAC_SIZE
* using a shared key @ref CRYPTO_SYMMETRIC_KEY_SIZE big and a @ref CRYPTO_NONCE_SIZE
* byte nonce.
*
* @retval -1 if there was a problem.
* @return length of encrypted data if everything was fine.
*/
non_null()
int32_t encrypt_data_symmetric(const uint8_t *shared_key, const uint8_t *nonce, const uint8_t *plain, size_t length,
uint8_t *encrypted);
/**
* @brief Decrypt message with precomputed shared key.
*
* Decrypts encrypted of length length to plain of length
* `length - CRYPTO_MAC_SIZE` using a shared key @ref CRYPTO_SHARED_KEY_SIZE
* big and a @ref CRYPTO_NONCE_SIZE byte nonce.
*
* @retval -1 if there was a problem (decryption failed).
* @return length of plain data if everything was fine.
*/
non_null()
int32_t decrypt_data_symmetric(const uint8_t *shared_key, const uint8_t *nonce, const uint8_t *encrypted, size_t length,
uint8_t *plain);
/**
* @brief Increment the given nonce by 1 in big endian (rightmost byte incremented
* first).
*/
non_null()
void increment_nonce(uint8_t *nonce);
/**
* @brief Increment the given nonce by a given number.
*
* The number should be in host byte order.
*/
non_null()
void increment_nonce_number(uint8_t *nonce, uint32_t increment);
/**
* @brief Fill a key @ref CRYPTO_SYMMETRIC_KEY_SIZE big with random bytes.
*/
non_null()
void new_symmetric_key(const Random *rng, uint8_t *key);
/**
* @brief Locks `length` bytes of memory pointed to by `data`.
*
* This will attempt to prevent the specified memory region from being swapped
* to disk.
*
* @return true on success.
*/
non_null()
bool crypto_memlock(void *data, size_t length);
/**
* @brief Unlocks `length` bytes of memory pointed to by `data`.
*
* This allows the specified memory region to be swapped to disk.
*
* This function call has the side effect of zeroing the specified memory region
* whether or not it succeeds. Therefore it should only be used once the memory
* is no longer in use.
*
* @return true on success.
*/
non_null()
bool crypto_memunlock(void *data, size_t length);
/**
* @brief Generate a random secret HMAC key.
*/
non_null()
void new_hmac_key(const Random *rng, uint8_t key[CRYPTO_HMAC_KEY_SIZE]);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // C_TOXCORE_TOXCORE_CRYPTO_CORE_H

View File

@ -0,0 +1,97 @@
#include "crypto_core.h"
#include <gtest/gtest.h>
#include <algorithm>
#include <array>
#include <vector>
#include "util.h"
namespace {
using HmacKey = std::array<uint8_t, CRYPTO_HMAC_KEY_SIZE>;
using Hmac = std::array<uint8_t, CRYPTO_HMAC_SIZE>;
using ExtPublicKey = std::array<uint8_t, EXT_PUBLIC_KEY_SIZE>;
using ExtSecretKey = std::array<uint8_t, EXT_SECRET_KEY_SIZE>;
using Signature = std::array<uint8_t, CRYPTO_SIGNATURE_SIZE>;
using Nonce = std::array<uint8_t, CRYPTO_NONCE_SIZE>;
TEST(CryptoCore, IncrementNonce)
{
Nonce nonce{};
increment_nonce(nonce.data());
EXPECT_EQ(
nonce, (Nonce{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}));
for (int i = 0; i < 0x1F4; ++i) {
increment_nonce(nonce.data());
}
EXPECT_EQ(nonce,
(Nonce{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0xF5}}));
}
TEST(CryptoCore, IncrementNonceNumber)
{
Nonce nonce{};
increment_nonce_number(nonce.data(), 0x1F5);
EXPECT_EQ(nonce,
(Nonce{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0xF5}}));
increment_nonce_number(nonce.data(), 0x1F5);
EXPECT_EQ(nonce,
(Nonce{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, 0xEA}}));
increment_nonce_number(nonce.data(), 0x12345678);
EXPECT_EQ(nonce,
(Nonce{
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x12, 0x34, 0x5A, 0x62}}));
}
TEST(CryptoCore, Signatures)
{
const Random *rng = system_random();
ASSERT_NE(rng, nullptr);
ExtPublicKey pk;
ExtSecretKey sk;
EXPECT_TRUE(create_extended_keypair(pk.data(), sk.data()));
std::vector<uint8_t> message;
// Try a few different sizes, including empty 0 length message.
for (uint8_t i = 0; i < 100; ++i) {
Signature signature;
EXPECT_TRUE(crypto_signature_create(
signature.data(), message.data(), message.size(), get_sig_sk(sk.data())));
EXPECT_TRUE(crypto_signature_verify(
signature.data(), message.data(), message.size(), get_sig_pk(pk.data())));
message.push_back(random_u08(rng));
}
}
TEST(CryptoCore, Hmac)
{
const Random *rng = system_random();
ASSERT_NE(rng, nullptr);
HmacKey sk;
new_hmac_key(rng, sk.data());
std::vector<uint8_t> message;
// Try a few different sizes, including empty 0 length message.
for (uint8_t i = 0; i < 100; ++i) {
Hmac auth;
crypto_hmac(auth.data(), sk.data(), message.data(), message.size());
EXPECT_TRUE(crypto_hmac_verify(auth.data(), sk.data(), message.data(), message.size()));
message.push_back(random_u08(rng));
}
}
} // namespace

View File

@ -0,0 +1,190 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Conference_Connected {
uint32_t conference_number;
};
non_null()
static void tox_event_conference_connected_construct(Tox_Event_Conference_Connected *conference_connected)
{
*conference_connected = (Tox_Event_Conference_Connected) {
0
};
}
non_null()
static void tox_event_conference_connected_destruct(Tox_Event_Conference_Connected *conference_connected)
{
return;
}
non_null()
static void tox_event_conference_connected_set_conference_number(
Tox_Event_Conference_Connected *conference_connected, uint32_t conference_number)
{
assert(conference_connected != nullptr);
conference_connected->conference_number = conference_number;
}
uint32_t tox_event_conference_connected_get_conference_number(
const Tox_Event_Conference_Connected *conference_connected)
{
assert(conference_connected != nullptr);
return conference_connected->conference_number;
}
non_null()
static bool tox_event_conference_connected_pack(
const Tox_Event_Conference_Connected *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_CONFERENCE_CONNECTED)
&& bin_pack_u32(bp, event->conference_number);
}
non_null()
static bool tox_event_conference_connected_unpack(
Tox_Event_Conference_Connected *event, Bin_Unpack *bu)
{
assert(event != nullptr);
return bin_unpack_u32(bu, &event->conference_number);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Conference_Connected *tox_events_add_conference_connected(Tox_Events *events)
{
if (events->conference_connected_size == UINT32_MAX) {
return nullptr;
}
if (events->conference_connected_size == events->conference_connected_capacity) {
const uint32_t new_conference_connected_capacity = events->conference_connected_capacity * 2 + 1;
Tox_Event_Conference_Connected *new_conference_connected = (Tox_Event_Conference_Connected *)realloc(
events->conference_connected, new_conference_connected_capacity * sizeof(Tox_Event_Conference_Connected));
if (new_conference_connected == nullptr) {
return nullptr;
}
events->conference_connected = new_conference_connected;
events->conference_connected_capacity = new_conference_connected_capacity;
}
Tox_Event_Conference_Connected *const conference_connected =
&events->conference_connected[events->conference_connected_size];
tox_event_conference_connected_construct(conference_connected);
++events->conference_connected_size;
return conference_connected;
}
void tox_events_clear_conference_connected(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->conference_connected_size; ++i) {
tox_event_conference_connected_destruct(&events->conference_connected[i]);
}
free(events->conference_connected);
events->conference_connected = nullptr;
events->conference_connected_size = 0;
events->conference_connected_capacity = 0;
}
uint32_t tox_events_get_conference_connected_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->conference_connected_size;
}
const Tox_Event_Conference_Connected *tox_events_get_conference_connected(const Tox_Events *events, uint32_t index)
{
assert(index < events->conference_connected_size);
assert(events->conference_connected != nullptr);
return &events->conference_connected[index];
}
bool tox_events_pack_conference_connected(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_conference_connected_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_conference_connected_pack(tox_events_get_conference_connected(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_conference_connected(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Conference_Connected *event = tox_events_add_conference_connected(events);
if (event == nullptr) {
return false;
}
return tox_event_conference_connected_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_conference_connected(Tox *tox, uint32_t conference_number, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Conference_Connected *conference_connected = tox_events_add_conference_connected(state->events);
if (conference_connected == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_conference_connected_set_conference_number(conference_connected, conference_number);
}

View File

@ -0,0 +1,249 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Conference_Invite {
uint32_t friend_number;
Tox_Conference_Type type;
uint8_t *cookie;
uint32_t cookie_length;
};
non_null()
static void tox_event_conference_invite_construct(Tox_Event_Conference_Invite *conference_invite)
{
*conference_invite = (Tox_Event_Conference_Invite) {
0
};
}
non_null()
static void tox_event_conference_invite_destruct(Tox_Event_Conference_Invite *conference_invite)
{
free(conference_invite->cookie);
}
non_null()
static void tox_event_conference_invite_set_friend_number(Tox_Event_Conference_Invite *conference_invite,
uint32_t friend_number)
{
assert(conference_invite != nullptr);
conference_invite->friend_number = friend_number;
}
uint32_t tox_event_conference_invite_get_friend_number(const Tox_Event_Conference_Invite *conference_invite)
{
assert(conference_invite != nullptr);
return conference_invite->friend_number;
}
non_null()
static void tox_event_conference_invite_set_type(Tox_Event_Conference_Invite *conference_invite,
Tox_Conference_Type type)
{
assert(conference_invite != nullptr);
conference_invite->type = type;
}
Tox_Conference_Type tox_event_conference_invite_get_type(const Tox_Event_Conference_Invite *conference_invite)
{
assert(conference_invite != nullptr);
return conference_invite->type;
}
non_null()
static bool tox_event_conference_invite_set_cookie(Tox_Event_Conference_Invite *conference_invite,
const uint8_t *cookie, uint32_t cookie_length)
{
assert(conference_invite != nullptr);
if (conference_invite->cookie != nullptr) {
free(conference_invite->cookie);
conference_invite->cookie = nullptr;
conference_invite->cookie_length = 0;
}
conference_invite->cookie = (uint8_t *)malloc(cookie_length);
if (conference_invite->cookie == nullptr) {
return false;
}
memcpy(conference_invite->cookie, cookie, cookie_length);
conference_invite->cookie_length = cookie_length;
return true;
}
uint32_t tox_event_conference_invite_get_cookie_length(const Tox_Event_Conference_Invite *conference_invite)
{
assert(conference_invite != nullptr);
return conference_invite->cookie_length;
}
const uint8_t *tox_event_conference_invite_get_cookie(const Tox_Event_Conference_Invite *conference_invite)
{
assert(conference_invite != nullptr);
return conference_invite->cookie;
}
non_null()
static bool tox_event_conference_invite_pack(
const Tox_Event_Conference_Invite *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_CONFERENCE_INVITE)
&& bin_pack_array(bp, 3)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_u32(bp, event->type)
&& bin_pack_bin(bp, event->cookie, event->cookie_length);
}
non_null()
static bool tox_event_conference_invite_unpack(
Tox_Event_Conference_Invite *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 3)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& tox_unpack_conference_type(bu, &event->type)
&& bin_unpack_bin(bu, &event->cookie, &event->cookie_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Conference_Invite *tox_events_add_conference_invite(Tox_Events *events)
{
if (events->conference_invite_size == UINT32_MAX) {
return nullptr;
}
if (events->conference_invite_size == events->conference_invite_capacity) {
const uint32_t new_conference_invite_capacity = events->conference_invite_capacity * 2 + 1;
Tox_Event_Conference_Invite *new_conference_invite = (Tox_Event_Conference_Invite *)realloc(
events->conference_invite, new_conference_invite_capacity * sizeof(Tox_Event_Conference_Invite));
if (new_conference_invite == nullptr) {
return nullptr;
}
events->conference_invite = new_conference_invite;
events->conference_invite_capacity = new_conference_invite_capacity;
}
Tox_Event_Conference_Invite *const conference_invite = &events->conference_invite[events->conference_invite_size];
tox_event_conference_invite_construct(conference_invite);
++events->conference_invite_size;
return conference_invite;
}
void tox_events_clear_conference_invite(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->conference_invite_size; ++i) {
tox_event_conference_invite_destruct(&events->conference_invite[i]);
}
free(events->conference_invite);
events->conference_invite = nullptr;
events->conference_invite_size = 0;
events->conference_invite_capacity = 0;
}
uint32_t tox_events_get_conference_invite_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->conference_invite_size;
}
const Tox_Event_Conference_Invite *tox_events_get_conference_invite(const Tox_Events *events, uint32_t index)
{
assert(index < events->conference_invite_size);
assert(events->conference_invite != nullptr);
return &events->conference_invite[index];
}
bool tox_events_pack_conference_invite(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_conference_invite_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_conference_invite_pack(tox_events_get_conference_invite(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_conference_invite(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Conference_Invite *event = tox_events_add_conference_invite(events);
if (event == nullptr) {
return false;
}
return tox_event_conference_invite_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_conference_invite(Tox *tox, uint32_t friend_number, Tox_Conference_Type type,
const uint8_t *cookie, size_t length, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Conference_Invite *conference_invite = tox_events_add_conference_invite(state->events);
if (conference_invite == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_conference_invite_set_friend_number(conference_invite, friend_number);
tox_event_conference_invite_set_type(conference_invite, type);
tox_event_conference_invite_set_cookie(conference_invite, cookie, length);
}

View File

@ -0,0 +1,266 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Conference_Message {
uint32_t conference_number;
uint32_t peer_number;
Tox_Message_Type type;
uint8_t *message;
uint32_t message_length;
};
non_null()
static void tox_event_conference_message_construct(Tox_Event_Conference_Message *conference_message)
{
*conference_message = (Tox_Event_Conference_Message) {
0
};
}
non_null()
static void tox_event_conference_message_destruct(Tox_Event_Conference_Message *conference_message)
{
free(conference_message->message);
}
non_null()
static void tox_event_conference_message_set_conference_number(Tox_Event_Conference_Message *conference_message,
uint32_t conference_number)
{
assert(conference_message != nullptr);
conference_message->conference_number = conference_number;
}
uint32_t tox_event_conference_message_get_conference_number(const Tox_Event_Conference_Message *conference_message)
{
assert(conference_message != nullptr);
return conference_message->conference_number;
}
non_null()
static void tox_event_conference_message_set_peer_number(Tox_Event_Conference_Message *conference_message,
uint32_t peer_number)
{
assert(conference_message != nullptr);
conference_message->peer_number = peer_number;
}
uint32_t tox_event_conference_message_get_peer_number(const Tox_Event_Conference_Message *conference_message)
{
assert(conference_message != nullptr);
return conference_message->peer_number;
}
non_null()
static void tox_event_conference_message_set_type(Tox_Event_Conference_Message *conference_message,
Tox_Message_Type type)
{
assert(conference_message != nullptr);
conference_message->type = type;
}
Tox_Message_Type tox_event_conference_message_get_type(const Tox_Event_Conference_Message *conference_message)
{
assert(conference_message != nullptr);
return conference_message->type;
}
non_null()
static bool tox_event_conference_message_set_message(Tox_Event_Conference_Message *conference_message,
const uint8_t *message, uint32_t message_length)
{
assert(conference_message != nullptr);
if (conference_message->message != nullptr) {
free(conference_message->message);
conference_message->message = nullptr;
conference_message->message_length = 0;
}
conference_message->message = (uint8_t *)malloc(message_length);
if (conference_message->message == nullptr) {
return false;
}
memcpy(conference_message->message, message, message_length);
conference_message->message_length = message_length;
return true;
}
uint32_t tox_event_conference_message_get_message_length(const Tox_Event_Conference_Message *conference_message)
{
assert(conference_message != nullptr);
return conference_message->message_length;
}
const uint8_t *tox_event_conference_message_get_message(const Tox_Event_Conference_Message *conference_message)
{
assert(conference_message != nullptr);
return conference_message->message;
}
non_null()
static bool tox_event_conference_message_pack(
const Tox_Event_Conference_Message *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_CONFERENCE_MESSAGE)
&& bin_pack_array(bp, 4)
&& bin_pack_u32(bp, event->conference_number)
&& bin_pack_u32(bp, event->peer_number)
&& bin_pack_u32(bp, event->type)
&& bin_pack_bin(bp, event->message, event->message_length);
}
non_null()
static bool tox_event_conference_message_unpack(
Tox_Event_Conference_Message *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 4)) {
return false;
}
return bin_unpack_u32(bu, &event->conference_number)
&& bin_unpack_u32(bu, &event->peer_number)
&& tox_unpack_message_type(bu, &event->type)
&& bin_unpack_bin(bu, &event->message, &event->message_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Conference_Message *tox_events_add_conference_message(Tox_Events *events)
{
if (events->conference_message_size == UINT32_MAX) {
return nullptr;
}
if (events->conference_message_size == events->conference_message_capacity) {
const uint32_t new_conference_message_capacity = events->conference_message_capacity * 2 + 1;
Tox_Event_Conference_Message *new_conference_message = (Tox_Event_Conference_Message *)realloc(
events->conference_message, new_conference_message_capacity * sizeof(Tox_Event_Conference_Message));
if (new_conference_message == nullptr) {
return nullptr;
}
events->conference_message = new_conference_message;
events->conference_message_capacity = new_conference_message_capacity;
}
Tox_Event_Conference_Message *const conference_message = &events->conference_message[events->conference_message_size];
tox_event_conference_message_construct(conference_message);
++events->conference_message_size;
return conference_message;
}
void tox_events_clear_conference_message(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->conference_message_size; ++i) {
tox_event_conference_message_destruct(&events->conference_message[i]);
}
free(events->conference_message);
events->conference_message = nullptr;
events->conference_message_size = 0;
events->conference_message_capacity = 0;
}
uint32_t tox_events_get_conference_message_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->conference_message_size;
}
const Tox_Event_Conference_Message *tox_events_get_conference_message(const Tox_Events *events, uint32_t index)
{
assert(index < events->conference_message_size);
assert(events->conference_message != nullptr);
return &events->conference_message[index];
}
bool tox_events_pack_conference_message(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_conference_message_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_conference_message_pack(tox_events_get_conference_message(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_conference_message(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Conference_Message *event = tox_events_add_conference_message(events);
if (event == nullptr) {
return false;
}
return tox_event_conference_message_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_conference_message(Tox *tox, uint32_t conference_number, uint32_t peer_number,
Tox_Message_Type type, const uint8_t *message, size_t length, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Conference_Message *conference_message = tox_events_add_conference_message(state->events);
if (conference_message == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_conference_message_set_conference_number(conference_message, conference_number);
tox_event_conference_message_set_peer_number(conference_message, peer_number);
tox_event_conference_message_set_type(conference_message, type);
tox_event_conference_message_set_message(conference_message, message, length);
}

View File

@ -0,0 +1,195 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Conference_Peer_List_Changed {
uint32_t conference_number;
};
non_null()
static void tox_event_conference_peer_list_changed_construct(Tox_Event_Conference_Peer_List_Changed
*conference_peer_list_changed)
{
*conference_peer_list_changed = (Tox_Event_Conference_Peer_List_Changed) {
0
};
}
non_null()
static void tox_event_conference_peer_list_changed_destruct(Tox_Event_Conference_Peer_List_Changed
*conference_peer_list_changed)
{
return;
}
non_null()
static void tox_event_conference_peer_list_changed_set_conference_number(Tox_Event_Conference_Peer_List_Changed
*conference_peer_list_changed, uint32_t conference_number)
{
assert(conference_peer_list_changed != nullptr);
conference_peer_list_changed->conference_number = conference_number;
}
uint32_t tox_event_conference_peer_list_changed_get_conference_number(const Tox_Event_Conference_Peer_List_Changed
*conference_peer_list_changed)
{
assert(conference_peer_list_changed != nullptr);
return conference_peer_list_changed->conference_number;
}
non_null()
static bool tox_event_conference_peer_list_changed_pack(
const Tox_Event_Conference_Peer_List_Changed *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED)
&& bin_pack_u32(bp, event->conference_number);
}
non_null()
static bool tox_event_conference_peer_list_changed_unpack(
Tox_Event_Conference_Peer_List_Changed *event, Bin_Unpack *bu)
{
assert(event != nullptr);
return bin_unpack_u32(bu, &event->conference_number);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Conference_Peer_List_Changed *tox_events_add_conference_peer_list_changed(Tox_Events *events)
{
if (events->conference_peer_list_changed_size == UINT32_MAX) {
return nullptr;
}
if (events->conference_peer_list_changed_size == events->conference_peer_list_changed_capacity) {
const uint32_t new_conference_peer_list_changed_capacity = events->conference_peer_list_changed_capacity * 2 + 1;
Tox_Event_Conference_Peer_List_Changed *new_conference_peer_list_changed = (Tox_Event_Conference_Peer_List_Changed *)
realloc(
events->conference_peer_list_changed,
new_conference_peer_list_changed_capacity * sizeof(Tox_Event_Conference_Peer_List_Changed));
if (new_conference_peer_list_changed == nullptr) {
return nullptr;
}
events->conference_peer_list_changed = new_conference_peer_list_changed;
events->conference_peer_list_changed_capacity = new_conference_peer_list_changed_capacity;
}
Tox_Event_Conference_Peer_List_Changed *const conference_peer_list_changed =
&events->conference_peer_list_changed[events->conference_peer_list_changed_size];
tox_event_conference_peer_list_changed_construct(conference_peer_list_changed);
++events->conference_peer_list_changed_size;
return conference_peer_list_changed;
}
void tox_events_clear_conference_peer_list_changed(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->conference_peer_list_changed_size; ++i) {
tox_event_conference_peer_list_changed_destruct(&events->conference_peer_list_changed[i]);
}
free(events->conference_peer_list_changed);
events->conference_peer_list_changed = nullptr;
events->conference_peer_list_changed_size = 0;
events->conference_peer_list_changed_capacity = 0;
}
uint32_t tox_events_get_conference_peer_list_changed_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->conference_peer_list_changed_size;
}
const Tox_Event_Conference_Peer_List_Changed *tox_events_get_conference_peer_list_changed(const Tox_Events *events,
uint32_t index)
{
assert(index < events->conference_peer_list_changed_size);
assert(events->conference_peer_list_changed != nullptr);
return &events->conference_peer_list_changed[index];
}
bool tox_events_pack_conference_peer_list_changed(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_conference_peer_list_changed_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_conference_peer_list_changed_pack(tox_events_get_conference_peer_list_changed(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_conference_peer_list_changed(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Conference_Peer_List_Changed *event = tox_events_add_conference_peer_list_changed(events);
if (event == nullptr) {
return false;
}
return tox_event_conference_peer_list_changed_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_conference_peer_list_changed(Tox *tox, uint32_t conference_number, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Conference_Peer_List_Changed *conference_peer_list_changed = tox_events_add_conference_peer_list_changed(
state->events);
if (conference_peer_list_changed == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_conference_peer_list_changed_set_conference_number(conference_peer_list_changed, conference_number);
}

View File

@ -0,0 +1,250 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Conference_Peer_Name {
uint32_t conference_number;
uint32_t peer_number;
uint8_t *name;
uint32_t name_length;
};
non_null()
static void tox_event_conference_peer_name_construct(Tox_Event_Conference_Peer_Name *conference_peer_name)
{
*conference_peer_name = (Tox_Event_Conference_Peer_Name) {
0
};
}
non_null()
static void tox_event_conference_peer_name_destruct(Tox_Event_Conference_Peer_Name *conference_peer_name)
{
free(conference_peer_name->name);
}
non_null()
static void tox_event_conference_peer_name_set_conference_number(Tox_Event_Conference_Peer_Name *conference_peer_name,
uint32_t conference_number)
{
assert(conference_peer_name != nullptr);
conference_peer_name->conference_number = conference_number;
}
uint32_t tox_event_conference_peer_name_get_conference_number(const Tox_Event_Conference_Peer_Name
*conference_peer_name)
{
assert(conference_peer_name != nullptr);
return conference_peer_name->conference_number;
}
non_null()
static void tox_event_conference_peer_name_set_peer_number(Tox_Event_Conference_Peer_Name *conference_peer_name,
uint32_t peer_number)
{
assert(conference_peer_name != nullptr);
conference_peer_name->peer_number = peer_number;
}
uint32_t tox_event_conference_peer_name_get_peer_number(const Tox_Event_Conference_Peer_Name *conference_peer_name)
{
assert(conference_peer_name != nullptr);
return conference_peer_name->peer_number;
}
non_null()
static bool tox_event_conference_peer_name_set_name(Tox_Event_Conference_Peer_Name *conference_peer_name,
const uint8_t *name, uint32_t name_length)
{
assert(conference_peer_name != nullptr);
if (conference_peer_name->name != nullptr) {
free(conference_peer_name->name);
conference_peer_name->name = nullptr;
conference_peer_name->name_length = 0;
}
conference_peer_name->name = (uint8_t *)malloc(name_length);
if (conference_peer_name->name == nullptr) {
return false;
}
memcpy(conference_peer_name->name, name, name_length);
conference_peer_name->name_length = name_length;
return true;
}
uint32_t tox_event_conference_peer_name_get_name_length(const Tox_Event_Conference_Peer_Name *conference_peer_name)
{
assert(conference_peer_name != nullptr);
return conference_peer_name->name_length;
}
const uint8_t *tox_event_conference_peer_name_get_name(const Tox_Event_Conference_Peer_Name *conference_peer_name)
{
assert(conference_peer_name != nullptr);
return conference_peer_name->name;
}
non_null()
static bool tox_event_conference_peer_name_pack(
const Tox_Event_Conference_Peer_Name *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_CONFERENCE_PEER_NAME)
&& bin_pack_array(bp, 3)
&& bin_pack_u32(bp, event->conference_number)
&& bin_pack_u32(bp, event->peer_number)
&& bin_pack_bin(bp, event->name, event->name_length);
}
non_null()
static bool tox_event_conference_peer_name_unpack(
Tox_Event_Conference_Peer_Name *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 3)) {
return false;
}
return bin_unpack_u32(bu, &event->conference_number)
&& bin_unpack_u32(bu, &event->peer_number)
&& bin_unpack_bin(bu, &event->name, &event->name_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Conference_Peer_Name *tox_events_add_conference_peer_name(Tox_Events *events)
{
if (events->conference_peer_name_size == UINT32_MAX) {
return nullptr;
}
if (events->conference_peer_name_size == events->conference_peer_name_capacity) {
const uint32_t new_conference_peer_name_capacity = events->conference_peer_name_capacity * 2 + 1;
Tox_Event_Conference_Peer_Name *new_conference_peer_name = (Tox_Event_Conference_Peer_Name *)realloc(
events->conference_peer_name, new_conference_peer_name_capacity * sizeof(Tox_Event_Conference_Peer_Name));
if (new_conference_peer_name == nullptr) {
return nullptr;
}
events->conference_peer_name = new_conference_peer_name;
events->conference_peer_name_capacity = new_conference_peer_name_capacity;
}
Tox_Event_Conference_Peer_Name *const conference_peer_name =
&events->conference_peer_name[events->conference_peer_name_size];
tox_event_conference_peer_name_construct(conference_peer_name);
++events->conference_peer_name_size;
return conference_peer_name;
}
void tox_events_clear_conference_peer_name(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->conference_peer_name_size; ++i) {
tox_event_conference_peer_name_destruct(&events->conference_peer_name[i]);
}
free(events->conference_peer_name);
events->conference_peer_name = nullptr;
events->conference_peer_name_size = 0;
events->conference_peer_name_capacity = 0;
}
uint32_t tox_events_get_conference_peer_name_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->conference_peer_name_size;
}
const Tox_Event_Conference_Peer_Name *tox_events_get_conference_peer_name(const Tox_Events *events, uint32_t index)
{
assert(index < events->conference_peer_name_size);
assert(events->conference_peer_name != nullptr);
return &events->conference_peer_name[index];
}
bool tox_events_pack_conference_peer_name(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_conference_peer_name_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_conference_peer_name_pack(tox_events_get_conference_peer_name(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_conference_peer_name(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Conference_Peer_Name *event = tox_events_add_conference_peer_name(events);
if (event == nullptr) {
return false;
}
return tox_event_conference_peer_name_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_conference_peer_name(Tox *tox, uint32_t conference_number, uint32_t peer_number,
const uint8_t *name, size_t length, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Conference_Peer_Name *conference_peer_name = tox_events_add_conference_peer_name(state->events);
if (conference_peer_name == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_conference_peer_name_set_conference_number(conference_peer_name, conference_number);
tox_event_conference_peer_name_set_peer_number(conference_peer_name, peer_number);
tox_event_conference_peer_name_set_name(conference_peer_name, name, length);
}

View File

@ -0,0 +1,248 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Conference_Title {
uint32_t conference_number;
uint32_t peer_number;
uint8_t *title;
uint32_t title_length;
};
non_null()
static void tox_event_conference_title_construct(Tox_Event_Conference_Title *conference_title)
{
*conference_title = (Tox_Event_Conference_Title) {
0
};
}
non_null()
static void tox_event_conference_title_destruct(Tox_Event_Conference_Title *conference_title)
{
free(conference_title->title);
}
non_null()
static void tox_event_conference_title_set_conference_number(Tox_Event_Conference_Title *conference_title,
uint32_t conference_number)
{
assert(conference_title != nullptr);
conference_title->conference_number = conference_number;
}
uint32_t tox_event_conference_title_get_conference_number(const Tox_Event_Conference_Title *conference_title)
{
assert(conference_title != nullptr);
return conference_title->conference_number;
}
non_null()
static void tox_event_conference_title_set_peer_number(Tox_Event_Conference_Title *conference_title,
uint32_t peer_number)
{
assert(conference_title != nullptr);
conference_title->peer_number = peer_number;
}
uint32_t tox_event_conference_title_get_peer_number(const Tox_Event_Conference_Title *conference_title)
{
assert(conference_title != nullptr);
return conference_title->peer_number;
}
non_null()
static bool tox_event_conference_title_set_title(Tox_Event_Conference_Title *conference_title, const uint8_t *title,
uint32_t title_length)
{
assert(conference_title != nullptr);
if (conference_title->title != nullptr) {
free(conference_title->title);
conference_title->title = nullptr;
conference_title->title_length = 0;
}
conference_title->title = (uint8_t *)malloc(title_length);
if (conference_title->title == nullptr) {
return false;
}
memcpy(conference_title->title, title, title_length);
conference_title->title_length = title_length;
return true;
}
uint32_t tox_event_conference_title_get_title_length(const Tox_Event_Conference_Title *conference_title)
{
assert(conference_title != nullptr);
return conference_title->title_length;
}
const uint8_t *tox_event_conference_title_get_title(const Tox_Event_Conference_Title *conference_title)
{
assert(conference_title != nullptr);
return conference_title->title;
}
non_null()
static bool tox_event_conference_title_pack(
const Tox_Event_Conference_Title *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_CONFERENCE_TITLE)
&& bin_pack_array(bp, 3)
&& bin_pack_u32(bp, event->conference_number)
&& bin_pack_u32(bp, event->peer_number)
&& bin_pack_bin(bp, event->title, event->title_length);
}
non_null()
static bool tox_event_conference_title_unpack(
Tox_Event_Conference_Title *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 3)) {
return false;
}
return bin_unpack_u32(bu, &event->conference_number)
&& bin_unpack_u32(bu, &event->peer_number)
&& bin_unpack_bin(bu, &event->title, &event->title_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Conference_Title *tox_events_add_conference_title(Tox_Events *events)
{
if (events->conference_title_size == UINT32_MAX) {
return nullptr;
}
if (events->conference_title_size == events->conference_title_capacity) {
const uint32_t new_conference_title_capacity = events->conference_title_capacity * 2 + 1;
Tox_Event_Conference_Title *new_conference_title = (Tox_Event_Conference_Title *)realloc(
events->conference_title, new_conference_title_capacity * sizeof(Tox_Event_Conference_Title));
if (new_conference_title == nullptr) {
return nullptr;
}
events->conference_title = new_conference_title;
events->conference_title_capacity = new_conference_title_capacity;
}
Tox_Event_Conference_Title *const conference_title = &events->conference_title[events->conference_title_size];
tox_event_conference_title_construct(conference_title);
++events->conference_title_size;
return conference_title;
}
void tox_events_clear_conference_title(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->conference_title_size; ++i) {
tox_event_conference_title_destruct(&events->conference_title[i]);
}
free(events->conference_title);
events->conference_title = nullptr;
events->conference_title_size = 0;
events->conference_title_capacity = 0;
}
uint32_t tox_events_get_conference_title_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->conference_title_size;
}
const Tox_Event_Conference_Title *tox_events_get_conference_title(const Tox_Events *events, uint32_t index)
{
assert(index < events->conference_title_size);
assert(events->conference_title != nullptr);
return &events->conference_title[index];
}
bool tox_events_pack_conference_title(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_conference_title_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_conference_title_pack(tox_events_get_conference_title(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_conference_title(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Conference_Title *event = tox_events_add_conference_title(events);
if (event == nullptr) {
return false;
}
return tox_event_conference_title_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_conference_title(Tox *tox, uint32_t conference_number, uint32_t peer_number,
const uint8_t *title, size_t length, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Conference_Title *conference_title = tox_events_add_conference_title(state->events);
if (conference_title == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_conference_title_set_conference_number(conference_title, conference_number);
tox_event_conference_title_set_peer_number(conference_title, peer_number);
tox_event_conference_title_set_title(conference_title, title, length);
}

View File

@ -0,0 +1,82 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include "../ccompat.h"
Tox_Events_State *tox_events_alloc(void *user_data)
{
Tox_Events_State *state = (Tox_Events_State *)user_data;
assert(state != nullptr);
if (state->events != nullptr) {
// Already allocated.
return state;
}
state->events = (Tox_Events *)calloc(1, sizeof(Tox_Events));
if (state->events == nullptr) {
// It's still null => allocation failed.
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
} else {
*state->events = (Tox_Events) {
nullptr
};
}
return state;
}
void tox_events_free(Tox_Events *events)
{
if (events == nullptr) {
return;
}
tox_events_clear_conference_connected(events);
tox_events_clear_conference_invite(events);
tox_events_clear_conference_message(events);
tox_events_clear_conference_peer_list_changed(events);
tox_events_clear_conference_peer_name(events);
tox_events_clear_conference_title(events);
tox_events_clear_file_chunk_request(events);
tox_events_clear_file_recv_chunk(events);
tox_events_clear_file_recv_control(events);
tox_events_clear_file_recv(events);
tox_events_clear_friend_connection_status(events);
tox_events_clear_friend_lossless_packet(events);
tox_events_clear_friend_lossy_packet(events);
tox_events_clear_friend_message(events);
tox_events_clear_friend_name(events);
tox_events_clear_friend_read_receipt(events);
tox_events_clear_friend_request(events);
tox_events_clear_friend_status(events);
tox_events_clear_friend_status_message(events);
tox_events_clear_friend_typing(events);
tox_events_clear_self_connection_status(events);
tox_events_clear_group_peer_name(events);
tox_events_clear_group_peer_status(events);
tox_events_clear_group_topic(events);
tox_events_clear_group_privacy_state(events);
tox_events_clear_group_voice_state(events);
tox_events_clear_group_topic_lock(events);
tox_events_clear_group_peer_limit(events);
tox_events_clear_group_password(events);
tox_events_clear_group_message(events);
tox_events_clear_group_private_message(events);
tox_events_clear_group_custom_packet(events);
tox_events_clear_group_custom_private_packet(events);
tox_events_clear_group_invite(events);
tox_events_clear_group_peer_join(events);
tox_events_clear_group_peer_exit(events);
tox_events_clear_group_self_join(events);
tox_events_clear_group_join_fail(events);
tox_events_clear_group_moderation(events);
free(events);
}

View File

@ -0,0 +1,360 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#ifndef C_TOXCORE_TOXCORE_TOX_EVENTS_INTERNAL_H
#define C_TOXCORE_TOXCORE_TOX_EVENTS_INTERNAL_H
#include "../attributes.h"
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../tox_events.h"
#ifdef __cplusplus
extern "C" {
#endif
struct Tox_Events {
Tox_Event_Conference_Connected *conference_connected;
uint32_t conference_connected_size;
uint32_t conference_connected_capacity;
Tox_Event_Conference_Invite *conference_invite;
uint32_t conference_invite_size;
uint32_t conference_invite_capacity;
Tox_Event_Conference_Message *conference_message;
uint32_t conference_message_size;
uint32_t conference_message_capacity;
Tox_Event_Conference_Peer_List_Changed *conference_peer_list_changed;
uint32_t conference_peer_list_changed_size;
uint32_t conference_peer_list_changed_capacity;
Tox_Event_Conference_Peer_Name *conference_peer_name;
uint32_t conference_peer_name_size;
uint32_t conference_peer_name_capacity;
Tox_Event_Conference_Title *conference_title;
uint32_t conference_title_size;
uint32_t conference_title_capacity;
Tox_Event_File_Chunk_Request *file_chunk_request;
uint32_t file_chunk_request_size;
uint32_t file_chunk_request_capacity;
Tox_Event_File_Recv *file_recv;
uint32_t file_recv_size;
uint32_t file_recv_capacity;
Tox_Event_File_Recv_Chunk *file_recv_chunk;
uint32_t file_recv_chunk_size;
uint32_t file_recv_chunk_capacity;
Tox_Event_File_Recv_Control *file_recv_control;
uint32_t file_recv_control_size;
uint32_t file_recv_control_capacity;
Tox_Event_Friend_Connection_Status *friend_connection_status;
uint32_t friend_connection_status_size;
uint32_t friend_connection_status_capacity;
Tox_Event_Friend_Lossless_Packet *friend_lossless_packet;
uint32_t friend_lossless_packet_size;
uint32_t friend_lossless_packet_capacity;
Tox_Event_Friend_Lossy_Packet *friend_lossy_packet;
uint32_t friend_lossy_packet_size;
uint32_t friend_lossy_packet_capacity;
Tox_Event_Friend_Message *friend_message;
uint32_t friend_message_size;
uint32_t friend_message_capacity;
Tox_Event_Friend_Name *friend_name;
uint32_t friend_name_size;
uint32_t friend_name_capacity;
Tox_Event_Friend_Read_Receipt *friend_read_receipt;
uint32_t friend_read_receipt_size;
uint32_t friend_read_receipt_capacity;
Tox_Event_Friend_Request *friend_request;
uint32_t friend_request_size;
uint32_t friend_request_capacity;
Tox_Event_Friend_Status *friend_status;
uint32_t friend_status_size;
uint32_t friend_status_capacity;
Tox_Event_Friend_Status_Message *friend_status_message;
uint32_t friend_status_message_size;
uint32_t friend_status_message_capacity;
Tox_Event_Friend_Typing *friend_typing;
uint32_t friend_typing_size;
uint32_t friend_typing_capacity;
Tox_Event_Self_Connection_Status *self_connection_status;
uint32_t self_connection_status_size;
uint32_t self_connection_status_capacity;
Tox_Event_Group_Peer_Name *group_peer_name;
uint32_t group_peer_name_size;
uint32_t group_peer_name_capacity;
Tox_Event_Group_Peer_Status *group_peer_status;
uint32_t group_peer_status_size;
uint32_t group_peer_status_capacity;
Tox_Event_Group_Topic *group_topic;
uint32_t group_topic_size;
uint32_t group_topic_capacity;
Tox_Event_Group_Privacy_State *group_privacy_state;
uint32_t group_privacy_state_size;
uint32_t group_privacy_state_capacity;
Tox_Event_Group_Voice_State *group_voice_state;
uint32_t group_voice_state_size;
uint32_t group_voice_state_capacity;
Tox_Event_Group_Topic_Lock *group_topic_lock;
uint32_t group_topic_lock_size;
uint32_t group_topic_lock_capacity;
Tox_Event_Group_Peer_Limit *group_peer_limit;
uint32_t group_peer_limit_size;
uint32_t group_peer_limit_capacity;
Tox_Event_Group_Password *group_password;
uint32_t group_password_size;
uint32_t group_password_capacity;
Tox_Event_Group_Message *group_message;
uint32_t group_message_size;
uint32_t group_message_capacity;
Tox_Event_Group_Private_Message *group_private_message;
uint32_t group_private_message_size;
uint32_t group_private_message_capacity;
Tox_Event_Group_Custom_Packet *group_custom_packet;
uint32_t group_custom_packet_size;
uint32_t group_custom_packet_capacity;
Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet;
uint32_t group_custom_private_packet_size;
uint32_t group_custom_private_packet_capacity;
Tox_Event_Group_Invite *group_invite;
uint32_t group_invite_size;
uint32_t group_invite_capacity;
Tox_Event_Group_Peer_Join *group_peer_join;
uint32_t group_peer_join_size;
uint32_t group_peer_join_capacity;
Tox_Event_Group_Peer_Exit *group_peer_exit;
uint32_t group_peer_exit_size;
uint32_t group_peer_exit_capacity;
Tox_Event_Group_Self_Join *group_self_join;
uint32_t group_self_join_size;
uint32_t group_self_join_capacity;
Tox_Event_Group_Join_Fail *group_join_fail;
uint32_t group_join_fail_size;
uint32_t group_join_fail_capacity;
Tox_Event_Group_Moderation *group_moderation;
uint32_t group_moderation_size;
uint32_t group_moderation_capacity;
};
typedef struct Tox_Events_State {
Tox_Err_Events_Iterate error;
Tox_Events *events;
} Tox_Events_State;
tox_conference_connected_cb tox_events_handle_conference_connected;
tox_conference_invite_cb tox_events_handle_conference_invite;
tox_conference_message_cb tox_events_handle_conference_message;
tox_conference_peer_list_changed_cb tox_events_handle_conference_peer_list_changed;
tox_conference_peer_name_cb tox_events_handle_conference_peer_name;
tox_conference_title_cb tox_events_handle_conference_title;
tox_file_chunk_request_cb tox_events_handle_file_chunk_request;
tox_file_recv_cb tox_events_handle_file_recv;
tox_file_recv_chunk_cb tox_events_handle_file_recv_chunk;
tox_file_recv_control_cb tox_events_handle_file_recv_control;
tox_friend_connection_status_cb tox_events_handle_friend_connection_status;
tox_friend_lossless_packet_cb tox_events_handle_friend_lossless_packet;
tox_friend_lossy_packet_cb tox_events_handle_friend_lossy_packet;
tox_friend_message_cb tox_events_handle_friend_message;
tox_friend_name_cb tox_events_handle_friend_name;
tox_friend_read_receipt_cb tox_events_handle_friend_read_receipt;
tox_friend_request_cb tox_events_handle_friend_request;
tox_friend_status_cb tox_events_handle_friend_status;
tox_friend_status_message_cb tox_events_handle_friend_status_message;
tox_friend_typing_cb tox_events_handle_friend_typing;
tox_self_connection_status_cb tox_events_handle_self_connection_status;
tox_group_peer_name_cb tox_events_handle_group_peer_name;
tox_group_peer_status_cb tox_events_handle_group_peer_status;
tox_group_topic_cb tox_events_handle_group_topic;
tox_group_privacy_state_cb tox_events_handle_group_privacy_state;
tox_group_voice_state_cb tox_events_handle_group_voice_state;
tox_group_topic_lock_cb tox_events_handle_group_topic_lock;
tox_group_peer_limit_cb tox_events_handle_group_peer_limit;
tox_group_password_cb tox_events_handle_group_password;
tox_group_message_cb tox_events_handle_group_message;
tox_group_private_message_cb tox_events_handle_group_private_message;
tox_group_custom_packet_cb tox_events_handle_group_custom_packet;
tox_group_custom_private_packet_cb tox_events_handle_group_custom_private_packet;
tox_group_invite_cb tox_events_handle_group_invite;
tox_group_peer_join_cb tox_events_handle_group_peer_join;
tox_group_peer_exit_cb tox_events_handle_group_peer_exit;
tox_group_self_join_cb tox_events_handle_group_self_join;
tox_group_join_fail_cb tox_events_handle_group_join_fail;
tox_group_moderation_cb tox_events_handle_group_moderation;
// non_null()
typedef void tox_events_clear_cb(Tox_Events *events);
tox_events_clear_cb tox_events_clear_conference_connected;
tox_events_clear_cb tox_events_clear_conference_invite;
tox_events_clear_cb tox_events_clear_conference_message;
tox_events_clear_cb tox_events_clear_conference_peer_list_changed;
tox_events_clear_cb tox_events_clear_conference_peer_name;
tox_events_clear_cb tox_events_clear_conference_title;
tox_events_clear_cb tox_events_clear_file_chunk_request;
tox_events_clear_cb tox_events_clear_file_recv_chunk;
tox_events_clear_cb tox_events_clear_file_recv_control;
tox_events_clear_cb tox_events_clear_file_recv;
tox_events_clear_cb tox_events_clear_friend_connection_status;
tox_events_clear_cb tox_events_clear_friend_lossless_packet;
tox_events_clear_cb tox_events_clear_friend_lossy_packet;
tox_events_clear_cb tox_events_clear_friend_message;
tox_events_clear_cb tox_events_clear_friend_name;
tox_events_clear_cb tox_events_clear_friend_read_receipt;
tox_events_clear_cb tox_events_clear_friend_request;
tox_events_clear_cb tox_events_clear_friend_status_message;
tox_events_clear_cb tox_events_clear_friend_status;
tox_events_clear_cb tox_events_clear_friend_typing;
tox_events_clear_cb tox_events_clear_self_connection_status;
tox_events_clear_cb tox_events_clear_group_peer_name;
tox_events_clear_cb tox_events_clear_group_peer_status;
tox_events_clear_cb tox_events_clear_group_topic;
tox_events_clear_cb tox_events_clear_group_privacy_state;
tox_events_clear_cb tox_events_clear_group_voice_state;
tox_events_clear_cb tox_events_clear_group_topic_lock;
tox_events_clear_cb tox_events_clear_group_peer_limit;
tox_events_clear_cb tox_events_clear_group_password;
tox_events_clear_cb tox_events_clear_group_message;
tox_events_clear_cb tox_events_clear_group_private_message;
tox_events_clear_cb tox_events_clear_group_custom_packet;
tox_events_clear_cb tox_events_clear_group_custom_private_packet;
tox_events_clear_cb tox_events_clear_group_invite;
tox_events_clear_cb tox_events_clear_group_peer_join;
tox_events_clear_cb tox_events_clear_group_peer_exit;
tox_events_clear_cb tox_events_clear_group_self_join;
tox_events_clear_cb tox_events_clear_group_join_fail;
tox_events_clear_cb tox_events_clear_group_moderation;
// non_null()
typedef bool tox_events_pack_cb(const Tox_Events *events, Bin_Pack *bp);
tox_events_pack_cb tox_events_pack_conference_connected;
tox_events_pack_cb tox_events_pack_conference_invite;
tox_events_pack_cb tox_events_pack_conference_message;
tox_events_pack_cb tox_events_pack_conference_peer_list_changed;
tox_events_pack_cb tox_events_pack_conference_peer_name;
tox_events_pack_cb tox_events_pack_conference_title;
tox_events_pack_cb tox_events_pack_file_chunk_request;
tox_events_pack_cb tox_events_pack_file_recv_chunk;
tox_events_pack_cb tox_events_pack_file_recv_control;
tox_events_pack_cb tox_events_pack_file_recv;
tox_events_pack_cb tox_events_pack_friend_connection_status;
tox_events_pack_cb tox_events_pack_friend_lossless_packet;
tox_events_pack_cb tox_events_pack_friend_lossy_packet;
tox_events_pack_cb tox_events_pack_friend_message;
tox_events_pack_cb tox_events_pack_friend_name;
tox_events_pack_cb tox_events_pack_friend_read_receipt;
tox_events_pack_cb tox_events_pack_friend_request;
tox_events_pack_cb tox_events_pack_friend_status_message;
tox_events_pack_cb tox_events_pack_friend_status;
tox_events_pack_cb tox_events_pack_friend_typing;
tox_events_pack_cb tox_events_pack_self_connection_status;
tox_events_pack_cb tox_events_pack_group_peer_name;
tox_events_pack_cb tox_events_pack_group_peer_status;
tox_events_pack_cb tox_events_pack_group_topic;
tox_events_pack_cb tox_events_pack_group_privacy_state;
tox_events_pack_cb tox_events_pack_group_voice_state;
tox_events_pack_cb tox_events_pack_group_topic_lock;
tox_events_pack_cb tox_events_pack_group_peer_limit;
tox_events_pack_cb tox_events_pack_group_password;
tox_events_pack_cb tox_events_pack_group_message;
tox_events_pack_cb tox_events_pack_group_private_message;
tox_events_pack_cb tox_events_pack_group_custom_packet;
tox_events_pack_cb tox_events_pack_group_custom_private_packet;
tox_events_pack_cb tox_events_pack_group_invite;
tox_events_pack_cb tox_events_pack_group_peer_join;
tox_events_pack_cb tox_events_pack_group_peer_exit;
tox_events_pack_cb tox_events_pack_group_self_join;
tox_events_pack_cb tox_events_pack_group_join_fail;
tox_events_pack_cb tox_events_pack_group_moderation;
tox_events_pack_cb tox_events_pack;
// non_null()
typedef bool tox_events_unpack_cb(Tox_Events *events, Bin_Unpack *bu);
tox_events_unpack_cb tox_events_unpack_conference_connected;
tox_events_unpack_cb tox_events_unpack_conference_invite;
tox_events_unpack_cb tox_events_unpack_conference_message;
tox_events_unpack_cb tox_events_unpack_conference_peer_list_changed;
tox_events_unpack_cb tox_events_unpack_conference_peer_name;
tox_events_unpack_cb tox_events_unpack_conference_title;
tox_events_unpack_cb tox_events_unpack_file_chunk_request;
tox_events_unpack_cb tox_events_unpack_file_recv_chunk;
tox_events_unpack_cb tox_events_unpack_file_recv_control;
tox_events_unpack_cb tox_events_unpack_file_recv;
tox_events_unpack_cb tox_events_unpack_friend_connection_status;
tox_events_unpack_cb tox_events_unpack_friend_lossless_packet;
tox_events_unpack_cb tox_events_unpack_friend_lossy_packet;
tox_events_unpack_cb tox_events_unpack_friend_message;
tox_events_unpack_cb tox_events_unpack_friend_name;
tox_events_unpack_cb tox_events_unpack_friend_read_receipt;
tox_events_unpack_cb tox_events_unpack_friend_request;
tox_events_unpack_cb tox_events_unpack_friend_status_message;
tox_events_unpack_cb tox_events_unpack_friend_status;
tox_events_unpack_cb tox_events_unpack_friend_typing;
tox_events_unpack_cb tox_events_unpack_self_connection_status;
tox_events_unpack_cb tox_events_unpack_group_peer_name;
tox_events_unpack_cb tox_events_unpack_group_peer_status;
tox_events_unpack_cb tox_events_unpack_group_topic;
tox_events_unpack_cb tox_events_unpack_group_privacy_state;
tox_events_unpack_cb tox_events_unpack_group_voice_state;
tox_events_unpack_cb tox_events_unpack_group_topic_lock;
tox_events_unpack_cb tox_events_unpack_group_peer_limit;
tox_events_unpack_cb tox_events_unpack_group_password;
tox_events_unpack_cb tox_events_unpack_group_message;
tox_events_unpack_cb tox_events_unpack_group_private_message;
tox_events_unpack_cb tox_events_unpack_group_custom_packet;
tox_events_unpack_cb tox_events_unpack_group_custom_private_packet;
tox_events_unpack_cb tox_events_unpack_group_invite;
tox_events_unpack_cb tox_events_unpack_group_peer_join;
tox_events_unpack_cb tox_events_unpack_group_peer_exit;
tox_events_unpack_cb tox_events_unpack_group_self_join;
tox_events_unpack_cb tox_events_unpack_group_join_fail;
tox_events_unpack_cb tox_events_unpack_group_moderation;
tox_events_unpack_cb tox_events_unpack;
non_null()
Tox_Events_State *tox_events_alloc(void *user_data);
#ifdef __cplusplus
}
#endif
#endif // C_TOXCORE_TOXCORE_TOX_EVENTS_INTERNAL_H

View File

@ -0,0 +1,243 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_File_Chunk_Request {
uint32_t friend_number;
uint32_t file_number;
uint64_t position;
uint16_t length;
};
non_null()
static void tox_event_file_chunk_request_construct(Tox_Event_File_Chunk_Request *file_chunk_request)
{
*file_chunk_request = (Tox_Event_File_Chunk_Request) {
0
};
}
non_null()
static void tox_event_file_chunk_request_destruct(Tox_Event_File_Chunk_Request *file_chunk_request)
{
return;
}
non_null()
static void tox_event_file_chunk_request_set_friend_number(Tox_Event_File_Chunk_Request *file_chunk_request,
uint32_t friend_number)
{
assert(file_chunk_request != nullptr);
file_chunk_request->friend_number = friend_number;
}
uint32_t tox_event_file_chunk_request_get_friend_number(const Tox_Event_File_Chunk_Request *file_chunk_request)
{
assert(file_chunk_request != nullptr);
return file_chunk_request->friend_number;
}
non_null()
static void tox_event_file_chunk_request_set_file_number(Tox_Event_File_Chunk_Request *file_chunk_request,
uint32_t file_number)
{
assert(file_chunk_request != nullptr);
file_chunk_request->file_number = file_number;
}
uint32_t tox_event_file_chunk_request_get_file_number(const Tox_Event_File_Chunk_Request *file_chunk_request)
{
assert(file_chunk_request != nullptr);
return file_chunk_request->file_number;
}
non_null()
static void tox_event_file_chunk_request_set_position(Tox_Event_File_Chunk_Request *file_chunk_request,
uint64_t position)
{
assert(file_chunk_request != nullptr);
file_chunk_request->position = position;
}
uint64_t tox_event_file_chunk_request_get_position(const Tox_Event_File_Chunk_Request *file_chunk_request)
{
assert(file_chunk_request != nullptr);
return file_chunk_request->position;
}
non_null()
static void tox_event_file_chunk_request_set_length(Tox_Event_File_Chunk_Request *file_chunk_request, uint16_t length)
{
assert(file_chunk_request != nullptr);
file_chunk_request->length = length;
}
uint16_t tox_event_file_chunk_request_get_length(const Tox_Event_File_Chunk_Request *file_chunk_request)
{
assert(file_chunk_request != nullptr);
return file_chunk_request->length;
}
non_null()
static bool tox_event_file_chunk_request_pack(
const Tox_Event_File_Chunk_Request *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FILE_CHUNK_REQUEST)
&& bin_pack_array(bp, 4)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_u32(bp, event->file_number)
&& bin_pack_u64(bp, event->position)
&& bin_pack_u16(bp, event->length);
}
non_null()
static bool tox_event_file_chunk_request_unpack(
Tox_Event_File_Chunk_Request *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 4)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& bin_unpack_u32(bu, &event->file_number)
&& bin_unpack_u64(bu, &event->position)
&& bin_unpack_u16(bu, &event->length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_File_Chunk_Request *tox_events_add_file_chunk_request(Tox_Events *events)
{
if (events->file_chunk_request_size == UINT32_MAX) {
return nullptr;
}
if (events->file_chunk_request_size == events->file_chunk_request_capacity) {
const uint32_t new_file_chunk_request_capacity = events->file_chunk_request_capacity * 2 + 1;
Tox_Event_File_Chunk_Request *new_file_chunk_request = (Tox_Event_File_Chunk_Request *)realloc(
events->file_chunk_request, new_file_chunk_request_capacity * sizeof(Tox_Event_File_Chunk_Request));
if (new_file_chunk_request == nullptr) {
return nullptr;
}
events->file_chunk_request = new_file_chunk_request;
events->file_chunk_request_capacity = new_file_chunk_request_capacity;
}
Tox_Event_File_Chunk_Request *const file_chunk_request = &events->file_chunk_request[events->file_chunk_request_size];
tox_event_file_chunk_request_construct(file_chunk_request);
++events->file_chunk_request_size;
return file_chunk_request;
}
void tox_events_clear_file_chunk_request(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->file_chunk_request_size; ++i) {
tox_event_file_chunk_request_destruct(&events->file_chunk_request[i]);
}
free(events->file_chunk_request);
events->file_chunk_request = nullptr;
events->file_chunk_request_size = 0;
events->file_chunk_request_capacity = 0;
}
uint32_t tox_events_get_file_chunk_request_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->file_chunk_request_size;
}
const Tox_Event_File_Chunk_Request *tox_events_get_file_chunk_request(const Tox_Events *events, uint32_t index)
{
assert(index < events->file_chunk_request_size);
assert(events->file_chunk_request != nullptr);
return &events->file_chunk_request[index];
}
bool tox_events_pack_file_chunk_request(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_file_chunk_request_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_file_chunk_request_pack(tox_events_get_file_chunk_request(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_file_chunk_request(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_File_Chunk_Request *event = tox_events_add_file_chunk_request(events);
if (event == nullptr) {
return false;
}
return tox_event_file_chunk_request_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_file_chunk_request(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position,
size_t length, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_File_Chunk_Request *file_chunk_request = tox_events_add_file_chunk_request(state->events);
if (file_chunk_request == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_file_chunk_request_set_friend_number(file_chunk_request, friend_number);
tox_event_file_chunk_request_set_file_number(file_chunk_request, file_number);
tox_event_file_chunk_request_set_position(file_chunk_request, position);
tox_event_file_chunk_request_set_length(file_chunk_request, length);
}

View File

@ -0,0 +1,282 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_File_Recv {
uint32_t friend_number;
uint32_t file_number;
uint32_t kind;
uint64_t file_size;
uint8_t *filename;
uint32_t filename_length;
};
non_null()
static void tox_event_file_recv_construct(Tox_Event_File_Recv *file_recv)
{
*file_recv = (Tox_Event_File_Recv) {
0
};
}
non_null()
static void tox_event_file_recv_destruct(Tox_Event_File_Recv *file_recv)
{
free(file_recv->filename);
}
non_null()
static void tox_event_file_recv_set_friend_number(Tox_Event_File_Recv *file_recv,
uint32_t friend_number)
{
assert(file_recv != nullptr);
file_recv->friend_number = friend_number;
}
uint32_t tox_event_file_recv_get_friend_number(const Tox_Event_File_Recv *file_recv)
{
assert(file_recv != nullptr);
return file_recv->friend_number;
}
non_null()
static void tox_event_file_recv_set_file_number(Tox_Event_File_Recv *file_recv,
uint32_t file_number)
{
assert(file_recv != nullptr);
file_recv->file_number = file_number;
}
uint32_t tox_event_file_recv_get_file_number(const Tox_Event_File_Recv *file_recv)
{
assert(file_recv != nullptr);
return file_recv->file_number;
}
non_null()
static void tox_event_file_recv_set_kind(Tox_Event_File_Recv *file_recv,
uint32_t kind)
{
assert(file_recv != nullptr);
file_recv->kind = kind;
}
uint32_t tox_event_file_recv_get_kind(const Tox_Event_File_Recv *file_recv)
{
assert(file_recv != nullptr);
return file_recv->kind;
}
non_null()
static void tox_event_file_recv_set_file_size(Tox_Event_File_Recv *file_recv,
uint64_t file_size)
{
assert(file_recv != nullptr);
file_recv->file_size = file_size;
}
uint64_t tox_event_file_recv_get_file_size(const Tox_Event_File_Recv *file_recv)
{
assert(file_recv != nullptr);
return file_recv->file_size;
}
non_null()
static bool tox_event_file_recv_set_filename(Tox_Event_File_Recv *file_recv, const uint8_t *filename,
uint32_t filename_length)
{
assert(file_recv != nullptr);
if (file_recv->filename != nullptr) {
free(file_recv->filename);
file_recv->filename = nullptr;
file_recv->filename_length = 0;
}
file_recv->filename = (uint8_t *)malloc(filename_length);
if (file_recv->filename == nullptr) {
return false;
}
memcpy(file_recv->filename, filename, filename_length);
file_recv->filename_length = filename_length;
return true;
}
uint32_t tox_event_file_recv_get_filename_length(const Tox_Event_File_Recv *file_recv)
{
assert(file_recv != nullptr);
return file_recv->filename_length;
}
const uint8_t *tox_event_file_recv_get_filename(const Tox_Event_File_Recv *file_recv)
{
assert(file_recv != nullptr);
return file_recv->filename;
}
non_null()
static bool tox_event_file_recv_pack(
const Tox_Event_File_Recv *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FILE_RECV)
&& bin_pack_array(bp, 5)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_u32(bp, event->file_number)
&& bin_pack_u32(bp, event->kind)
&& bin_pack_u64(bp, event->file_size)
&& bin_pack_bin(bp, event->filename, event->filename_length);
}
non_null()
static bool tox_event_file_recv_unpack(
Tox_Event_File_Recv *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 5)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& bin_unpack_u32(bu, &event->file_number)
&& bin_unpack_u32(bu, &event->kind)
&& bin_unpack_u64(bu, &event->file_size)
&& bin_unpack_bin(bu, &event->filename, &event->filename_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_File_Recv *tox_events_add_file_recv(Tox_Events *events)
{
if (events->file_recv_size == UINT32_MAX) {
return nullptr;
}
if (events->file_recv_size == events->file_recv_capacity) {
const uint32_t new_file_recv_capacity = events->file_recv_capacity * 2 + 1;
Tox_Event_File_Recv *new_file_recv = (Tox_Event_File_Recv *)realloc(
events->file_recv, new_file_recv_capacity * sizeof(Tox_Event_File_Recv));
if (new_file_recv == nullptr) {
return nullptr;
}
events->file_recv = new_file_recv;
events->file_recv_capacity = new_file_recv_capacity;
}
Tox_Event_File_Recv *const file_recv = &events->file_recv[events->file_recv_size];
tox_event_file_recv_construct(file_recv);
++events->file_recv_size;
return file_recv;
}
void tox_events_clear_file_recv(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->file_recv_size; ++i) {
tox_event_file_recv_destruct(&events->file_recv[i]);
}
free(events->file_recv);
events->file_recv = nullptr;
events->file_recv_size = 0;
events->file_recv_capacity = 0;
}
uint32_t tox_events_get_file_recv_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->file_recv_size;
}
const Tox_Event_File_Recv *tox_events_get_file_recv(const Tox_Events *events, uint32_t index)
{
assert(index < events->file_recv_size);
assert(events->file_recv != nullptr);
return &events->file_recv[index];
}
bool tox_events_pack_file_recv(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_file_recv_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_file_recv_pack(tox_events_get_file_recv(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_file_recv(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_File_Recv *event = tox_events_add_file_recv(events);
if (event == nullptr) {
return false;
}
return tox_event_file_recv_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_file_recv(Tox *tox, uint32_t friend_number, uint32_t file_number, uint32_t kind,
uint64_t file_size, const uint8_t *filename, size_t filename_length, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_File_Recv *file_recv = tox_events_add_file_recv(state->events);
if (file_recv == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_file_recv_set_friend_number(file_recv, friend_number);
tox_event_file_recv_set_file_number(file_recv, file_number);
tox_event_file_recv_set_kind(file_recv, kind);
tox_event_file_recv_set_file_size(file_recv, file_size);
tox_event_file_recv_set_filename(file_recv, filename, filename_length);
}

View File

@ -0,0 +1,265 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_File_Recv_Chunk {
uint32_t friend_number;
uint32_t file_number;
uint64_t position;
uint8_t *data;
uint32_t data_length;
};
non_null()
static void tox_event_file_recv_chunk_construct(Tox_Event_File_Recv_Chunk *file_recv_chunk)
{
*file_recv_chunk = (Tox_Event_File_Recv_Chunk) {
0
};
}
non_null()
static void tox_event_file_recv_chunk_destruct(Tox_Event_File_Recv_Chunk *file_recv_chunk)
{
free(file_recv_chunk->data);
}
non_null()
static void tox_event_file_recv_chunk_set_friend_number(Tox_Event_File_Recv_Chunk *file_recv_chunk,
uint32_t friend_number)
{
assert(file_recv_chunk != nullptr);
file_recv_chunk->friend_number = friend_number;
}
uint32_t tox_event_file_recv_chunk_get_friend_number(const Tox_Event_File_Recv_Chunk *file_recv_chunk)
{
assert(file_recv_chunk != nullptr);
return file_recv_chunk->friend_number;
}
non_null()
static void tox_event_file_recv_chunk_set_file_number(Tox_Event_File_Recv_Chunk *file_recv_chunk,
uint32_t file_number)
{
assert(file_recv_chunk != nullptr);
file_recv_chunk->file_number = file_number;
}
uint32_t tox_event_file_recv_chunk_get_file_number(const Tox_Event_File_Recv_Chunk *file_recv_chunk)
{
assert(file_recv_chunk != nullptr);
return file_recv_chunk->file_number;
}
non_null()
static void tox_event_file_recv_chunk_set_position(Tox_Event_File_Recv_Chunk *file_recv_chunk,
uint64_t position)
{
assert(file_recv_chunk != nullptr);
file_recv_chunk->position = position;
}
uint64_t tox_event_file_recv_chunk_get_position(const Tox_Event_File_Recv_Chunk *file_recv_chunk)
{
assert(file_recv_chunk != nullptr);
return file_recv_chunk->position;
}
non_null()
static bool tox_event_file_recv_chunk_set_data(Tox_Event_File_Recv_Chunk *file_recv_chunk, const uint8_t *data,
uint32_t data_length)
{
assert(file_recv_chunk != nullptr);
if (file_recv_chunk->data != nullptr) {
free(file_recv_chunk->data);
file_recv_chunk->data = nullptr;
file_recv_chunk->data_length = 0;
}
file_recv_chunk->data = (uint8_t *)malloc(data_length);
if (file_recv_chunk->data == nullptr) {
return false;
}
memcpy(file_recv_chunk->data, data, data_length);
file_recv_chunk->data_length = data_length;
return true;
}
uint32_t tox_event_file_recv_chunk_get_length(const Tox_Event_File_Recv_Chunk *file_recv_chunk)
{
assert(file_recv_chunk != nullptr);
return file_recv_chunk->data_length;
}
const uint8_t *tox_event_file_recv_chunk_get_data(const Tox_Event_File_Recv_Chunk *file_recv_chunk)
{
assert(file_recv_chunk != nullptr);
return file_recv_chunk->data;
}
non_null()
static bool tox_event_file_recv_chunk_pack(
const Tox_Event_File_Recv_Chunk *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FILE_RECV_CHUNK)
&& bin_pack_array(bp, 4)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_u32(bp, event->file_number)
&& bin_pack_u64(bp, event->position)
&& bin_pack_bin(bp, event->data, event->data_length);
}
non_null()
static bool tox_event_file_recv_chunk_unpack(
Tox_Event_File_Recv_Chunk *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 4)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& bin_unpack_u32(bu, &event->file_number)
&& bin_unpack_u64(bu, &event->position)
&& bin_unpack_bin(bu, &event->data, &event->data_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_File_Recv_Chunk *tox_events_add_file_recv_chunk(Tox_Events *events)
{
if (events->file_recv_chunk_size == UINT32_MAX) {
return nullptr;
}
if (events->file_recv_chunk_size == events->file_recv_chunk_capacity) {
const uint32_t new_file_recv_chunk_capacity = events->file_recv_chunk_capacity * 2 + 1;
Tox_Event_File_Recv_Chunk *new_file_recv_chunk = (Tox_Event_File_Recv_Chunk *)realloc(
events->file_recv_chunk, new_file_recv_chunk_capacity * sizeof(Tox_Event_File_Recv_Chunk));
if (new_file_recv_chunk == nullptr) {
return nullptr;
}
events->file_recv_chunk = new_file_recv_chunk;
events->file_recv_chunk_capacity = new_file_recv_chunk_capacity;
}
Tox_Event_File_Recv_Chunk *const file_recv_chunk = &events->file_recv_chunk[events->file_recv_chunk_size];
tox_event_file_recv_chunk_construct(file_recv_chunk);
++events->file_recv_chunk_size;
return file_recv_chunk;
}
void tox_events_clear_file_recv_chunk(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->file_recv_chunk_size; ++i) {
tox_event_file_recv_chunk_destruct(&events->file_recv_chunk[i]);
}
free(events->file_recv_chunk);
events->file_recv_chunk = nullptr;
events->file_recv_chunk_size = 0;
events->file_recv_chunk_capacity = 0;
}
uint32_t tox_events_get_file_recv_chunk_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->file_recv_chunk_size;
}
const Tox_Event_File_Recv_Chunk *tox_events_get_file_recv_chunk(const Tox_Events *events, uint32_t index)
{
assert(index < events->file_recv_chunk_size);
assert(events->file_recv_chunk != nullptr);
return &events->file_recv_chunk[index];
}
bool tox_events_pack_file_recv_chunk(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_file_recv_chunk_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_file_recv_chunk_pack(tox_events_get_file_recv_chunk(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_file_recv_chunk(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_File_Recv_Chunk *event = tox_events_add_file_recv_chunk(events);
if (event == nullptr) {
return false;
}
return tox_event_file_recv_chunk_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_file_recv_chunk(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position,
const uint8_t *data, size_t length, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_File_Recv_Chunk *file_recv_chunk = tox_events_add_file_recv_chunk(state->events);
if (file_recv_chunk == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_file_recv_chunk_set_friend_number(file_recv_chunk, friend_number);
tox_event_file_recv_chunk_set_file_number(file_recv_chunk, file_number);
tox_event_file_recv_chunk_set_position(file_recv_chunk, position);
tox_event_file_recv_chunk_set_data(file_recv_chunk, data, length);
}

View File

@ -0,0 +1,228 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_File_Recv_Control {
uint32_t friend_number;
uint32_t file_number;
Tox_File_Control control;
};
non_null()
static void tox_event_file_recv_control_construct(Tox_Event_File_Recv_Control *file_recv_control)
{
*file_recv_control = (Tox_Event_File_Recv_Control) {
0
};
}
non_null()
static void tox_event_file_recv_control_destruct(Tox_Event_File_Recv_Control *file_recv_control)
{
return;
}
non_null()
static void tox_event_file_recv_control_set_friend_number(Tox_Event_File_Recv_Control *file_recv_control,
uint32_t friend_number)
{
assert(file_recv_control != nullptr);
file_recv_control->friend_number = friend_number;
}
uint32_t tox_event_file_recv_control_get_friend_number(const Tox_Event_File_Recv_Control *file_recv_control)
{
assert(file_recv_control != nullptr);
return file_recv_control->friend_number;
}
non_null()
static void tox_event_file_recv_control_set_file_number(Tox_Event_File_Recv_Control *file_recv_control,
uint32_t file_number)
{
assert(file_recv_control != nullptr);
file_recv_control->file_number = file_number;
}
uint32_t tox_event_file_recv_control_get_file_number(const Tox_Event_File_Recv_Control *file_recv_control)
{
assert(file_recv_control != nullptr);
return file_recv_control->file_number;
}
non_null()
static void tox_event_file_recv_control_set_control(Tox_Event_File_Recv_Control *file_recv_control,
Tox_File_Control control)
{
assert(file_recv_control != nullptr);
file_recv_control->control = control;
}
Tox_File_Control tox_event_file_recv_control_get_control(const Tox_Event_File_Recv_Control *file_recv_control)
{
assert(file_recv_control != nullptr);
return file_recv_control->control;
}
non_null()
static bool tox_event_file_recv_control_pack(
const Tox_Event_File_Recv_Control *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FILE_RECV_CONTROL)
&& bin_pack_array(bp, 3)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_u32(bp, event->file_number)
&& bin_pack_u32(bp, event->control);
}
non_null()
static bool tox_event_file_recv_control_unpack(
Tox_Event_File_Recv_Control *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 3)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& bin_unpack_u32(bu, &event->file_number)
&& tox_unpack_file_control(bu, &event->control);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_File_Recv_Control *tox_events_add_file_recv_control(Tox_Events *events)
{
if (events->file_recv_control_size == UINT32_MAX) {
return nullptr;
}
if (events->file_recv_control_size == events->file_recv_control_capacity) {
const uint32_t new_file_recv_control_capacity = events->file_recv_control_capacity * 2 + 1;
Tox_Event_File_Recv_Control *new_file_recv_control = (Tox_Event_File_Recv_Control *)realloc(
events->file_recv_control, new_file_recv_control_capacity * sizeof(Tox_Event_File_Recv_Control));
if (new_file_recv_control == nullptr) {
return nullptr;
}
events->file_recv_control = new_file_recv_control;
events->file_recv_control_capacity = new_file_recv_control_capacity;
}
Tox_Event_File_Recv_Control *const file_recv_control = &events->file_recv_control[events->file_recv_control_size];
tox_event_file_recv_control_construct(file_recv_control);
++events->file_recv_control_size;
return file_recv_control;
}
void tox_events_clear_file_recv_control(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->file_recv_control_size; ++i) {
tox_event_file_recv_control_destruct(&events->file_recv_control[i]);
}
free(events->file_recv_control);
events->file_recv_control = nullptr;
events->file_recv_control_size = 0;
events->file_recv_control_capacity = 0;
}
uint32_t tox_events_get_file_recv_control_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->file_recv_control_size;
}
const Tox_Event_File_Recv_Control *tox_events_get_file_recv_control(const Tox_Events *events, uint32_t index)
{
assert(index < events->file_recv_control_size);
assert(events->file_recv_control != nullptr);
return &events->file_recv_control[index];
}
bool tox_events_pack_file_recv_control(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_file_recv_control_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_file_recv_control_pack(tox_events_get_file_recv_control(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_file_recv_control(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_File_Recv_Control *event = tox_events_add_file_recv_control(events);
if (event == nullptr) {
return false;
}
return tox_event_file_recv_control_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_file_recv_control(Tox *tox, uint32_t friend_number, uint32_t file_number,
Tox_File_Control control, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_File_Recv_Control *file_recv_control = tox_events_add_file_recv_control(state->events);
if (file_recv_control == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_file_recv_control_set_friend_number(file_recv_control, friend_number);
tox_event_file_recv_control_set_file_number(file_recv_control, file_number);
tox_event_file_recv_control_set_control(file_recv_control, control);
}

View File

@ -0,0 +1,215 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Friend_Connection_Status {
uint32_t friend_number;
Tox_Connection connection_status;
};
non_null()
static void tox_event_friend_connection_status_construct(Tox_Event_Friend_Connection_Status *friend_connection_status)
{
*friend_connection_status = (Tox_Event_Friend_Connection_Status) {
0
};
}
non_null()
static void tox_event_friend_connection_status_destruct(Tox_Event_Friend_Connection_Status *friend_connection_status)
{
return;
}
non_null()
static void tox_event_friend_connection_status_set_friend_number(Tox_Event_Friend_Connection_Status
*friend_connection_status, uint32_t friend_number)
{
assert(friend_connection_status != nullptr);
friend_connection_status->friend_number = friend_number;
}
uint32_t tox_event_friend_connection_status_get_friend_number(const Tox_Event_Friend_Connection_Status
*friend_connection_status)
{
assert(friend_connection_status != nullptr);
return friend_connection_status->friend_number;
}
non_null()
static void tox_event_friend_connection_status_set_connection_status(Tox_Event_Friend_Connection_Status
*friend_connection_status, Tox_Connection connection_status)
{
assert(friend_connection_status != nullptr);
friend_connection_status->connection_status = connection_status;
}
Tox_Connection tox_event_friend_connection_status_get_connection_status(const Tox_Event_Friend_Connection_Status
*friend_connection_status)
{
assert(friend_connection_status != nullptr);
return friend_connection_status->connection_status;
}
non_null()
static bool tox_event_friend_connection_status_pack(
const Tox_Event_Friend_Connection_Status *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FRIEND_CONNECTION_STATUS)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_u32(bp, event->connection_status);
}
non_null()
static bool tox_event_friend_connection_status_unpack(
Tox_Event_Friend_Connection_Status *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& tox_unpack_connection(bu, &event->connection_status);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Friend_Connection_Status *tox_events_add_friend_connection_status(Tox_Events *events)
{
if (events->friend_connection_status_size == UINT32_MAX) {
return nullptr;
}
if (events->friend_connection_status_size == events->friend_connection_status_capacity) {
const uint32_t new_friend_connection_status_capacity = events->friend_connection_status_capacity * 2 + 1;
Tox_Event_Friend_Connection_Status *new_friend_connection_status = (Tox_Event_Friend_Connection_Status *)realloc(
events->friend_connection_status, new_friend_connection_status_capacity * sizeof(Tox_Event_Friend_Connection_Status));
if (new_friend_connection_status == nullptr) {
return nullptr;
}
events->friend_connection_status = new_friend_connection_status;
events->friend_connection_status_capacity = new_friend_connection_status_capacity;
}
Tox_Event_Friend_Connection_Status *const friend_connection_status =
&events->friend_connection_status[events->friend_connection_status_size];
tox_event_friend_connection_status_construct(friend_connection_status);
++events->friend_connection_status_size;
return friend_connection_status;
}
void tox_events_clear_friend_connection_status(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->friend_connection_status_size; ++i) {
tox_event_friend_connection_status_destruct(&events->friend_connection_status[i]);
}
free(events->friend_connection_status);
events->friend_connection_status = nullptr;
events->friend_connection_status_size = 0;
events->friend_connection_status_capacity = 0;
}
uint32_t tox_events_get_friend_connection_status_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->friend_connection_status_size;
}
const Tox_Event_Friend_Connection_Status *tox_events_get_friend_connection_status(const Tox_Events *events,
uint32_t index)
{
assert(index < events->friend_connection_status_size);
assert(events->friend_connection_status != nullptr);
return &events->friend_connection_status[index];
}
bool tox_events_pack_friend_connection_status(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_friend_connection_status_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_friend_connection_status_pack(tox_events_get_friend_connection_status(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_friend_connection_status(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Friend_Connection_Status *event = tox_events_add_friend_connection_status(events);
if (event == nullptr) {
return false;
}
return tox_event_friend_connection_status_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_friend_connection_status(Tox *tox, uint32_t friend_number, Tox_Connection connection_status,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Friend_Connection_Status *friend_connection_status = tox_events_add_friend_connection_status(state->events);
if (friend_connection_status == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_friend_connection_status_set_friend_number(friend_connection_status, friend_number);
tox_event_friend_connection_status_set_connection_status(friend_connection_status, connection_status);
}

View File

@ -0,0 +1,233 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Friend_Lossless_Packet {
uint32_t friend_number;
uint8_t *data;
uint32_t data_length;
};
non_null()
static void tox_event_friend_lossless_packet_construct(Tox_Event_Friend_Lossless_Packet *friend_lossless_packet)
{
*friend_lossless_packet = (Tox_Event_Friend_Lossless_Packet) {
0
};
}
non_null()
static void tox_event_friend_lossless_packet_destruct(Tox_Event_Friend_Lossless_Packet *friend_lossless_packet)
{
free(friend_lossless_packet->data);
}
non_null()
static void tox_event_friend_lossless_packet_set_friend_number(Tox_Event_Friend_Lossless_Packet *friend_lossless_packet,
uint32_t friend_number)
{
assert(friend_lossless_packet != nullptr);
friend_lossless_packet->friend_number = friend_number;
}
uint32_t tox_event_friend_lossless_packet_get_friend_number(const Tox_Event_Friend_Lossless_Packet
*friend_lossless_packet)
{
assert(friend_lossless_packet != nullptr);
return friend_lossless_packet->friend_number;
}
non_null()
static bool tox_event_friend_lossless_packet_set_data(Tox_Event_Friend_Lossless_Packet *friend_lossless_packet,
const uint8_t *data, uint32_t data_length)
{
assert(friend_lossless_packet != nullptr);
if (friend_lossless_packet->data != nullptr) {
free(friend_lossless_packet->data);
friend_lossless_packet->data = nullptr;
friend_lossless_packet->data_length = 0;
}
friend_lossless_packet->data = (uint8_t *)malloc(data_length);
if (friend_lossless_packet->data == nullptr) {
return false;
}
memcpy(friend_lossless_packet->data, data, data_length);
friend_lossless_packet->data_length = data_length;
return true;
}
uint32_t tox_event_friend_lossless_packet_get_data_length(const Tox_Event_Friend_Lossless_Packet *friend_lossless_packet)
{
assert(friend_lossless_packet != nullptr);
return friend_lossless_packet->data_length;
}
const uint8_t *tox_event_friend_lossless_packet_get_data(const Tox_Event_Friend_Lossless_Packet *friend_lossless_packet)
{
assert(friend_lossless_packet != nullptr);
return friend_lossless_packet->data;
}
non_null()
static bool tox_event_friend_lossless_packet_pack(
const Tox_Event_Friend_Lossless_Packet *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FRIEND_LOSSLESS_PACKET)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_bin(bp, event->data, event->data_length);
}
non_null()
static bool tox_event_friend_lossless_packet_unpack(
Tox_Event_Friend_Lossless_Packet *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& bin_unpack_bin(bu, &event->data, &event->data_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Friend_Lossless_Packet *tox_events_add_friend_lossless_packet(Tox_Events *events)
{
if (events->friend_lossless_packet_size == UINT32_MAX) {
return nullptr;
}
if (events->friend_lossless_packet_size == events->friend_lossless_packet_capacity) {
const uint32_t new_friend_lossless_packet_capacity = events->friend_lossless_packet_capacity * 2 + 1;
Tox_Event_Friend_Lossless_Packet *new_friend_lossless_packet = (Tox_Event_Friend_Lossless_Packet *)realloc(
events->friend_lossless_packet, new_friend_lossless_packet_capacity * sizeof(Tox_Event_Friend_Lossless_Packet));
if (new_friend_lossless_packet == nullptr) {
return nullptr;
}
events->friend_lossless_packet = new_friend_lossless_packet;
events->friend_lossless_packet_capacity = new_friend_lossless_packet_capacity;
}
Tox_Event_Friend_Lossless_Packet *const friend_lossless_packet =
&events->friend_lossless_packet[events->friend_lossless_packet_size];
tox_event_friend_lossless_packet_construct(friend_lossless_packet);
++events->friend_lossless_packet_size;
return friend_lossless_packet;
}
void tox_events_clear_friend_lossless_packet(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->friend_lossless_packet_size; ++i) {
tox_event_friend_lossless_packet_destruct(&events->friend_lossless_packet[i]);
}
free(events->friend_lossless_packet);
events->friend_lossless_packet = nullptr;
events->friend_lossless_packet_size = 0;
events->friend_lossless_packet_capacity = 0;
}
uint32_t tox_events_get_friend_lossless_packet_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->friend_lossless_packet_size;
}
const Tox_Event_Friend_Lossless_Packet *tox_events_get_friend_lossless_packet(const Tox_Events *events, uint32_t index)
{
assert(index < events->friend_lossless_packet_size);
assert(events->friend_lossless_packet != nullptr);
return &events->friend_lossless_packet[index];
}
bool tox_events_pack_friend_lossless_packet(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_friend_lossless_packet_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_friend_lossless_packet_pack(tox_events_get_friend_lossless_packet(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_friend_lossless_packet(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Friend_Lossless_Packet *event = tox_events_add_friend_lossless_packet(events);
if (event == nullptr) {
return false;
}
return tox_event_friend_lossless_packet_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_friend_lossless_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Friend_Lossless_Packet *friend_lossless_packet = tox_events_add_friend_lossless_packet(state->events);
if (friend_lossless_packet == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_friend_lossless_packet_set_friend_number(friend_lossless_packet, friend_number);
tox_event_friend_lossless_packet_set_data(friend_lossless_packet, data, length);
}

View File

@ -0,0 +1,232 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Friend_Lossy_Packet {
uint32_t friend_number;
uint8_t *data;
uint32_t data_length;
};
non_null()
static void tox_event_friend_lossy_packet_construct(Tox_Event_Friend_Lossy_Packet *friend_lossy_packet)
{
*friend_lossy_packet = (Tox_Event_Friend_Lossy_Packet) {
0
};
}
non_null()
static void tox_event_friend_lossy_packet_destruct(Tox_Event_Friend_Lossy_Packet *friend_lossy_packet)
{
free(friend_lossy_packet->data);
}
non_null()
static void tox_event_friend_lossy_packet_set_friend_number(Tox_Event_Friend_Lossy_Packet *friend_lossy_packet,
uint32_t friend_number)
{
assert(friend_lossy_packet != nullptr);
friend_lossy_packet->friend_number = friend_number;
}
uint32_t tox_event_friend_lossy_packet_get_friend_number(const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet)
{
assert(friend_lossy_packet != nullptr);
return friend_lossy_packet->friend_number;
}
non_null()
static bool tox_event_friend_lossy_packet_set_data(Tox_Event_Friend_Lossy_Packet *friend_lossy_packet,
const uint8_t *data, uint32_t data_length)
{
assert(friend_lossy_packet != nullptr);
if (friend_lossy_packet->data != nullptr) {
free(friend_lossy_packet->data);
friend_lossy_packet->data = nullptr;
friend_lossy_packet->data_length = 0;
}
friend_lossy_packet->data = (uint8_t *)malloc(data_length);
if (friend_lossy_packet->data == nullptr) {
return false;
}
memcpy(friend_lossy_packet->data, data, data_length);
friend_lossy_packet->data_length = data_length;
return true;
}
uint32_t tox_event_friend_lossy_packet_get_data_length(const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet)
{
assert(friend_lossy_packet != nullptr);
return friend_lossy_packet->data_length;
}
const uint8_t *tox_event_friend_lossy_packet_get_data(const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet)
{
assert(friend_lossy_packet != nullptr);
return friend_lossy_packet->data;
}
non_null()
static bool tox_event_friend_lossy_packet_pack(
const Tox_Event_Friend_Lossy_Packet *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FRIEND_LOSSY_PACKET)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_bin(bp, event->data, event->data_length);
}
non_null()
static bool tox_event_friend_lossy_packet_unpack(
Tox_Event_Friend_Lossy_Packet *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& bin_unpack_bin(bu, &event->data, &event->data_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Friend_Lossy_Packet *tox_events_add_friend_lossy_packet(Tox_Events *events)
{
if (events->friend_lossy_packet_size == UINT32_MAX) {
return nullptr;
}
if (events->friend_lossy_packet_size == events->friend_lossy_packet_capacity) {
const uint32_t new_friend_lossy_packet_capacity = events->friend_lossy_packet_capacity * 2 + 1;
Tox_Event_Friend_Lossy_Packet *new_friend_lossy_packet = (Tox_Event_Friend_Lossy_Packet *)realloc(
events->friend_lossy_packet, new_friend_lossy_packet_capacity * sizeof(Tox_Event_Friend_Lossy_Packet));
if (new_friend_lossy_packet == nullptr) {
return nullptr;
}
events->friend_lossy_packet = new_friend_lossy_packet;
events->friend_lossy_packet_capacity = new_friend_lossy_packet_capacity;
}
Tox_Event_Friend_Lossy_Packet *const friend_lossy_packet =
&events->friend_lossy_packet[events->friend_lossy_packet_size];
tox_event_friend_lossy_packet_construct(friend_lossy_packet);
++events->friend_lossy_packet_size;
return friend_lossy_packet;
}
void tox_events_clear_friend_lossy_packet(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->friend_lossy_packet_size; ++i) {
tox_event_friend_lossy_packet_destruct(&events->friend_lossy_packet[i]);
}
free(events->friend_lossy_packet);
events->friend_lossy_packet = nullptr;
events->friend_lossy_packet_size = 0;
events->friend_lossy_packet_capacity = 0;
}
uint32_t tox_events_get_friend_lossy_packet_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->friend_lossy_packet_size;
}
const Tox_Event_Friend_Lossy_Packet *tox_events_get_friend_lossy_packet(const Tox_Events *events, uint32_t index)
{
assert(index < events->friend_lossy_packet_size);
assert(events->friend_lossy_packet != nullptr);
return &events->friend_lossy_packet[index];
}
bool tox_events_pack_friend_lossy_packet(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_friend_lossy_packet_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_friend_lossy_packet_pack(tox_events_get_friend_lossy_packet(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_friend_lossy_packet(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Friend_Lossy_Packet *event = tox_events_add_friend_lossy_packet(events);
if (event == nullptr) {
return false;
}
return tox_event_friend_lossy_packet_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_friend_lossy_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Friend_Lossy_Packet *friend_lossy_packet = tox_events_add_friend_lossy_packet(state->events);
if (friend_lossy_packet == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_friend_lossy_packet_set_friend_number(friend_lossy_packet, friend_number);
tox_event_friend_lossy_packet_set_data(friend_lossy_packet, data, length);
}

View File

@ -0,0 +1,248 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Friend_Message {
uint32_t friend_number;
Tox_Message_Type type;
uint8_t *message;
uint32_t message_length;
};
non_null()
static void tox_event_friend_message_construct(Tox_Event_Friend_Message *friend_message)
{
*friend_message = (Tox_Event_Friend_Message) {
0
};
}
non_null()
static void tox_event_friend_message_destruct(Tox_Event_Friend_Message *friend_message)
{
free(friend_message->message);
}
non_null()
static void tox_event_friend_message_set_friend_number(Tox_Event_Friend_Message *friend_message,
uint32_t friend_number)
{
assert(friend_message != nullptr);
friend_message->friend_number = friend_number;
}
uint32_t tox_event_friend_message_get_friend_number(const Tox_Event_Friend_Message *friend_message)
{
assert(friend_message != nullptr);
return friend_message->friend_number;
}
non_null()
static void tox_event_friend_message_set_type(Tox_Event_Friend_Message *friend_message, Tox_Message_Type type)
{
assert(friend_message != nullptr);
friend_message->type = type;
}
Tox_Message_Type tox_event_friend_message_get_type(const Tox_Event_Friend_Message *friend_message)
{
assert(friend_message != nullptr);
return friend_message->type;
}
non_null()
static bool tox_event_friend_message_set_message(Tox_Event_Friend_Message *friend_message, const uint8_t *message,
uint32_t message_length)
{
assert(friend_message != nullptr);
if (friend_message->message != nullptr) {
free(friend_message->message);
friend_message->message = nullptr;
friend_message->message_length = 0;
}
friend_message->message = (uint8_t *)malloc(message_length);
if (friend_message->message == nullptr) {
return false;
}
memcpy(friend_message->message, message, message_length);
friend_message->message_length = message_length;
return true;
}
uint32_t tox_event_friend_message_get_message_length(const Tox_Event_Friend_Message *friend_message)
{
assert(friend_message != nullptr);
return friend_message->message_length;
}
const uint8_t *tox_event_friend_message_get_message(const Tox_Event_Friend_Message *friend_message)
{
assert(friend_message != nullptr);
return friend_message->message;
}
non_null()
static bool tox_event_friend_message_pack(
const Tox_Event_Friend_Message *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FRIEND_MESSAGE)
&& bin_pack_array(bp, 3)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_u32(bp, event->type)
&& bin_pack_bin(bp, event->message, event->message_length);
}
non_null()
static bool tox_event_friend_message_unpack(
Tox_Event_Friend_Message *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 3)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& tox_unpack_message_type(bu, &event->type)
&& bin_unpack_bin(bu, &event->message, &event->message_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Friend_Message *tox_events_add_friend_message(Tox_Events *events)
{
if (events->friend_message_size == UINT32_MAX) {
return nullptr;
}
if (events->friend_message_size == events->friend_message_capacity) {
const uint32_t new_friend_message_capacity = events->friend_message_capacity * 2 + 1;
Tox_Event_Friend_Message *new_friend_message = (Tox_Event_Friend_Message *)realloc(
events->friend_message, new_friend_message_capacity * sizeof(Tox_Event_Friend_Message));
if (new_friend_message == nullptr) {
return nullptr;
}
events->friend_message = new_friend_message;
events->friend_message_capacity = new_friend_message_capacity;
}
Tox_Event_Friend_Message *const friend_message = &events->friend_message[events->friend_message_size];
tox_event_friend_message_construct(friend_message);
++events->friend_message_size;
return friend_message;
}
void tox_events_clear_friend_message(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->friend_message_size; ++i) {
tox_event_friend_message_destruct(&events->friend_message[i]);
}
free(events->friend_message);
events->friend_message = nullptr;
events->friend_message_size = 0;
events->friend_message_capacity = 0;
}
uint32_t tox_events_get_friend_message_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->friend_message_size;
}
const Tox_Event_Friend_Message *tox_events_get_friend_message(const Tox_Events *events, uint32_t index)
{
assert(index < events->friend_message_size);
assert(events->friend_message != nullptr);
return &events->friend_message[index];
}
bool tox_events_pack_friend_message(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_friend_message_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_friend_message_pack(tox_events_get_friend_message(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_friend_message(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Friend_Message *event = tox_events_add_friend_message(events);
if (event == nullptr) {
return false;
}
return tox_event_friend_message_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_friend_message(Tox *tox, uint32_t friend_number, Tox_Message_Type type, const uint8_t *message,
size_t length, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Friend_Message *friend_message = tox_events_add_friend_message(state->events);
if (friend_message == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_friend_message_set_friend_number(friend_message, friend_number);
tox_event_friend_message_set_type(friend_message, type);
tox_event_friend_message_set_message(friend_message, message, length);
}

View File

@ -0,0 +1,231 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Friend_Name {
uint32_t friend_number;
uint8_t *name;
uint32_t name_length;
};
non_null()
static void tox_event_friend_name_construct(Tox_Event_Friend_Name *friend_name)
{
*friend_name = (Tox_Event_Friend_Name) {
0
};
}
non_null()
static void tox_event_friend_name_destruct(Tox_Event_Friend_Name *friend_name)
{
free(friend_name->name);
}
non_null()
static void tox_event_friend_name_set_friend_number(Tox_Event_Friend_Name *friend_name,
uint32_t friend_number)
{
assert(friend_name != nullptr);
friend_name->friend_number = friend_number;
}
uint32_t tox_event_friend_name_get_friend_number(const Tox_Event_Friend_Name *friend_name)
{
assert(friend_name != nullptr);
return friend_name->friend_number;
}
non_null()
static bool tox_event_friend_name_set_name(Tox_Event_Friend_Name *friend_name, const uint8_t *name,
uint32_t name_length)
{
assert(friend_name != nullptr);
if (friend_name->name != nullptr) {
free(friend_name->name);
friend_name->name = nullptr;
friend_name->name_length = 0;
}
friend_name->name = (uint8_t *)malloc(name_length);
if (friend_name->name == nullptr) {
return false;
}
memcpy(friend_name->name, name, name_length);
friend_name->name_length = name_length;
return true;
}
uint32_t tox_event_friend_name_get_name_length(const Tox_Event_Friend_Name *friend_name)
{
assert(friend_name != nullptr);
return friend_name->name_length;
}
const uint8_t *tox_event_friend_name_get_name(const Tox_Event_Friend_Name *friend_name)
{
assert(friend_name != nullptr);
return friend_name->name;
}
non_null()
static bool tox_event_friend_name_pack(
const Tox_Event_Friend_Name *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FRIEND_NAME)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_bin(bp, event->name, event->name_length);
}
non_null()
static bool tox_event_friend_name_unpack(
Tox_Event_Friend_Name *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& bin_unpack_bin(bu, &event->name, &event->name_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Friend_Name *tox_events_add_friend_name(Tox_Events *events)
{
if (events->friend_name_size == UINT32_MAX) {
return nullptr;
}
if (events->friend_name_size == events->friend_name_capacity) {
const uint32_t new_friend_name_capacity = events->friend_name_capacity * 2 + 1;
Tox_Event_Friend_Name *new_friend_name = (Tox_Event_Friend_Name *)realloc(
events->friend_name, new_friend_name_capacity * sizeof(Tox_Event_Friend_Name));
if (new_friend_name == nullptr) {
return nullptr;
}
events->friend_name = new_friend_name;
events->friend_name_capacity = new_friend_name_capacity;
}
Tox_Event_Friend_Name *const friend_name = &events->friend_name[events->friend_name_size];
tox_event_friend_name_construct(friend_name);
++events->friend_name_size;
return friend_name;
}
void tox_events_clear_friend_name(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->friend_name_size; ++i) {
tox_event_friend_name_destruct(&events->friend_name[i]);
}
free(events->friend_name);
events->friend_name = nullptr;
events->friend_name_size = 0;
events->friend_name_capacity = 0;
}
uint32_t tox_events_get_friend_name_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->friend_name_size;
}
const Tox_Event_Friend_Name *tox_events_get_friend_name(const Tox_Events *events, uint32_t index)
{
assert(index < events->friend_name_size);
assert(events->friend_name != nullptr);
return &events->friend_name[index];
}
bool tox_events_pack_friend_name(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_friend_name_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_friend_name_pack(tox_events_get_friend_name(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_friend_name(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Friend_Name *event = tox_events_add_friend_name(events);
if (event == nullptr) {
return false;
}
return tox_event_friend_name_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_friend_name(Tox *tox, uint32_t friend_number, const uint8_t *name, size_t length,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Friend_Name *friend_name = tox_events_add_friend_name(state->events);
if (friend_name == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_friend_name_set_friend_number(friend_name, friend_number);
tox_event_friend_name_set_name(friend_name, name, length);
}

View File

@ -0,0 +1,210 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Friend_Read_Receipt {
uint32_t friend_number;
uint32_t message_id;
};
non_null()
static void tox_event_friend_read_receipt_construct(Tox_Event_Friend_Read_Receipt *friend_read_receipt)
{
*friend_read_receipt = (Tox_Event_Friend_Read_Receipt) {
0
};
}
non_null()
static void tox_event_friend_read_receipt_destruct(Tox_Event_Friend_Read_Receipt *friend_read_receipt)
{
return;
}
non_null()
static void tox_event_friend_read_receipt_set_friend_number(Tox_Event_Friend_Read_Receipt *friend_read_receipt,
uint32_t friend_number)
{
assert(friend_read_receipt != nullptr);
friend_read_receipt->friend_number = friend_number;
}
uint32_t tox_event_friend_read_receipt_get_friend_number(const Tox_Event_Friend_Read_Receipt *friend_read_receipt)
{
assert(friend_read_receipt != nullptr);
return friend_read_receipt->friend_number;
}
non_null()
static void tox_event_friend_read_receipt_set_message_id(Tox_Event_Friend_Read_Receipt *friend_read_receipt,
uint32_t message_id)
{
assert(friend_read_receipt != nullptr);
friend_read_receipt->message_id = message_id;
}
uint32_t tox_event_friend_read_receipt_get_message_id(const Tox_Event_Friend_Read_Receipt *friend_read_receipt)
{
assert(friend_read_receipt != nullptr);
return friend_read_receipt->message_id;
}
non_null()
static bool tox_event_friend_read_receipt_pack(
const Tox_Event_Friend_Read_Receipt *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FRIEND_READ_RECEIPT)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_u32(bp, event->message_id);
}
non_null()
static bool tox_event_friend_read_receipt_unpack(
Tox_Event_Friend_Read_Receipt *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& bin_unpack_u32(bu, &event->message_id);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Friend_Read_Receipt *tox_events_add_friend_read_receipt(Tox_Events *events)
{
if (events->friend_read_receipt_size == UINT32_MAX) {
return nullptr;
}
if (events->friend_read_receipt_size == events->friend_read_receipt_capacity) {
const uint32_t new_friend_read_receipt_capacity = events->friend_read_receipt_capacity * 2 + 1;
Tox_Event_Friend_Read_Receipt *new_friend_read_receipt = (Tox_Event_Friend_Read_Receipt *)realloc(
events->friend_read_receipt, new_friend_read_receipt_capacity * sizeof(Tox_Event_Friend_Read_Receipt));
if (new_friend_read_receipt == nullptr) {
return nullptr;
}
events->friend_read_receipt = new_friend_read_receipt;
events->friend_read_receipt_capacity = new_friend_read_receipt_capacity;
}
Tox_Event_Friend_Read_Receipt *const friend_read_receipt =
&events->friend_read_receipt[events->friend_read_receipt_size];
tox_event_friend_read_receipt_construct(friend_read_receipt);
++events->friend_read_receipt_size;
return friend_read_receipt;
}
void tox_events_clear_friend_read_receipt(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->friend_read_receipt_size; ++i) {
tox_event_friend_read_receipt_destruct(&events->friend_read_receipt[i]);
}
free(events->friend_read_receipt);
events->friend_read_receipt = nullptr;
events->friend_read_receipt_size = 0;
events->friend_read_receipt_capacity = 0;
}
uint32_t tox_events_get_friend_read_receipt_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->friend_read_receipt_size;
}
const Tox_Event_Friend_Read_Receipt *tox_events_get_friend_read_receipt(const Tox_Events *events, uint32_t index)
{
assert(index < events->friend_read_receipt_size);
assert(events->friend_read_receipt != nullptr);
return &events->friend_read_receipt[index];
}
bool tox_events_pack_friend_read_receipt(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_friend_read_receipt_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_friend_read_receipt_pack(tox_events_get_friend_read_receipt(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_friend_read_receipt(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Friend_Read_Receipt *event = tox_events_add_friend_read_receipt(events);
if (event == nullptr) {
return false;
}
return tox_event_friend_read_receipt_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_friend_read_receipt(Tox *tox, uint32_t friend_number, uint32_t message_id, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Friend_Read_Receipt *friend_read_receipt = tox_events_add_friend_read_receipt(state->events);
if (friend_read_receipt == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_friend_read_receipt_set_friend_number(friend_read_receipt, friend_number);
tox_event_friend_read_receipt_set_message_id(friend_read_receipt, message_id);
}

View File

@ -0,0 +1,232 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Friend_Request {
uint8_t public_key[TOX_PUBLIC_KEY_SIZE];
uint8_t *message;
uint32_t message_length;
};
non_null()
static void tox_event_friend_request_construct(Tox_Event_Friend_Request *friend_request)
{
*friend_request = (Tox_Event_Friend_Request) {
0
};
}
non_null()
static void tox_event_friend_request_destruct(Tox_Event_Friend_Request *friend_request)
{
free(friend_request->message);
}
non_null()
static bool tox_event_friend_request_set_public_key(Tox_Event_Friend_Request *friend_request, const uint8_t *public_key)
{
assert(friend_request != nullptr);
memcpy(friend_request->public_key, public_key, TOX_PUBLIC_KEY_SIZE);
return true;
}
const uint8_t *tox_event_friend_request_get_public_key(const Tox_Event_Friend_Request *friend_request)
{
assert(friend_request != nullptr);
return friend_request->public_key;
}
non_null()
static bool tox_event_friend_request_set_message(Tox_Event_Friend_Request *friend_request, const uint8_t *message,
uint32_t message_length)
{
assert(friend_request != nullptr);
if (friend_request->message != nullptr) {
free(friend_request->message);
friend_request->message = nullptr;
friend_request->message_length = 0;
}
friend_request->message = (uint8_t *)malloc(message_length);
if (friend_request->message == nullptr) {
return false;
}
memcpy(friend_request->message, message, message_length);
friend_request->message_length = message_length;
return true;
}
uint32_t tox_event_friend_request_get_message_length(const Tox_Event_Friend_Request *friend_request)
{
assert(friend_request != nullptr);
return friend_request->message_length;
}
const uint8_t *tox_event_friend_request_get_message(const Tox_Event_Friend_Request *friend_request)
{
assert(friend_request != nullptr);
return friend_request->message;
}
non_null()
static bool tox_event_friend_request_pack(
const Tox_Event_Friend_Request *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FRIEND_REQUEST)
&& bin_pack_array(bp, 2)
&& bin_pack_bin(bp, event->public_key, TOX_PUBLIC_KEY_SIZE)
&& bin_pack_bin(bp, event->message, event->message_length);
}
non_null()
static bool tox_event_friend_request_unpack(
Tox_Event_Friend_Request *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_bin_fixed(bu, event->public_key, TOX_PUBLIC_KEY_SIZE)
&& bin_unpack_bin(bu, &event->message, &event->message_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Friend_Request *tox_events_add_friend_request(Tox_Events *events)
{
if (events->friend_request_size == UINT32_MAX) {
return nullptr;
}
if (events->friend_request_size == events->friend_request_capacity) {
const uint32_t new_friend_request_capacity = events->friend_request_capacity * 2 + 1;
Tox_Event_Friend_Request *new_friend_request = (Tox_Event_Friend_Request *)realloc(
events->friend_request, new_friend_request_capacity * sizeof(Tox_Event_Friend_Request));
if (new_friend_request == nullptr) {
return nullptr;
}
events->friend_request = new_friend_request;
events->friend_request_capacity = new_friend_request_capacity;
}
Tox_Event_Friend_Request *const friend_request = &events->friend_request[events->friend_request_size];
tox_event_friend_request_construct(friend_request);
++events->friend_request_size;
return friend_request;
}
void tox_events_clear_friend_request(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->friend_request_size; ++i) {
tox_event_friend_request_destruct(&events->friend_request[i]);
}
free(events->friend_request);
events->friend_request = nullptr;
events->friend_request_size = 0;
events->friend_request_capacity = 0;
}
uint32_t tox_events_get_friend_request_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->friend_request_size;
}
const Tox_Event_Friend_Request *tox_events_get_friend_request(const Tox_Events *events, uint32_t index)
{
assert(index < events->friend_request_size);
assert(events->friend_request != nullptr);
return &events->friend_request[index];
}
bool tox_events_pack_friend_request(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_friend_request_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_friend_request_pack(tox_events_get_friend_request(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_friend_request(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Friend_Request *event = tox_events_add_friend_request(events);
if (event == nullptr) {
return false;
}
return tox_event_friend_request_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_friend_request(Tox *tox, const uint8_t *public_key, const uint8_t *message, size_t length,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Friend_Request *friend_request = tox_events_add_friend_request(state->events);
if (friend_request == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_friend_request_set_public_key(friend_request, public_key);
tox_event_friend_request_set_message(friend_request, message, length);
}

View File

@ -0,0 +1,211 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Friend_Status {
uint32_t friend_number;
Tox_User_Status status;
};
non_null()
static void tox_event_friend_status_construct(Tox_Event_Friend_Status *friend_status)
{
*friend_status = (Tox_Event_Friend_Status) {
0
};
}
non_null()
static void tox_event_friend_status_destruct(Tox_Event_Friend_Status *friend_status)
{
return;
}
non_null()
static void tox_event_friend_status_set_friend_number(Tox_Event_Friend_Status *friend_status,
uint32_t friend_number)
{
assert(friend_status != nullptr);
friend_status->friend_number = friend_number;
}
uint32_t tox_event_friend_status_get_friend_number(const Tox_Event_Friend_Status *friend_status)
{
assert(friend_status != nullptr);
return friend_status->friend_number;
}
non_null()
static void tox_event_friend_status_set_status(Tox_Event_Friend_Status *friend_status,
Tox_User_Status status)
{
assert(friend_status != nullptr);
friend_status->status = status;
}
Tox_User_Status tox_event_friend_status_get_status(const Tox_Event_Friend_Status *friend_status)
{
assert(friend_status != nullptr);
return friend_status->status;
}
non_null()
static bool tox_event_friend_status_pack(
const Tox_Event_Friend_Status *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FRIEND_STATUS)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_u32(bp, event->status);
}
non_null()
static bool tox_event_friend_status_unpack(
Tox_Event_Friend_Status *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& tox_unpack_user_status(bu, &event->status);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Friend_Status *tox_events_add_friend_status(Tox_Events *events)
{
if (events->friend_status_size == UINT32_MAX) {
return nullptr;
}
if (events->friend_status_size == events->friend_status_capacity) {
const uint32_t new_friend_status_capacity = events->friend_status_capacity * 2 + 1;
Tox_Event_Friend_Status *new_friend_status = (Tox_Event_Friend_Status *)realloc(
events->friend_status, new_friend_status_capacity * sizeof(Tox_Event_Friend_Status));
if (new_friend_status == nullptr) {
return nullptr;
}
events->friend_status = new_friend_status;
events->friend_status_capacity = new_friend_status_capacity;
}
Tox_Event_Friend_Status *const friend_status = &events->friend_status[events->friend_status_size];
tox_event_friend_status_construct(friend_status);
++events->friend_status_size;
return friend_status;
}
void tox_events_clear_friend_status(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->friend_status_size; ++i) {
tox_event_friend_status_destruct(&events->friend_status[i]);
}
free(events->friend_status);
events->friend_status = nullptr;
events->friend_status_size = 0;
events->friend_status_capacity = 0;
}
uint32_t tox_events_get_friend_status_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->friend_status_size;
}
const Tox_Event_Friend_Status *tox_events_get_friend_status(const Tox_Events *events, uint32_t index)
{
assert(index < events->friend_status_size);
assert(events->friend_status != nullptr);
return &events->friend_status[index];
}
bool tox_events_pack_friend_status(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_friend_status_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_friend_status_pack(tox_events_get_friend_status(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_friend_status(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Friend_Status *event = tox_events_add_friend_status(events);
if (event == nullptr) {
return false;
}
return tox_event_friend_status_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_friend_status(Tox *tox, uint32_t friend_number, Tox_User_Status status,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Friend_Status *friend_status = tox_events_add_friend_status(state->events);
if (friend_status == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_friend_status_set_friend_number(friend_status, friend_number);
tox_event_friend_status_set_status(friend_status, status);
}

View File

@ -0,0 +1,234 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Friend_Status_Message {
uint32_t friend_number;
uint8_t *message;
uint32_t message_length;
};
non_null()
static void tox_event_friend_status_message_construct(Tox_Event_Friend_Status_Message *friend_status_message)
{
*friend_status_message = (Tox_Event_Friend_Status_Message) {
0
};
}
non_null()
static void tox_event_friend_status_message_destruct(Tox_Event_Friend_Status_Message *friend_status_message)
{
free(friend_status_message->message);
}
non_null()
static void tox_event_friend_status_message_set_friend_number(Tox_Event_Friend_Status_Message *friend_status_message,
uint32_t friend_number)
{
assert(friend_status_message != nullptr);
friend_status_message->friend_number = friend_number;
}
uint32_t tox_event_friend_status_message_get_friend_number(const Tox_Event_Friend_Status_Message *friend_status_message)
{
assert(friend_status_message != nullptr);
return friend_status_message->friend_number;
}
non_null()
static bool tox_event_friend_status_message_set_message(Tox_Event_Friend_Status_Message *friend_status_message,
const uint8_t *message, uint32_t message_length)
{
assert(friend_status_message != nullptr);
if (friend_status_message->message != nullptr) {
free(friend_status_message->message);
friend_status_message->message = nullptr;
friend_status_message->message_length = 0;
}
friend_status_message->message = (uint8_t *)malloc(message_length);
if (friend_status_message->message == nullptr) {
return false;
}
memcpy(friend_status_message->message, message, message_length);
friend_status_message->message_length = message_length;
return true;
}
uint32_t tox_event_friend_status_message_get_message_length(const Tox_Event_Friend_Status_Message
*friend_status_message)
{
assert(friend_status_message != nullptr);
return friend_status_message->message_length;
}
const uint8_t *tox_event_friend_status_message_get_message(const Tox_Event_Friend_Status_Message
*friend_status_message)
{
assert(friend_status_message != nullptr);
return friend_status_message->message;
}
non_null()
static bool tox_event_friend_status_message_pack(
const Tox_Event_Friend_Status_Message *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FRIEND_STATUS_MESSAGE)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_bin(bp, event->message, event->message_length);
}
non_null()
static bool tox_event_friend_status_message_unpack(
Tox_Event_Friend_Status_Message *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& bin_unpack_bin(bu, &event->message, &event->message_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Friend_Status_Message *tox_events_add_friend_status_message(Tox_Events *events)
{
if (events->friend_status_message_size == UINT32_MAX) {
return nullptr;
}
if (events->friend_status_message_size == events->friend_status_message_capacity) {
const uint32_t new_friend_status_message_capacity = events->friend_status_message_capacity * 2 + 1;
Tox_Event_Friend_Status_Message *new_friend_status_message = (Tox_Event_Friend_Status_Message *)realloc(
events->friend_status_message, new_friend_status_message_capacity * sizeof(Tox_Event_Friend_Status_Message));
if (new_friend_status_message == nullptr) {
return nullptr;
}
events->friend_status_message = new_friend_status_message;
events->friend_status_message_capacity = new_friend_status_message_capacity;
}
Tox_Event_Friend_Status_Message *const friend_status_message =
&events->friend_status_message[events->friend_status_message_size];
tox_event_friend_status_message_construct(friend_status_message);
++events->friend_status_message_size;
return friend_status_message;
}
void tox_events_clear_friend_status_message(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->friend_status_message_size; ++i) {
tox_event_friend_status_message_destruct(&events->friend_status_message[i]);
}
free(events->friend_status_message);
events->friend_status_message = nullptr;
events->friend_status_message_size = 0;
events->friend_status_message_capacity = 0;
}
uint32_t tox_events_get_friend_status_message_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->friend_status_message_size;
}
const Tox_Event_Friend_Status_Message *tox_events_get_friend_status_message(const Tox_Events *events, uint32_t index)
{
assert(index < events->friend_status_message_size);
assert(events->friend_status_message != nullptr);
return &events->friend_status_message[index];
}
bool tox_events_pack_friend_status_message(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_friend_status_message_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_friend_status_message_pack(tox_events_get_friend_status_message(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_friend_status_message(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Friend_Status_Message *event = tox_events_add_friend_status_message(events);
if (event == nullptr) {
return false;
}
return tox_event_friend_status_message_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_friend_status_message(Tox *tox, uint32_t friend_number, const uint8_t *message,
size_t length, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Friend_Status_Message *friend_status_message = tox_events_add_friend_status_message(state->events);
if (friend_status_message == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_friend_status_message_set_friend_number(friend_status_message, friend_number);
tox_event_friend_status_message_set_message(friend_status_message, message, length);
}

View File

@ -0,0 +1,208 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Friend_Typing {
uint32_t friend_number;
bool typing;
};
non_null()
static void tox_event_friend_typing_construct(Tox_Event_Friend_Typing *friend_typing)
{
*friend_typing = (Tox_Event_Friend_Typing) {
0
};
}
non_null()
static void tox_event_friend_typing_destruct(Tox_Event_Friend_Typing *friend_typing)
{
return;
}
non_null()
static void tox_event_friend_typing_set_friend_number(Tox_Event_Friend_Typing *friend_typing,
uint32_t friend_number)
{
assert(friend_typing != nullptr);
friend_typing->friend_number = friend_number;
}
uint32_t tox_event_friend_typing_get_friend_number(const Tox_Event_Friend_Typing *friend_typing)
{
assert(friend_typing != nullptr);
return friend_typing->friend_number;
}
non_null()
static void tox_event_friend_typing_set_typing(Tox_Event_Friend_Typing *friend_typing, bool typing)
{
assert(friend_typing != nullptr);
friend_typing->typing = typing;
}
bool tox_event_friend_typing_get_typing(const Tox_Event_Friend_Typing *friend_typing)
{
assert(friend_typing != nullptr);
return friend_typing->typing;
}
non_null()
static bool tox_event_friend_typing_pack(
const Tox_Event_Friend_Typing *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_FRIEND_TYPING)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_bool(bp, event->typing);
}
non_null()
static bool tox_event_friend_typing_unpack(
Tox_Event_Friend_Typing *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& bin_unpack_bool(bu, &event->typing);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Friend_Typing *tox_events_add_friend_typing(Tox_Events *events)
{
if (events->friend_typing_size == UINT32_MAX) {
return nullptr;
}
if (events->friend_typing_size == events->friend_typing_capacity) {
const uint32_t new_friend_typing_capacity = events->friend_typing_capacity * 2 + 1;
Tox_Event_Friend_Typing *new_friend_typing = (Tox_Event_Friend_Typing *)realloc(
events->friend_typing, new_friend_typing_capacity * sizeof(Tox_Event_Friend_Typing));
if (new_friend_typing == nullptr) {
return nullptr;
}
events->friend_typing = new_friend_typing;
events->friend_typing_capacity = new_friend_typing_capacity;
}
Tox_Event_Friend_Typing *const friend_typing = &events->friend_typing[events->friend_typing_size];
tox_event_friend_typing_construct(friend_typing);
++events->friend_typing_size;
return friend_typing;
}
void tox_events_clear_friend_typing(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->friend_typing_size; ++i) {
tox_event_friend_typing_destruct(&events->friend_typing[i]);
}
free(events->friend_typing);
events->friend_typing = nullptr;
events->friend_typing_size = 0;
events->friend_typing_capacity = 0;
}
uint32_t tox_events_get_friend_typing_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->friend_typing_size;
}
const Tox_Event_Friend_Typing *tox_events_get_friend_typing(const Tox_Events *events, uint32_t index)
{
assert(index < events->friend_typing_size);
assert(events->friend_typing != nullptr);
return &events->friend_typing[index];
}
bool tox_events_pack_friend_typing(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_friend_typing_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_friend_typing_pack(tox_events_get_friend_typing(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_friend_typing(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Friend_Typing *event = tox_events_add_friend_typing(events);
if (event == nullptr) {
return false;
}
return tox_event_friend_typing_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_friend_typing(Tox *tox, uint32_t friend_number, bool typing, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Friend_Typing *friend_typing = tox_events_add_friend_typing(state->events);
if (friend_typing == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_friend_typing_set_friend_number(friend_typing, friend_number);
tox_event_friend_typing_set_typing(friend_typing, typing);
}

View File

@ -0,0 +1,252 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Custom_Packet {
uint32_t group_number;
uint32_t peer_id;
uint8_t *data;
uint32_t data_length;
};
non_null()
static void tox_event_group_custom_packet_construct(Tox_Event_Group_Custom_Packet *group_custom_packet)
{
*group_custom_packet = (Tox_Event_Group_Custom_Packet) {
0
};
}
non_null()
static void tox_event_group_custom_packet_destruct(Tox_Event_Group_Custom_Packet *group_custom_packet)
{
free(group_custom_packet->data);
}
non_null()
static void tox_event_group_custom_packet_set_group_number(Tox_Event_Group_Custom_Packet *group_custom_packet,
uint32_t group_number)
{
assert(group_custom_packet != nullptr);
group_custom_packet->group_number = group_number;
}
uint32_t tox_event_group_custom_packet_get_group_number(const Tox_Event_Group_Custom_Packet *group_custom_packet)
{
assert(group_custom_packet != nullptr);
return group_custom_packet->group_number;
}
non_null()
static void tox_event_group_custom_packet_set_peer_id(Tox_Event_Group_Custom_Packet *group_custom_packet,
uint32_t peer_id)
{
assert(group_custom_packet != nullptr);
group_custom_packet->peer_id = peer_id;
}
uint32_t tox_event_group_custom_packet_get_peer_id(const Tox_Event_Group_Custom_Packet *group_custom_packet)
{
assert(group_custom_packet != nullptr);
return group_custom_packet->peer_id;
}
non_null()
static bool tox_event_group_custom_packet_set_data(Tox_Event_Group_Custom_Packet *group_custom_packet,
const uint8_t *data, uint32_t data_length)
{
assert(group_custom_packet != nullptr);
if (group_custom_packet->data != nullptr) {
free(group_custom_packet->data);
group_custom_packet->data = nullptr;
group_custom_packet->data_length = 0;
}
group_custom_packet->data = (uint8_t *)malloc(data_length);
if (group_custom_packet->data == nullptr) {
return false;
}
memcpy(group_custom_packet->data, data, data_length);
group_custom_packet->data_length = data_length;
return true;
}
size_t tox_event_group_custom_packet_get_data_length(const Tox_Event_Group_Custom_Packet *group_custom_packet)
{
assert(group_custom_packet != nullptr);
return group_custom_packet->data_length;
}
const uint8_t *tox_event_group_custom_packet_get_data(const Tox_Event_Group_Custom_Packet *group_custom_packet)
{
assert(group_custom_packet != nullptr);
return group_custom_packet->data;
}
non_null()
static bool tox_event_group_custom_packet_pack(
const Tox_Event_Group_Custom_Packet *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_CUSTOM_PACKET)
&& bin_pack_array(bp, 3)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->peer_id)
&& bin_pack_bin(bp, event->data, event->data_length);
}
non_null()
static bool tox_event_group_custom_packet_unpack(
Tox_Event_Group_Custom_Packet *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 3)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& bin_unpack_u32(bu, &event->peer_id)
&& bin_unpack_bin(bu, &event->data, &event->data_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Custom_Packet *tox_events_add_group_custom_packet(Tox_Events *events)
{
if (events->group_custom_packet_size == UINT32_MAX) {
return nullptr;
}
if (events->group_custom_packet_size == events->group_custom_packet_capacity) {
const uint32_t new_group_custom_packet_capacity = events->group_custom_packet_capacity * 2 + 1;
Tox_Event_Group_Custom_Packet *new_group_custom_packet = (Tox_Event_Group_Custom_Packet *)
realloc(
events->group_custom_packet,
new_group_custom_packet_capacity * sizeof(Tox_Event_Group_Custom_Packet));
if (new_group_custom_packet == nullptr) {
return nullptr;
}
events->group_custom_packet = new_group_custom_packet;
events->group_custom_packet_capacity = new_group_custom_packet_capacity;
}
Tox_Event_Group_Custom_Packet *const group_custom_packet =
&events->group_custom_packet[events->group_custom_packet_size];
tox_event_group_custom_packet_construct(group_custom_packet);
++events->group_custom_packet_size;
return group_custom_packet;
}
void tox_events_clear_group_custom_packet(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_custom_packet_size; ++i) {
tox_event_group_custom_packet_destruct(&events->group_custom_packet[i]);
}
free(events->group_custom_packet);
events->group_custom_packet = nullptr;
events->group_custom_packet_size = 0;
events->group_custom_packet_capacity = 0;
}
uint32_t tox_events_get_group_custom_packet_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_custom_packet_size;
}
const Tox_Event_Group_Custom_Packet *tox_events_get_group_custom_packet(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_custom_packet_size);
assert(events->group_custom_packet != nullptr);
return &events->group_custom_packet[index];
}
bool tox_events_pack_group_custom_packet(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_custom_packet_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_custom_packet_pack(tox_events_get_group_custom_packet(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_custom_packet(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Custom_Packet *event = tox_events_add_group_custom_packet(events);
if (event == nullptr) {
return false;
}
return tox_event_group_custom_packet_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_custom_packet(Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *data, size_t length,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Custom_Packet *group_custom_packet = tox_events_add_group_custom_packet(state->events);
if (group_custom_packet == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_custom_packet_set_group_number(group_custom_packet, group_number);
tox_event_group_custom_packet_set_peer_id(group_custom_packet, peer_id);
tox_event_group_custom_packet_set_data(group_custom_packet, data, length);
}

View File

@ -0,0 +1,252 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Custom_Private_Packet {
uint32_t group_number;
uint32_t peer_id;
uint8_t *data;
uint32_t data_length;
};
non_null()
static void tox_event_group_custom_private_packet_construct(Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet)
{
*group_custom_private_packet = (Tox_Event_Group_Custom_Private_Packet) {
0
};
}
non_null()
static void tox_event_group_custom_private_packet_destruct(Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet)
{
free(group_custom_private_packet->data);
}
non_null()
static void tox_event_group_custom_private_packet_set_group_number(Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet,
uint32_t group_number)
{
assert(group_custom_private_packet != nullptr);
group_custom_private_packet->group_number = group_number;
}
uint32_t tox_event_group_custom_private_packet_get_group_number(const Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet)
{
assert(group_custom_private_packet != nullptr);
return group_custom_private_packet->group_number;
}
non_null()
static void tox_event_group_custom_private_packet_set_peer_id(Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet,
uint32_t peer_id)
{
assert(group_custom_private_packet != nullptr);
group_custom_private_packet->peer_id = peer_id;
}
uint32_t tox_event_group_custom_private_packet_get_peer_id(const Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet)
{
assert(group_custom_private_packet != nullptr);
return group_custom_private_packet->peer_id;
}
non_null()
static bool tox_event_group_custom_private_packet_set_data(Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet,
const uint8_t *data, uint32_t data_length)
{
assert(group_custom_private_packet != nullptr);
if (group_custom_private_packet->data != nullptr) {
free(group_custom_private_packet->data);
group_custom_private_packet->data = nullptr;
group_custom_private_packet->data_length = 0;
}
group_custom_private_packet->data = (uint8_t *)malloc(data_length);
if (group_custom_private_packet->data == nullptr) {
return false;
}
memcpy(group_custom_private_packet->data, data, data_length);
group_custom_private_packet->data_length = data_length;
return true;
}
size_t tox_event_group_custom_private_packet_get_data_length(const Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet)
{
assert(group_custom_private_packet != nullptr);
return group_custom_private_packet->data_length;
}
const uint8_t *tox_event_group_custom_private_packet_get_data(const Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet)
{
assert(group_custom_private_packet != nullptr);
return group_custom_private_packet->data;
}
non_null()
static bool tox_event_group_custom_private_packet_pack(
const Tox_Event_Group_Custom_Private_Packet *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_CUSTOM_PRIVATE_PACKET)
&& bin_pack_array(bp, 3)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->peer_id)
&& bin_pack_bin(bp, event->data, event->data_length);
}
non_null()
static bool tox_event_group_custom_private_packet_unpack(
Tox_Event_Group_Custom_Private_Packet *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 3)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& bin_unpack_u32(bu, &event->peer_id)
&& bin_unpack_bin(bu, &event->data, &event->data_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Custom_Private_Packet *tox_events_add_group_custom_private_packet(Tox_Events *events)
{
if (events->group_custom_private_packet_size == UINT32_MAX) {
return nullptr;
}
if (events->group_custom_private_packet_size == events->group_custom_private_packet_capacity) {
const uint32_t new_group_custom_private_packet_capacity = events->group_custom_private_packet_capacity * 2 + 1;
Tox_Event_Group_Custom_Private_Packet *new_group_custom_private_packet = (Tox_Event_Group_Custom_Private_Packet *)
realloc(
events->group_custom_private_packet,
new_group_custom_private_packet_capacity * sizeof(Tox_Event_Group_Custom_Private_Packet));
if (new_group_custom_private_packet == nullptr) {
return nullptr;
}
events->group_custom_private_packet = new_group_custom_private_packet;
events->group_custom_private_packet_capacity = new_group_custom_private_packet_capacity;
}
Tox_Event_Group_Custom_Private_Packet *const group_custom_private_packet =
&events->group_custom_private_packet[events->group_custom_private_packet_size];
tox_event_group_custom_private_packet_construct(group_custom_private_packet);
++events->group_custom_private_packet_size;
return group_custom_private_packet;
}
void tox_events_clear_group_custom_private_packet(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_custom_private_packet_size; ++i) {
tox_event_group_custom_private_packet_destruct(&events->group_custom_private_packet[i]);
}
free(events->group_custom_private_packet);
events->group_custom_private_packet = nullptr;
events->group_custom_private_packet_size = 0;
events->group_custom_private_packet_capacity = 0;
}
uint32_t tox_events_get_group_custom_private_packet_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_custom_private_packet_size;
}
const Tox_Event_Group_Custom_Private_Packet *tox_events_get_group_custom_private_packet(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_custom_private_packet_size);
assert(events->group_custom_private_packet != nullptr);
return &events->group_custom_private_packet[index];
}
bool tox_events_pack_group_custom_private_packet(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_custom_private_packet_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_custom_private_packet_pack(tox_events_get_group_custom_private_packet(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_custom_private_packet(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Custom_Private_Packet *event = tox_events_add_group_custom_private_packet(events);
if (event == nullptr) {
return false;
}
return tox_event_group_custom_private_packet_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_custom_private_packet(Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *data, size_t length,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet = tox_events_add_group_custom_private_packet(state->events);
if (group_custom_private_packet == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_custom_private_packet_set_group_number(group_custom_private_packet, group_number);
tox_event_group_custom_private_packet_set_peer_id(group_custom_private_packet, peer_id);
tox_event_group_custom_private_packet_set_data(group_custom_private_packet, data, length);
}

View File

@ -0,0 +1,274 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Invite {
uint32_t friend_number;
uint8_t *invite_data;
uint32_t invite_data_length;
uint8_t *group_name;
uint32_t group_name_length;
};
non_null()
static void tox_event_group_invite_construct(Tox_Event_Group_Invite *group_invite)
{
*group_invite = (Tox_Event_Group_Invite) {
0
};
}
non_null()
static void tox_event_group_invite_destruct(Tox_Event_Group_Invite *group_invite)
{
free(group_invite->invite_data);
free(group_invite->group_name);
}
non_null()
static void tox_event_group_invite_set_friend_number(Tox_Event_Group_Invite *group_invite,
uint32_t friend_number)
{
assert(group_invite != nullptr);
group_invite->friend_number = friend_number;
}
uint32_t tox_event_group_invite_get_friend_number(const Tox_Event_Group_Invite *group_invite)
{
assert(group_invite != nullptr);
return group_invite->friend_number;
}
non_null()
static bool tox_event_group_invite_set_invite_data(Tox_Event_Group_Invite *group_invite,
const uint8_t *invite_data, uint32_t invite_data_length)
{
assert(group_invite != nullptr);
if (group_invite->invite_data != nullptr) {
free(group_invite->invite_data);
group_invite->invite_data = nullptr;
group_invite->invite_data_length = 0;
}
group_invite->invite_data = (uint8_t *)malloc(invite_data_length);
if (group_invite->invite_data == nullptr) {
return false;
}
memcpy(group_invite->invite_data, invite_data, invite_data_length);
group_invite->invite_data_length = invite_data_length;
return true;
}
size_t tox_event_group_invite_get_invite_data_length(const Tox_Event_Group_Invite *group_invite)
{
assert(group_invite != nullptr);
return group_invite->invite_data_length;
}
const uint8_t *tox_event_group_invite_get_invite_data(const Tox_Event_Group_Invite *group_invite)
{
assert(group_invite != nullptr);
return group_invite->invite_data;
}
non_null()
static bool tox_event_group_invite_set_group_name(Tox_Event_Group_Invite *group_invite,
const uint8_t *group_name, uint32_t group_name_length)
{
assert(group_invite != nullptr);
if (group_invite->group_name != nullptr) {
free(group_invite->group_name);
group_invite->group_name = nullptr;
group_invite->group_name_length = 0;
}
group_invite->group_name = (uint8_t *)malloc(group_name_length);
if (group_invite->group_name == nullptr) {
return false;
}
memcpy(group_invite->group_name, group_name, group_name_length);
group_invite->group_name_length = group_name_length;
return true;
}
size_t tox_event_group_invite_get_group_name_length(const Tox_Event_Group_Invite *group_invite)
{
assert(group_invite != nullptr);
return group_invite->group_name_length;
}
const uint8_t *tox_event_group_invite_get_group_name(const Tox_Event_Group_Invite *group_invite)
{
assert(group_invite != nullptr);
return group_invite->group_name;
}
non_null()
static bool tox_event_group_invite_pack(
const Tox_Event_Group_Invite *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_INVITE)
&& bin_pack_array(bp, 3)
&& bin_pack_u32(bp, event->friend_number)
&& bin_pack_bin(bp, event->invite_data, event->invite_data_length)
&& bin_pack_bin(bp, event->group_name, event->group_name_length);
}
non_null()
static bool tox_event_group_invite_unpack(
Tox_Event_Group_Invite *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 3)) {
return false;
}
return bin_unpack_u32(bu, &event->friend_number)
&& bin_unpack_bin(bu, &event->invite_data, &event->invite_data_length)
&& bin_unpack_bin(bu, &event->group_name, &event->group_name_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Invite *tox_events_add_group_invite(Tox_Events *events)
{
if (events->group_invite_size == UINT32_MAX) {
return nullptr;
}
if (events->group_invite_size == events->group_invite_capacity) {
const uint32_t new_group_invite_capacity = events->group_invite_capacity * 2 + 1;
Tox_Event_Group_Invite *new_group_invite = (Tox_Event_Group_Invite *)
realloc(
events->group_invite,
new_group_invite_capacity * sizeof(Tox_Event_Group_Invite));
if (new_group_invite == nullptr) {
return nullptr;
}
events->group_invite = new_group_invite;
events->group_invite_capacity = new_group_invite_capacity;
}
Tox_Event_Group_Invite *const group_invite =
&events->group_invite[events->group_invite_size];
tox_event_group_invite_construct(group_invite);
++events->group_invite_size;
return group_invite;
}
void tox_events_clear_group_invite(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_invite_size; ++i) {
tox_event_group_invite_destruct(&events->group_invite[i]);
}
free(events->group_invite);
events->group_invite = nullptr;
events->group_invite_size = 0;
events->group_invite_capacity = 0;
}
uint32_t tox_events_get_group_invite_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_invite_size;
}
const Tox_Event_Group_Invite *tox_events_get_group_invite(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_invite_size);
assert(events->group_invite != nullptr);
return &events->group_invite[index];
}
bool tox_events_pack_group_invite(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_invite_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_invite_pack(tox_events_get_group_invite(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_invite(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Invite *event = tox_events_add_group_invite(events);
if (event == nullptr) {
return false;
}
return tox_event_group_invite_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_invite(Tox *tox, uint32_t friend_number, const uint8_t *invite_data, size_t length, const uint8_t *group_name, size_t group_name_length,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Invite *group_invite = tox_events_add_group_invite(state->events);
if (group_invite == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_invite_set_friend_number(group_invite, friend_number);
tox_event_group_invite_set_invite_data(group_invite, invite_data, length);
tox_event_group_invite_set_group_name(group_invite, group_name, group_name_length);
}

View File

@ -0,0 +1,214 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Join_Fail {
uint32_t group_number;
Tox_Group_Join_Fail fail_type;
};
non_null()
static void tox_event_group_join_fail_construct(Tox_Event_Group_Join_Fail *group_join_fail)
{
*group_join_fail = (Tox_Event_Group_Join_Fail) {
0
};
}
non_null()
static void tox_event_group_join_fail_destruct(Tox_Event_Group_Join_Fail *group_join_fail)
{
return;
}
non_null()
static void tox_event_group_join_fail_set_group_number(Tox_Event_Group_Join_Fail *group_join_fail,
uint32_t group_number)
{
assert(group_join_fail != nullptr);
group_join_fail->group_number = group_number;
}
uint32_t tox_event_group_join_fail_get_group_number(const Tox_Event_Group_Join_Fail *group_join_fail)
{
assert(group_join_fail != nullptr);
return group_join_fail->group_number;
}
non_null()
static void tox_event_group_join_fail_set_fail_type(Tox_Event_Group_Join_Fail *group_join_fail,
Tox_Group_Join_Fail fail_type)
{
assert(group_join_fail != nullptr);
group_join_fail->fail_type = fail_type;
}
Tox_Group_Join_Fail tox_event_group_join_fail_get_fail_type(const Tox_Event_Group_Join_Fail *group_join_fail)
{
assert(group_join_fail != nullptr);
return group_join_fail->fail_type;
}
non_null()
static bool tox_event_group_join_fail_pack(
const Tox_Event_Group_Join_Fail *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_JOIN_FAIL)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->fail_type);
}
non_null()
static bool tox_event_group_join_fail_unpack(
Tox_Event_Group_Join_Fail *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& tox_unpack_group_join_fail(bu, &event->fail_type);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Join_Fail *tox_events_add_group_join_fail(Tox_Events *events)
{
if (events->group_join_fail_size == UINT32_MAX) {
return nullptr;
}
if (events->group_join_fail_size == events->group_join_fail_capacity) {
const uint32_t new_group_join_fail_capacity = events->group_join_fail_capacity * 2 + 1;
Tox_Event_Group_Join_Fail *new_group_join_fail = (Tox_Event_Group_Join_Fail *)
realloc(
events->group_join_fail,
new_group_join_fail_capacity * sizeof(Tox_Event_Group_Join_Fail));
if (new_group_join_fail == nullptr) {
return nullptr;
}
events->group_join_fail = new_group_join_fail;
events->group_join_fail_capacity = new_group_join_fail_capacity;
}
Tox_Event_Group_Join_Fail *const group_join_fail =
&events->group_join_fail[events->group_join_fail_size];
tox_event_group_join_fail_construct(group_join_fail);
++events->group_join_fail_size;
return group_join_fail;
}
void tox_events_clear_group_join_fail(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_join_fail_size; ++i) {
tox_event_group_join_fail_destruct(&events->group_join_fail[i]);
}
free(events->group_join_fail);
events->group_join_fail = nullptr;
events->group_join_fail_size = 0;
events->group_join_fail_capacity = 0;
}
uint32_t tox_events_get_group_join_fail_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_join_fail_size;
}
const Tox_Event_Group_Join_Fail *tox_events_get_group_join_fail(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_join_fail_size);
assert(events->group_join_fail != nullptr);
return &events->group_join_fail[index];
}
bool tox_events_pack_group_join_fail(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_join_fail_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_join_fail_pack(tox_events_get_group_join_fail(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_join_fail(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Join_Fail *event = tox_events_add_group_join_fail(events);
if (event == nullptr) {
return false;
}
return tox_event_group_join_fail_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_join_fail(Tox *tox, uint32_t group_number, Tox_Group_Join_Fail fail_type,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Join_Fail *group_join_fail = tox_events_add_group_join_fail(state->events);
if (group_join_fail == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_join_fail_set_group_number(group_join_fail, group_number);
tox_event_group_join_fail_set_fail_type(group_join_fail, fail_type);
}

View File

@ -0,0 +1,286 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Message {
uint32_t group_number;
uint32_t peer_id;
Tox_Message_Type type;
uint8_t *message;
uint32_t message_length;
uint32_t message_id;
};
non_null()
static void tox_event_group_message_construct(Tox_Event_Group_Message *group_message)
{
*group_message = (Tox_Event_Group_Message) {
0
};
}
non_null()
static void tox_event_group_message_destruct(Tox_Event_Group_Message *group_message)
{
free(group_message->message);
}
non_null()
static void tox_event_group_message_set_group_number(Tox_Event_Group_Message *group_message,
uint32_t group_number)
{
assert(group_message != nullptr);
group_message->group_number = group_number;
}
uint32_t tox_event_group_message_get_group_number(const Tox_Event_Group_Message *group_message)
{
assert(group_message != nullptr);
return group_message->group_number;
}
non_null()
static void tox_event_group_message_set_peer_id(Tox_Event_Group_Message *group_message,
uint32_t peer_id)
{
assert(group_message != nullptr);
group_message->peer_id = peer_id;
}
uint32_t tox_event_group_message_get_peer_id(const Tox_Event_Group_Message *group_message)
{
assert(group_message != nullptr);
return group_message->peer_id;
}
non_null()
static void tox_event_group_message_set_type(Tox_Event_Group_Message *group_message,
Tox_Message_Type type)
{
assert(group_message != nullptr);
group_message->type = type;
}
Tox_Message_Type tox_event_group_message_get_type(const Tox_Event_Group_Message *group_message)
{
assert(group_message != nullptr);
return group_message->type;
}
non_null()
static bool tox_event_group_message_set_message(Tox_Event_Group_Message *group_message,
const uint8_t *message, uint32_t message_length)
{
assert(group_message != nullptr);
if (group_message->message != nullptr) {
free(group_message->message);
group_message->message = nullptr;
group_message->message_length = 0;
}
group_message->message = (uint8_t *)malloc(message_length);
if (group_message->message == nullptr) {
return false;
}
memcpy(group_message->message, message, message_length);
group_message->message_length = message_length;
return true;
}
size_t tox_event_group_message_get_message_length(const Tox_Event_Group_Message *group_message)
{
assert(group_message != nullptr);
return group_message->message_length;
}
const uint8_t *tox_event_group_message_get_message(const Tox_Event_Group_Message *group_message)
{
assert(group_message != nullptr);
return group_message->message;
}
non_null()
static void tox_event_group_message_set_message_id(Tox_Event_Group_Message *group_message,
uint32_t message_id)
{
assert(group_message != nullptr);
group_message->message_id = message_id;
}
uint32_t tox_event_group_message_get_message_id(const Tox_Event_Group_Message *group_message)
{
assert(group_message != nullptr);
return group_message->message_id;
}
non_null()
static bool tox_event_group_message_pack(
const Tox_Event_Group_Message *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_MESSAGE)
&& bin_pack_array(bp, 5)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->peer_id)
&& bin_pack_u32(bp, event->type)
&& bin_pack_bin(bp, event->message, event->message_length)
&& bin_pack_u32(bp, event->message_id);
}
non_null()
static bool tox_event_group_message_unpack(
Tox_Event_Group_Message *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 5)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& bin_unpack_u32(bu, &event->peer_id)
&& tox_unpack_message_type(bu, &event->type)
&& bin_unpack_bin(bu, &event->message, &event->message_length)
&& bin_unpack_u32(bu, &event->message_id);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Message *tox_events_add_group_message(Tox_Events *events)
{
if (events->group_message_size == UINT32_MAX) {
return nullptr;
}
if (events->group_message_size == events->group_message_capacity) {
const uint32_t new_group_message_capacity = events->group_message_capacity * 2 + 1;
Tox_Event_Group_Message *new_group_message = (Tox_Event_Group_Message *)
realloc(
events->group_message,
new_group_message_capacity * sizeof(Tox_Event_Group_Message));
if (new_group_message == nullptr) {
return nullptr;
}
events->group_message = new_group_message;
events->group_message_capacity = new_group_message_capacity;
}
Tox_Event_Group_Message *const group_message =
&events->group_message[events->group_message_size];
tox_event_group_message_construct(group_message);
++events->group_message_size;
return group_message;
}
void tox_events_clear_group_message(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_message_size; ++i) {
tox_event_group_message_destruct(&events->group_message[i]);
}
free(events->group_message);
events->group_message = nullptr;
events->group_message_size = 0;
events->group_message_capacity = 0;
}
uint32_t tox_events_get_group_message_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_message_size;
}
const Tox_Event_Group_Message *tox_events_get_group_message(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_message_size);
assert(events->group_message != nullptr);
return &events->group_message[index];
}
bool tox_events_pack_group_message(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_message_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_message_pack(tox_events_get_group_message(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_message(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Message *event = tox_events_add_group_message(events);
if (event == nullptr) {
return false;
}
return tox_event_group_message_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_message(Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Message_Type type, const uint8_t *message, size_t length, uint32_t message_id,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Message *group_message = tox_events_add_group_message(state->events);
if (group_message == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_message_set_group_number(group_message, group_number);
tox_event_group_message_set_peer_id(group_message, peer_id);
tox_event_group_message_set_type(group_message, type);
tox_event_group_message_set_message(group_message, message, length);
tox_event_group_message_set_message_id(group_message, message_id);
}

View File

@ -0,0 +1,248 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Moderation {
uint32_t group_number;
uint32_t source_peer_id;
uint32_t target_peer_id;
Tox_Group_Mod_Event mod_type;
};
non_null()
static void tox_event_group_moderation_construct(Tox_Event_Group_Moderation *group_moderation)
{
*group_moderation = (Tox_Event_Group_Moderation) {
0
};
}
non_null()
static void tox_event_group_moderation_destruct(Tox_Event_Group_Moderation *group_moderation)
{
return;
}
non_null()
static void tox_event_group_moderation_set_group_number(Tox_Event_Group_Moderation *group_moderation,
uint32_t group_number)
{
assert(group_moderation != nullptr);
group_moderation->group_number = group_number;
}
uint32_t tox_event_group_moderation_get_group_number(const Tox_Event_Group_Moderation *group_moderation)
{
assert(group_moderation != nullptr);
return group_moderation->group_number;
}
non_null()
static void tox_event_group_moderation_set_source_peer_id(Tox_Event_Group_Moderation *group_moderation,
uint32_t source_peer_id)
{
assert(group_moderation != nullptr);
group_moderation->source_peer_id = source_peer_id;
}
uint32_t tox_event_group_moderation_get_source_peer_id(const Tox_Event_Group_Moderation *group_moderation)
{
assert(group_moderation != nullptr);
return group_moderation->source_peer_id;
}
non_null()
static void tox_event_group_moderation_set_target_peer_id(Tox_Event_Group_Moderation *group_moderation,
uint32_t target_peer_id)
{
assert(group_moderation != nullptr);
group_moderation->target_peer_id = target_peer_id;
}
uint32_t tox_event_group_moderation_get_target_peer_id(const Tox_Event_Group_Moderation *group_moderation)
{
assert(group_moderation != nullptr);
return group_moderation->target_peer_id;
}
non_null()
static void tox_event_group_moderation_set_mod_type(Tox_Event_Group_Moderation *group_moderation,
Tox_Group_Mod_Event mod_type)
{
assert(group_moderation != nullptr);
group_moderation->mod_type = mod_type;
}
Tox_Group_Mod_Event tox_event_group_moderation_get_mod_type(const Tox_Event_Group_Moderation *group_moderation)
{
assert(group_moderation != nullptr);
return group_moderation->mod_type;
}
non_null()
static bool tox_event_group_moderation_pack(
const Tox_Event_Group_Moderation *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_MODERATION)
&& bin_pack_array(bp, 4)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->source_peer_id)
&& bin_pack_u32(bp, event->target_peer_id)
&& bin_pack_u32(bp, event->mod_type);
}
non_null()
static bool tox_event_group_moderation_unpack(
Tox_Event_Group_Moderation *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 4)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& bin_unpack_u32(bu, &event->source_peer_id)
&& bin_unpack_u32(bu, &event->target_peer_id)
&& tox_unpack_group_mod_event(bu, &event->mod_type);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Moderation *tox_events_add_group_moderation(Tox_Events *events)
{
if (events->group_moderation_size == UINT32_MAX) {
return nullptr;
}
if (events->group_moderation_size == events->group_moderation_capacity) {
const uint32_t new_group_moderation_capacity = events->group_moderation_capacity * 2 + 1;
Tox_Event_Group_Moderation *new_group_moderation = (Tox_Event_Group_Moderation *)
realloc(
events->group_moderation,
new_group_moderation_capacity * sizeof(Tox_Event_Group_Moderation));
if (new_group_moderation == nullptr) {
return nullptr;
}
events->group_moderation = new_group_moderation;
events->group_moderation_capacity = new_group_moderation_capacity;
}
Tox_Event_Group_Moderation *const group_moderation =
&events->group_moderation[events->group_moderation_size];
tox_event_group_moderation_construct(group_moderation);
++events->group_moderation_size;
return group_moderation;
}
void tox_events_clear_group_moderation(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_moderation_size; ++i) {
tox_event_group_moderation_destruct(&events->group_moderation[i]);
}
free(events->group_moderation);
events->group_moderation = nullptr;
events->group_moderation_size = 0;
events->group_moderation_capacity = 0;
}
uint32_t tox_events_get_group_moderation_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_moderation_size;
}
const Tox_Event_Group_Moderation *tox_events_get_group_moderation(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_moderation_size);
assert(events->group_moderation != nullptr);
return &events->group_moderation[index];
}
bool tox_events_pack_group_moderation(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_moderation_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_moderation_pack(tox_events_get_group_moderation(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_moderation(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Moderation *event = tox_events_add_group_moderation(events);
if (event == nullptr) {
return false;
}
return tox_event_group_moderation_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_moderation(Tox *tox, uint32_t group_number, uint32_t source_peer_id, uint32_t target_peer_id, Tox_Group_Mod_Event mod_type,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Moderation *group_moderation = tox_events_add_group_moderation(state->events);
if (group_moderation == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_moderation_set_group_number(group_moderation, group_number);
tox_event_group_moderation_set_source_peer_id(group_moderation, source_peer_id);
tox_event_group_moderation_set_target_peer_id(group_moderation, target_peer_id);
tox_event_group_moderation_set_mod_type(group_moderation, mod_type);
}

View File

@ -0,0 +1,235 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Password {
uint32_t group_number;
uint8_t *password;
uint32_t password_length;
};
non_null()
static void tox_event_group_password_construct(Tox_Event_Group_Password *group_password)
{
*group_password = (Tox_Event_Group_Password) {
0
};
}
non_null()
static void tox_event_group_password_destruct(Tox_Event_Group_Password *group_password)
{
free(group_password->password);
}
non_null()
static void tox_event_group_password_set_group_number(Tox_Event_Group_Password *group_password,
uint32_t group_number)
{
assert(group_password != nullptr);
group_password->group_number = group_number;
}
uint32_t tox_event_group_password_get_group_number(const Tox_Event_Group_Password *group_password)
{
assert(group_password != nullptr);
return group_password->group_number;
}
non_null()
static bool tox_event_group_password_set_password(Tox_Event_Group_Password *group_password,
const uint8_t *password, uint32_t password_length)
{
assert(group_password != nullptr);
if (group_password->password != nullptr) {
free(group_password->password);
group_password->password = nullptr;
group_password->password_length = 0;
}
group_password->password = (uint8_t *)malloc(password_length);
if (group_password->password == nullptr) {
return false;
}
memcpy(group_password->password, password, password_length);
group_password->password_length = password_length;
return true;
}
size_t tox_event_group_password_get_password_length(const Tox_Event_Group_Password *group_password)
{
assert(group_password != nullptr);
return group_password->password_length;
}
const uint8_t *tox_event_group_password_get_password(const Tox_Event_Group_Password *group_password)
{
assert(group_password != nullptr);
return group_password->password;
}
non_null()
static bool tox_event_group_password_pack(
const Tox_Event_Group_Password *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_PASSWORD)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_bin(bp, event->password, event->password_length);
}
non_null()
static bool tox_event_group_password_unpack(
Tox_Event_Group_Password *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& bin_unpack_bin(bu, &event->password, &event->password_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Password *tox_events_add_group_password(Tox_Events *events)
{
if (events->group_password_size == UINT32_MAX) {
return nullptr;
}
if (events->group_password_size == events->group_password_capacity) {
const uint32_t new_group_password_capacity = events->group_password_capacity * 2 + 1;
Tox_Event_Group_Password *new_group_password = (Tox_Event_Group_Password *)
realloc(
events->group_password,
new_group_password_capacity * sizeof(Tox_Event_Group_Password));
if (new_group_password == nullptr) {
return nullptr;
}
events->group_password = new_group_password;
events->group_password_capacity = new_group_password_capacity;
}
Tox_Event_Group_Password *const group_password =
&events->group_password[events->group_password_size];
tox_event_group_password_construct(group_password);
++events->group_password_size;
return group_password;
}
void tox_events_clear_group_password(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_password_size; ++i) {
tox_event_group_password_destruct(&events->group_password[i]);
}
free(events->group_password);
events->group_password = nullptr;
events->group_password_size = 0;
events->group_password_capacity = 0;
}
uint32_t tox_events_get_group_password_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_password_size;
}
const Tox_Event_Group_Password *tox_events_get_group_password(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_password_size);
assert(events->group_password != nullptr);
return &events->group_password[index];
}
bool tox_events_pack_group_password(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_password_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_password_pack(tox_events_get_group_password(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_password(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Password *event = tox_events_add_group_password(events);
if (event == nullptr) {
return false;
}
return tox_event_group_password_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_password(Tox *tox, uint32_t group_number, const uint8_t *password, size_t length,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Password *group_password = tox_events_add_group_password(state->events);
if (group_password == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_password_set_group_number(group_password, group_number);
tox_event_group_password_set_password(group_password, password, length);
}

View File

@ -0,0 +1,308 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Peer_Exit {
uint32_t group_number;
uint32_t peer_id;
Tox_Group_Exit_Type exit_type;
uint8_t *name;
uint32_t name_length;
uint8_t *part_message;
uint32_t part_message_length;
};
non_null()
static void tox_event_group_peer_exit_construct(Tox_Event_Group_Peer_Exit *group_peer_exit)
{
*group_peer_exit = (Tox_Event_Group_Peer_Exit) {
0
};
}
non_null()
static void tox_event_group_peer_exit_destruct(Tox_Event_Group_Peer_Exit *group_peer_exit)
{
free(group_peer_exit->name);
free(group_peer_exit->part_message);
}
non_null()
static void tox_event_group_peer_exit_set_group_number(Tox_Event_Group_Peer_Exit *group_peer_exit,
uint32_t group_number)
{
assert(group_peer_exit != nullptr);
group_peer_exit->group_number = group_number;
}
uint32_t tox_event_group_peer_exit_get_group_number(const Tox_Event_Group_Peer_Exit *group_peer_exit)
{
assert(group_peer_exit != nullptr);
return group_peer_exit->group_number;
}
non_null()
static void tox_event_group_peer_exit_set_peer_id(Tox_Event_Group_Peer_Exit *group_peer_exit,
uint32_t peer_id)
{
assert(group_peer_exit != nullptr);
group_peer_exit->peer_id = peer_id;
}
uint32_t tox_event_group_peer_exit_get_peer_id(const Tox_Event_Group_Peer_Exit *group_peer_exit)
{
assert(group_peer_exit != nullptr);
return group_peer_exit->peer_id;
}
non_null()
static void tox_event_group_peer_exit_set_exit_type(Tox_Event_Group_Peer_Exit *group_peer_exit,
Tox_Group_Exit_Type exit_type)
{
assert(group_peer_exit != nullptr);
group_peer_exit->exit_type = exit_type;
}
Tox_Group_Exit_Type tox_event_group_peer_exit_get_exit_type(const Tox_Event_Group_Peer_Exit *group_peer_exit)
{
assert(group_peer_exit != nullptr);
return group_peer_exit->exit_type;
}
non_null()
static bool tox_event_group_peer_exit_set_name(Tox_Event_Group_Peer_Exit *group_peer_exit,
const uint8_t *name, uint32_t name_length)
{
assert(group_peer_exit != nullptr);
if (group_peer_exit->name != nullptr) {
free(group_peer_exit->name);
group_peer_exit->name = nullptr;
group_peer_exit->name_length = 0;
}
group_peer_exit->name = (uint8_t *)malloc(name_length);
if (group_peer_exit->name == nullptr) {
return false;
}
memcpy(group_peer_exit->name, name, name_length);
group_peer_exit->name_length = name_length;
return true;
}
size_t tox_event_group_peer_exit_get_name_length(const Tox_Event_Group_Peer_Exit *group_peer_exit)
{
assert(group_peer_exit != nullptr);
return group_peer_exit->name_length;
}
const uint8_t *tox_event_group_peer_exit_get_name(const Tox_Event_Group_Peer_Exit *group_peer_exit)
{
assert(group_peer_exit != nullptr);
return group_peer_exit->name;
}
non_null()
static bool tox_event_group_peer_exit_set_part_message(Tox_Event_Group_Peer_Exit *group_peer_exit,
const uint8_t *part_message, uint32_t part_message_length)
{
assert(group_peer_exit != nullptr);
if (group_peer_exit->part_message != nullptr) {
free(group_peer_exit->part_message);
group_peer_exit->part_message = nullptr;
group_peer_exit->part_message_length = 0;
}
group_peer_exit->part_message = (uint8_t *)malloc(part_message_length);
if (group_peer_exit->part_message == nullptr) {
return false;
}
memcpy(group_peer_exit->part_message, part_message, part_message_length);
group_peer_exit->part_message_length = part_message_length;
return true;
}
size_t tox_event_group_peer_exit_get_part_message_length(const Tox_Event_Group_Peer_Exit *group_peer_exit)
{
assert(group_peer_exit != nullptr);
return group_peer_exit->part_message_length;
}
const uint8_t *tox_event_group_peer_exit_get_part_message(const Tox_Event_Group_Peer_Exit *group_peer_exit)
{
assert(group_peer_exit != nullptr);
return group_peer_exit->part_message;
}
non_null()
static bool tox_event_group_peer_exit_pack(
const Tox_Event_Group_Peer_Exit *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_PEER_EXIT)
&& bin_pack_array(bp, 5)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->peer_id)
&& bin_pack_u32(bp, event->exit_type)
&& bin_pack_bin(bp, event->name, event->name_length)
&& bin_pack_bin(bp, event->part_message, event->part_message_length);
}
non_null()
static bool tox_event_group_peer_exit_unpack(
Tox_Event_Group_Peer_Exit *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 5)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& bin_unpack_u32(bu, &event->peer_id)
&& tox_unpack_group_exit_type(bu, &event->exit_type)
&& bin_unpack_bin(bu, &event->name, &event->name_length)
&& bin_unpack_bin(bu, &event->part_message, &event->part_message_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Peer_Exit *tox_events_add_group_peer_exit(Tox_Events *events)
{
if (events->group_peer_exit_size == UINT32_MAX) {
return nullptr;
}
if (events->group_peer_exit_size == events->group_peer_exit_capacity) {
const uint32_t new_group_peer_exit_capacity = events->group_peer_exit_capacity * 2 + 1;
Tox_Event_Group_Peer_Exit *new_group_peer_exit = (Tox_Event_Group_Peer_Exit *)
realloc(
events->group_peer_exit,
new_group_peer_exit_capacity * sizeof(Tox_Event_Group_Peer_Exit));
if (new_group_peer_exit == nullptr) {
return nullptr;
}
events->group_peer_exit = new_group_peer_exit;
events->group_peer_exit_capacity = new_group_peer_exit_capacity;
}
Tox_Event_Group_Peer_Exit *const group_peer_exit =
&events->group_peer_exit[events->group_peer_exit_size];
tox_event_group_peer_exit_construct(group_peer_exit);
++events->group_peer_exit_size;
return group_peer_exit;
}
void tox_events_clear_group_peer_exit(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_peer_exit_size; ++i) {
tox_event_group_peer_exit_destruct(&events->group_peer_exit[i]);
}
free(events->group_peer_exit);
events->group_peer_exit = nullptr;
events->group_peer_exit_size = 0;
events->group_peer_exit_capacity = 0;
}
uint32_t tox_events_get_group_peer_exit_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_peer_exit_size;
}
const Tox_Event_Group_Peer_Exit *tox_events_get_group_peer_exit(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_peer_exit_size);
assert(events->group_peer_exit != nullptr);
return &events->group_peer_exit[index];
}
bool tox_events_pack_group_peer_exit(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_peer_exit_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_peer_exit_pack(tox_events_get_group_peer_exit(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_peer_exit(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Peer_Exit *event = tox_events_add_group_peer_exit(events);
if (event == nullptr) {
return false;
}
return tox_event_group_peer_exit_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_peer_exit(Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Group_Exit_Type exit_type, const uint8_t *name, size_t name_length, const uint8_t *part_message, size_t part_message_length,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Peer_Exit *group_peer_exit = tox_events_add_group_peer_exit(state->events);
if (group_peer_exit == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_peer_exit_set_group_number(group_peer_exit, group_number);
tox_event_group_peer_exit_set_peer_id(group_peer_exit, peer_id);
tox_event_group_peer_exit_set_exit_type(group_peer_exit, exit_type);
tox_event_group_peer_exit_set_name(group_peer_exit, name, name_length);
tox_event_group_peer_exit_set_part_message(group_peer_exit, part_message, part_message_length);
}

View File

@ -0,0 +1,214 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Peer_Join {
uint32_t group_number;
uint32_t peer_id;
};
non_null()
static void tox_event_group_peer_join_construct(Tox_Event_Group_Peer_Join *group_peer_join)
{
*group_peer_join = (Tox_Event_Group_Peer_Join) {
0
};
}
non_null()
static void tox_event_group_peer_join_destruct(Tox_Event_Group_Peer_Join *group_peer_join)
{
return;
}
non_null()
static void tox_event_group_peer_join_set_group_number(Tox_Event_Group_Peer_Join *group_peer_join,
uint32_t group_number)
{
assert(group_peer_join != nullptr);
group_peer_join->group_number = group_number;
}
uint32_t tox_event_group_peer_join_get_group_number(const Tox_Event_Group_Peer_Join *group_peer_join)
{
assert(group_peer_join != nullptr);
return group_peer_join->group_number;
}
non_null()
static void tox_event_group_peer_join_set_peer_id(Tox_Event_Group_Peer_Join *group_peer_join,
uint32_t peer_id)
{
assert(group_peer_join != nullptr);
group_peer_join->peer_id = peer_id;
}
uint32_t tox_event_group_peer_join_get_peer_id(const Tox_Event_Group_Peer_Join *group_peer_join)
{
assert(group_peer_join != nullptr);
return group_peer_join->peer_id;
}
non_null()
static bool tox_event_group_peer_join_pack(
const Tox_Event_Group_Peer_Join *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_PEER_JOIN)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->peer_id);
}
non_null()
static bool tox_event_group_peer_join_unpack(
Tox_Event_Group_Peer_Join *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& bin_unpack_u32(bu, &event->peer_id);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Peer_Join *tox_events_add_group_peer_join(Tox_Events *events)
{
if (events->group_peer_join_size == UINT32_MAX) {
return nullptr;
}
if (events->group_peer_join_size == events->group_peer_join_capacity) {
const uint32_t new_group_peer_join_capacity = events->group_peer_join_capacity * 2 + 1;
Tox_Event_Group_Peer_Join *new_group_peer_join = (Tox_Event_Group_Peer_Join *)
realloc(
events->group_peer_join,
new_group_peer_join_capacity * sizeof(Tox_Event_Group_Peer_Join));
if (new_group_peer_join == nullptr) {
return nullptr;
}
events->group_peer_join = new_group_peer_join;
events->group_peer_join_capacity = new_group_peer_join_capacity;
}
Tox_Event_Group_Peer_Join *const group_peer_join =
&events->group_peer_join[events->group_peer_join_size];
tox_event_group_peer_join_construct(group_peer_join);
++events->group_peer_join_size;
return group_peer_join;
}
void tox_events_clear_group_peer_join(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_peer_join_size; ++i) {
tox_event_group_peer_join_destruct(&events->group_peer_join[i]);
}
free(events->group_peer_join);
events->group_peer_join = nullptr;
events->group_peer_join_size = 0;
events->group_peer_join_capacity = 0;
}
uint32_t tox_events_get_group_peer_join_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_peer_join_size;
}
const Tox_Event_Group_Peer_Join *tox_events_get_group_peer_join(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_peer_join_size);
assert(events->group_peer_join != nullptr);
return &events->group_peer_join[index];
}
bool tox_events_pack_group_peer_join(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_peer_join_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_peer_join_pack(tox_events_get_group_peer_join(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_peer_join(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Peer_Join *event = tox_events_add_group_peer_join(events);
if (event == nullptr) {
return false;
}
return tox_event_group_peer_join_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_peer_join(Tox *tox, uint32_t group_number, uint32_t peer_id,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Peer_Join *group_peer_join = tox_events_add_group_peer_join(state->events);
if (group_peer_join == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_peer_join_set_group_number(group_peer_join, group_number);
tox_event_group_peer_join_set_peer_id(group_peer_join, peer_id);
}

View File

@ -0,0 +1,214 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Peer_Limit {
uint32_t group_number;
uint32_t peer_limit;
};
non_null()
static void tox_event_group_peer_limit_construct(Tox_Event_Group_Peer_Limit *group_peer_limit)
{
*group_peer_limit = (Tox_Event_Group_Peer_Limit) {
0
};
}
non_null()
static void tox_event_group_peer_limit_destruct(Tox_Event_Group_Peer_Limit *group_peer_limit)
{
return;
}
non_null()
static void tox_event_group_peer_limit_set_group_number(Tox_Event_Group_Peer_Limit *group_peer_limit,
uint32_t group_number)
{
assert(group_peer_limit != nullptr);
group_peer_limit->group_number = group_number;
}
uint32_t tox_event_group_peer_limit_get_group_number(const Tox_Event_Group_Peer_Limit *group_peer_limit)
{
assert(group_peer_limit != nullptr);
return group_peer_limit->group_number;
}
non_null()
static void tox_event_group_peer_limit_set_peer_limit(Tox_Event_Group_Peer_Limit *group_peer_limit,
uint32_t peer_limit)
{
assert(group_peer_limit != nullptr);
group_peer_limit->peer_limit = peer_limit;
}
uint32_t tox_event_group_peer_limit_get_peer_limit(const Tox_Event_Group_Peer_Limit *group_peer_limit)
{
assert(group_peer_limit != nullptr);
return group_peer_limit->peer_limit;
}
non_null()
static bool tox_event_group_peer_limit_pack(
const Tox_Event_Group_Peer_Limit *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_PEER_LIMIT)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->peer_limit);
}
non_null()
static bool tox_event_group_peer_limit_unpack(
Tox_Event_Group_Peer_Limit *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& bin_unpack_u32(bu, &event->peer_limit);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Peer_Limit *tox_events_add_group_peer_limit(Tox_Events *events)
{
if (events->group_peer_limit_size == UINT32_MAX) {
return nullptr;
}
if (events->group_peer_limit_size == events->group_peer_limit_capacity) {
const uint32_t new_group_peer_limit_capacity = events->group_peer_limit_capacity * 2 + 1;
Tox_Event_Group_Peer_Limit *new_group_peer_limit = (Tox_Event_Group_Peer_Limit *)
realloc(
events->group_peer_limit,
new_group_peer_limit_capacity * sizeof(Tox_Event_Group_Peer_Limit));
if (new_group_peer_limit == nullptr) {
return nullptr;
}
events->group_peer_limit = new_group_peer_limit;
events->group_peer_limit_capacity = new_group_peer_limit_capacity;
}
Tox_Event_Group_Peer_Limit *const group_peer_limit =
&events->group_peer_limit[events->group_peer_limit_size];
tox_event_group_peer_limit_construct(group_peer_limit);
++events->group_peer_limit_size;
return group_peer_limit;
}
void tox_events_clear_group_peer_limit(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_peer_limit_size; ++i) {
tox_event_group_peer_limit_destruct(&events->group_peer_limit[i]);
}
free(events->group_peer_limit);
events->group_peer_limit = nullptr;
events->group_peer_limit_size = 0;
events->group_peer_limit_capacity = 0;
}
uint32_t tox_events_get_group_peer_limit_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_peer_limit_size;
}
const Tox_Event_Group_Peer_Limit *tox_events_get_group_peer_limit(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_peer_limit_size);
assert(events->group_peer_limit != nullptr);
return &events->group_peer_limit[index];
}
bool tox_events_pack_group_peer_limit(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_peer_limit_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_peer_limit_pack(tox_events_get_group_peer_limit(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_peer_limit(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Peer_Limit *event = tox_events_add_group_peer_limit(events);
if (event == nullptr) {
return false;
}
return tox_event_group_peer_limit_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_peer_limit(Tox *tox, uint32_t group_number, uint32_t peer_limit,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Peer_Limit *group_peer_limit = tox_events_add_group_peer_limit(state->events);
if (group_peer_limit == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_peer_limit_set_group_number(group_peer_limit, group_number);
tox_event_group_peer_limit_set_peer_limit(group_peer_limit, peer_limit);
}

View File

@ -0,0 +1,252 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Peer_Name {
uint32_t group_number;
uint32_t peer_id;
uint8_t *name;
uint32_t name_length;
};
non_null()
static void tox_event_group_peer_name_construct(Tox_Event_Group_Peer_Name *group_peer_name)
{
*group_peer_name = (Tox_Event_Group_Peer_Name) {
0
};
}
non_null()
static void tox_event_group_peer_name_destruct(Tox_Event_Group_Peer_Name *group_peer_name)
{
free(group_peer_name->name);
}
non_null()
static void tox_event_group_peer_name_set_group_number(Tox_Event_Group_Peer_Name *group_peer_name,
uint32_t group_number)
{
assert(group_peer_name != nullptr);
group_peer_name->group_number = group_number;
}
uint32_t tox_event_group_peer_name_get_group_number(const Tox_Event_Group_Peer_Name *group_peer_name)
{
assert(group_peer_name != nullptr);
return group_peer_name->group_number;
}
non_null()
static void tox_event_group_peer_name_set_peer_id(Tox_Event_Group_Peer_Name *group_peer_name,
uint32_t peer_id)
{
assert(group_peer_name != nullptr);
group_peer_name->peer_id = peer_id;
}
uint32_t tox_event_group_peer_name_get_peer_id(const Tox_Event_Group_Peer_Name *group_peer_name)
{
assert(group_peer_name != nullptr);
return group_peer_name->peer_id;
}
non_null()
static bool tox_event_group_peer_name_set_name(Tox_Event_Group_Peer_Name *group_peer_name,
const uint8_t *name, uint32_t name_length)
{
assert(group_peer_name != nullptr);
if (group_peer_name->name != nullptr) {
free(group_peer_name->name);
group_peer_name->name = nullptr;
group_peer_name->name_length = 0;
}
group_peer_name->name = (uint8_t *)malloc(name_length);
if (group_peer_name->name == nullptr) {
return false;
}
memcpy(group_peer_name->name, name, name_length);
group_peer_name->name_length = name_length;
return true;
}
size_t tox_event_group_peer_name_get_name_length(const Tox_Event_Group_Peer_Name *group_peer_name)
{
assert(group_peer_name != nullptr);
return group_peer_name->name_length;
}
const uint8_t *tox_event_group_peer_name_get_name(const Tox_Event_Group_Peer_Name *group_peer_name)
{
assert(group_peer_name != nullptr);
return group_peer_name->name;
}
non_null()
static bool tox_event_group_peer_name_pack(
const Tox_Event_Group_Peer_Name *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_PEER_NAME)
&& bin_pack_array(bp, 3)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->peer_id)
&& bin_pack_bin(bp, event->name, event->name_length);
}
non_null()
static bool tox_event_group_peer_name_unpack(
Tox_Event_Group_Peer_Name *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 3)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& bin_unpack_u32(bu, &event->peer_id)
&& bin_unpack_bin(bu, &event->name, &event->name_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Peer_Name *tox_events_add_group_peer_name(Tox_Events *events)
{
if (events->group_peer_name_size == UINT32_MAX) {
return nullptr;
}
if (events->group_peer_name_size == events->group_peer_name_capacity) {
const uint32_t new_group_peer_name_capacity = events->group_peer_name_capacity * 2 + 1;
Tox_Event_Group_Peer_Name *new_group_peer_name = (Tox_Event_Group_Peer_Name *)
realloc(
events->group_peer_name,
new_group_peer_name_capacity * sizeof(Tox_Event_Group_Peer_Name));
if (new_group_peer_name == nullptr) {
return nullptr;
}
events->group_peer_name = new_group_peer_name;
events->group_peer_name_capacity = new_group_peer_name_capacity;
}
Tox_Event_Group_Peer_Name *const group_peer_name =
&events->group_peer_name[events->group_peer_name_size];
tox_event_group_peer_name_construct(group_peer_name);
++events->group_peer_name_size;
return group_peer_name;
}
void tox_events_clear_group_peer_name(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_peer_name_size; ++i) {
tox_event_group_peer_name_destruct(&events->group_peer_name[i]);
}
free(events->group_peer_name);
events->group_peer_name = nullptr;
events->group_peer_name_size = 0;
events->group_peer_name_capacity = 0;
}
uint32_t tox_events_get_group_peer_name_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_peer_name_size;
}
const Tox_Event_Group_Peer_Name *tox_events_get_group_peer_name(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_peer_name_size);
assert(events->group_peer_name != nullptr);
return &events->group_peer_name[index];
}
bool tox_events_pack_group_peer_name(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_peer_name_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_peer_name_pack(tox_events_get_group_peer_name(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_peer_name(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Peer_Name *event = tox_events_add_group_peer_name(events);
if (event == nullptr) {
return false;
}
return tox_event_group_peer_name_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_peer_name(Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *name, size_t length,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Peer_Name *group_peer_name = tox_events_add_group_peer_name(state->events);
if (group_peer_name == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_peer_name_set_group_number(group_peer_name, group_number);
tox_event_group_peer_name_set_peer_id(group_peer_name, peer_id);
tox_event_group_peer_name_set_name(group_peer_name, name, length);
}

View File

@ -0,0 +1,231 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Peer_Status {
uint32_t group_number;
uint32_t peer_id;
Tox_User_Status status;
};
non_null()
static void tox_event_group_peer_status_construct(Tox_Event_Group_Peer_Status *group_peer_status)
{
*group_peer_status = (Tox_Event_Group_Peer_Status) {
0
};
}
non_null()
static void tox_event_group_peer_status_destruct(Tox_Event_Group_Peer_Status *group_peer_status)
{
return;
}
non_null()
static void tox_event_group_peer_status_set_group_number(Tox_Event_Group_Peer_Status *group_peer_status,
uint32_t group_number)
{
assert(group_peer_status != nullptr);
group_peer_status->group_number = group_number;
}
uint32_t tox_event_group_peer_status_get_group_number(const Tox_Event_Group_Peer_Status *group_peer_status)
{
assert(group_peer_status != nullptr);
return group_peer_status->group_number;
}
non_null()
static void tox_event_group_peer_status_set_peer_id(Tox_Event_Group_Peer_Status *group_peer_status,
uint32_t peer_id)
{
assert(group_peer_status != nullptr);
group_peer_status->peer_id = peer_id;
}
uint32_t tox_event_group_peer_status_get_peer_id(const Tox_Event_Group_Peer_Status *group_peer_status)
{
assert(group_peer_status != nullptr);
return group_peer_status->peer_id;
}
non_null()
static void tox_event_group_peer_status_set_status(Tox_Event_Group_Peer_Status *group_peer_status,
Tox_User_Status status)
{
assert(group_peer_status != nullptr);
group_peer_status->status = status;
}
Tox_User_Status tox_event_group_peer_status_get_status(const Tox_Event_Group_Peer_Status *group_peer_status)
{
assert(group_peer_status != nullptr);
return group_peer_status->status;
}
non_null()
static bool tox_event_group_peer_status_pack(
const Tox_Event_Group_Peer_Status *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_PEER_STATUS)
&& bin_pack_array(bp, 3)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->peer_id)
&& bin_pack_u32(bp, event->status);
}
non_null()
static bool tox_event_group_peer_status_unpack(
Tox_Event_Group_Peer_Status *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 3)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& bin_unpack_u32(bu, &event->peer_id)
&& tox_unpack_user_status(bu, &event->status);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Peer_Status *tox_events_add_group_peer_status(Tox_Events *events)
{
if (events->group_peer_status_size == UINT32_MAX) {
return nullptr;
}
if (events->group_peer_status_size == events->group_peer_status_capacity) {
const uint32_t new_group_peer_status_capacity = events->group_peer_status_capacity * 2 + 1;
Tox_Event_Group_Peer_Status *new_group_peer_status = (Tox_Event_Group_Peer_Status *)
realloc(
events->group_peer_status,
new_group_peer_status_capacity * sizeof(Tox_Event_Group_Peer_Status));
if (new_group_peer_status == nullptr) {
return nullptr;
}
events->group_peer_status = new_group_peer_status;
events->group_peer_status_capacity = new_group_peer_status_capacity;
}
Tox_Event_Group_Peer_Status *const group_peer_status =
&events->group_peer_status[events->group_peer_status_size];
tox_event_group_peer_status_construct(group_peer_status);
++events->group_peer_status_size;
return group_peer_status;
}
void tox_events_clear_group_peer_status(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_peer_status_size; ++i) {
tox_event_group_peer_status_destruct(&events->group_peer_status[i]);
}
free(events->group_peer_status);
events->group_peer_status = nullptr;
events->group_peer_status_size = 0;
events->group_peer_status_capacity = 0;
}
uint32_t tox_events_get_group_peer_status_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_peer_status_size;
}
const Tox_Event_Group_Peer_Status *tox_events_get_group_peer_status(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_peer_status_size);
assert(events->group_peer_status != nullptr);
return &events->group_peer_status[index];
}
bool tox_events_pack_group_peer_status(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_peer_status_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_peer_status_pack(tox_events_get_group_peer_status(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_peer_status(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Peer_Status *event = tox_events_add_group_peer_status(events);
if (event == nullptr) {
return false;
}
return tox_event_group_peer_status_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_peer_status(Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_User_Status status,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Peer_Status *group_peer_status = tox_events_add_group_peer_status(state->events);
if (group_peer_status == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_peer_status_set_group_number(group_peer_status, group_number);
tox_event_group_peer_status_set_peer_id(group_peer_status, peer_id);
tox_event_group_peer_status_set_status(group_peer_status, status);
}

View File

@ -0,0 +1,214 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Privacy_State {
uint32_t group_number;
Tox_Group_Privacy_State privacy_state;
};
non_null()
static void tox_event_group_privacy_state_construct(Tox_Event_Group_Privacy_State *group_privacy_state)
{
*group_privacy_state = (Tox_Event_Group_Privacy_State) {
0
};
}
non_null()
static void tox_event_group_privacy_state_destruct(Tox_Event_Group_Privacy_State *group_privacy_state)
{
return;
}
non_null()
static void tox_event_group_privacy_state_set_group_number(Tox_Event_Group_Privacy_State *group_privacy_state,
uint32_t group_number)
{
assert(group_privacy_state != nullptr);
group_privacy_state->group_number = group_number;
}
uint32_t tox_event_group_privacy_state_get_group_number(const Tox_Event_Group_Privacy_State *group_privacy_state)
{
assert(group_privacy_state != nullptr);
return group_privacy_state->group_number;
}
non_null()
static void tox_event_group_privacy_state_set_privacy_state(Tox_Event_Group_Privacy_State *group_privacy_state,
Tox_Group_Privacy_State privacy_state)
{
assert(group_privacy_state != nullptr);
group_privacy_state->privacy_state = privacy_state;
}
Tox_Group_Privacy_State tox_event_group_privacy_state_get_privacy_state(const Tox_Event_Group_Privacy_State *group_privacy_state)
{
assert(group_privacy_state != nullptr);
return group_privacy_state->privacy_state;
}
non_null()
static bool tox_event_group_privacy_state_pack(
const Tox_Event_Group_Privacy_State *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_PRIVACY_STATE)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->privacy_state);
}
non_null()
static bool tox_event_group_privacy_state_unpack(
Tox_Event_Group_Privacy_State *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& tox_unpack_group_privacy_state(bu, &event->privacy_state);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Privacy_State *tox_events_add_group_privacy_state(Tox_Events *events)
{
if (events->group_privacy_state_size == UINT32_MAX) {
return nullptr;
}
if (events->group_privacy_state_size == events->group_privacy_state_capacity) {
const uint32_t new_group_privacy_state_capacity = events->group_privacy_state_capacity * 2 + 1;
Tox_Event_Group_Privacy_State *new_group_privacy_state = (Tox_Event_Group_Privacy_State *)
realloc(
events->group_privacy_state,
new_group_privacy_state_capacity * sizeof(Tox_Event_Group_Privacy_State));
if (new_group_privacy_state == nullptr) {
return nullptr;
}
events->group_privacy_state = new_group_privacy_state;
events->group_privacy_state_capacity = new_group_privacy_state_capacity;
}
Tox_Event_Group_Privacy_State *const group_privacy_state =
&events->group_privacy_state[events->group_privacy_state_size];
tox_event_group_privacy_state_construct(group_privacy_state);
++events->group_privacy_state_size;
return group_privacy_state;
}
void tox_events_clear_group_privacy_state(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_privacy_state_size; ++i) {
tox_event_group_privacy_state_destruct(&events->group_privacy_state[i]);
}
free(events->group_privacy_state);
events->group_privacy_state = nullptr;
events->group_privacy_state_size = 0;
events->group_privacy_state_capacity = 0;
}
uint32_t tox_events_get_group_privacy_state_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_privacy_state_size;
}
const Tox_Event_Group_Privacy_State *tox_events_get_group_privacy_state(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_privacy_state_size);
assert(events->group_privacy_state != nullptr);
return &events->group_privacy_state[index];
}
bool tox_events_pack_group_privacy_state(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_privacy_state_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_privacy_state_pack(tox_events_get_group_privacy_state(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_privacy_state(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Privacy_State *event = tox_events_add_group_privacy_state(events);
if (event == nullptr) {
return false;
}
return tox_event_group_privacy_state_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_privacy_state(Tox *tox, uint32_t group_number, Tox_Group_Privacy_State privacy_state,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Privacy_State *group_privacy_state = tox_events_add_group_privacy_state(state->events);
if (group_privacy_state == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_privacy_state_set_group_number(group_privacy_state, group_number);
tox_event_group_privacy_state_set_privacy_state(group_privacy_state, privacy_state);
}

View File

@ -0,0 +1,269 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Private_Message {
uint32_t group_number;
uint32_t peer_id;
Tox_Message_Type type;
uint8_t *message;
uint32_t message_length;
};
non_null()
static void tox_event_group_private_message_construct(Tox_Event_Group_Private_Message *group_private_message)
{
*group_private_message = (Tox_Event_Group_Private_Message) {
0
};
}
non_null()
static void tox_event_group_private_message_destruct(Tox_Event_Group_Private_Message *group_private_message)
{
free(group_private_message->message);
}
non_null()
static void tox_event_group_private_message_set_group_number(Tox_Event_Group_Private_Message *group_private_message,
uint32_t group_number)
{
assert(group_private_message != nullptr);
group_private_message->group_number = group_number;
}
uint32_t tox_event_group_private_message_get_group_number(const Tox_Event_Group_Private_Message *group_private_message)
{
assert(group_private_message != nullptr);
return group_private_message->group_number;
}
non_null()
static void tox_event_group_private_message_set_peer_id(Tox_Event_Group_Private_Message *group_private_message,
uint32_t peer_id)
{
assert(group_private_message != nullptr);
group_private_message->peer_id = peer_id;
}
uint32_t tox_event_group_private_message_get_peer_id(const Tox_Event_Group_Private_Message *group_private_message)
{
assert(group_private_message != nullptr);
return group_private_message->peer_id;
}
non_null()
static void tox_event_group_private_message_set_type(Tox_Event_Group_Private_Message *group_private_message,
Tox_Message_Type type)
{
assert(group_private_message != nullptr);
group_private_message->type = type;
}
Tox_Message_Type tox_event_group_private_message_get_type(const Tox_Event_Group_Private_Message *group_private_message)
{
assert(group_private_message != nullptr);
return group_private_message->type;
}
non_null()
static bool tox_event_group_private_message_set_message(Tox_Event_Group_Private_Message *group_private_message,
const uint8_t *message, uint32_t message_length)
{
assert(group_private_message != nullptr);
if (group_private_message->message != nullptr) {
free(group_private_message->message);
group_private_message->message = nullptr;
group_private_message->message_length = 0;
}
group_private_message->message = (uint8_t *)malloc(message_length);
if (group_private_message->message == nullptr) {
return false;
}
memcpy(group_private_message->message, message, message_length);
group_private_message->message_length = message_length;
return true;
}
size_t tox_event_group_private_message_get_message_length(const Tox_Event_Group_Private_Message *group_private_message)
{
assert(group_private_message != nullptr);
return group_private_message->message_length;
}
const uint8_t *tox_event_group_private_message_get_message(const Tox_Event_Group_Private_Message *group_private_message)
{
assert(group_private_message != nullptr);
return group_private_message->message;
}
non_null()
static bool tox_event_group_private_message_pack(
const Tox_Event_Group_Private_Message *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_PRIVATE_MESSAGE)
&& bin_pack_array(bp, 4)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->peer_id)
&& bin_pack_u32(bp, event->type)
&& bin_pack_bin(bp, event->message, event->message_length);
}
non_null()
static bool tox_event_group_private_message_unpack(
Tox_Event_Group_Private_Message *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 4)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& bin_unpack_u32(bu, &event->peer_id)
&& tox_unpack_message_type(bu, &event->type)
&& bin_unpack_bin(bu, &event->message, &event->message_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Private_Message *tox_events_add_group_private_message(Tox_Events *events)
{
if (events->group_private_message_size == UINT32_MAX) {
return nullptr;
}
if (events->group_private_message_size == events->group_private_message_capacity) {
const uint32_t new_group_private_message_capacity = events->group_private_message_capacity * 2 + 1;
Tox_Event_Group_Private_Message *new_group_private_message = (Tox_Event_Group_Private_Message *)
realloc(
events->group_private_message,
new_group_private_message_capacity * sizeof(Tox_Event_Group_Private_Message));
if (new_group_private_message == nullptr) {
return nullptr;
}
events->group_private_message = new_group_private_message;
events->group_private_message_capacity = new_group_private_message_capacity;
}
Tox_Event_Group_Private_Message *const group_private_message =
&events->group_private_message[events->group_private_message_size];
tox_event_group_private_message_construct(group_private_message);
++events->group_private_message_size;
return group_private_message;
}
void tox_events_clear_group_private_message(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_private_message_size; ++i) {
tox_event_group_private_message_destruct(&events->group_private_message[i]);
}
free(events->group_private_message);
events->group_private_message = nullptr;
events->group_private_message_size = 0;
events->group_private_message_capacity = 0;
}
uint32_t tox_events_get_group_private_message_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_private_message_size;
}
const Tox_Event_Group_Private_Message *tox_events_get_group_private_message(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_private_message_size);
assert(events->group_private_message != nullptr);
return &events->group_private_message[index];
}
bool tox_events_pack_group_private_message(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_private_message_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_private_message_pack(tox_events_get_group_private_message(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_private_message(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Private_Message *event = tox_events_add_group_private_message(events);
if (event == nullptr) {
return false;
}
return tox_event_group_private_message_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_private_message(Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Message_Type type, const uint8_t *message, size_t length,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Private_Message *group_private_message = tox_events_add_group_private_message(state->events);
if (group_private_message == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_private_message_set_group_number(group_private_message, group_number);
tox_event_group_private_message_set_peer_id(group_private_message, peer_id);
tox_event_group_private_message_set_type(group_private_message, type);
tox_event_group_private_message_set_message(group_private_message, message, length);
}

View File

@ -0,0 +1,193 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Self_Join {
uint32_t group_number;
};
non_null()
static void tox_event_group_self_join_construct(Tox_Event_Group_Self_Join *group_self_join)
{
*group_self_join = (Tox_Event_Group_Self_Join) {
0
};
}
non_null()
static void tox_event_group_self_join_destruct(Tox_Event_Group_Self_Join *group_self_join)
{
return;
}
non_null()
static void tox_event_group_self_join_set_group_number(Tox_Event_Group_Self_Join *group_self_join,
uint32_t group_number)
{
assert(group_self_join != nullptr);
group_self_join->group_number = group_number;
}
uint32_t tox_event_group_self_join_get_group_number(const Tox_Event_Group_Self_Join *group_self_join)
{
assert(group_self_join != nullptr);
return group_self_join->group_number;
}
non_null()
static bool tox_event_group_self_join_pack(
const Tox_Event_Group_Self_Join *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_SELF_JOIN)
&& bin_pack_u32(bp, event->group_number);
}
non_null()
static bool tox_event_group_self_join_unpack(
Tox_Event_Group_Self_Join *event, Bin_Unpack *bu)
{
assert(event != nullptr);
return bin_unpack_u32(bu, &event->group_number);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Self_Join *tox_events_add_group_self_join(Tox_Events *events)
{
if (events->group_self_join_size == UINT32_MAX) {
return nullptr;
}
if (events->group_self_join_size == events->group_self_join_capacity) {
const uint32_t new_group_self_join_capacity = events->group_self_join_capacity * 2 + 1;
Tox_Event_Group_Self_Join *new_group_self_join = (Tox_Event_Group_Self_Join *)
realloc(
events->group_self_join,
new_group_self_join_capacity * sizeof(Tox_Event_Group_Self_Join));
if (new_group_self_join == nullptr) {
return nullptr;
}
events->group_self_join = new_group_self_join;
events->group_self_join_capacity = new_group_self_join_capacity;
}
Tox_Event_Group_Self_Join *const group_self_join =
&events->group_self_join[events->group_self_join_size];
tox_event_group_self_join_construct(group_self_join);
++events->group_self_join_size;
return group_self_join;
}
void tox_events_clear_group_self_join(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_self_join_size; ++i) {
tox_event_group_self_join_destruct(&events->group_self_join[i]);
}
free(events->group_self_join);
events->group_self_join = nullptr;
events->group_self_join_size = 0;
events->group_self_join_capacity = 0;
}
uint32_t tox_events_get_group_self_join_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_self_join_size;
}
const Tox_Event_Group_Self_Join *tox_events_get_group_self_join(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_self_join_size);
assert(events->group_self_join != nullptr);
return &events->group_self_join[index];
}
bool tox_events_pack_group_self_join(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_self_join_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_self_join_pack(tox_events_get_group_self_join(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_self_join(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Self_Join *event = tox_events_add_group_self_join(events);
if (event == nullptr) {
return false;
}
return tox_event_group_self_join_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_self_join(Tox *tox, uint32_t group_number,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Self_Join *group_self_join = tox_events_add_group_self_join(state->events);
if (group_self_join == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_self_join_set_group_number(group_self_join, group_number);
}

View File

@ -0,0 +1,252 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Topic {
uint32_t group_number;
uint32_t peer_id;
uint8_t *topic;
uint32_t topic_length;
};
non_null()
static void tox_event_group_topic_construct(Tox_Event_Group_Topic *group_topic)
{
*group_topic = (Tox_Event_Group_Topic) {
0
};
}
non_null()
static void tox_event_group_topic_destruct(Tox_Event_Group_Topic *group_topic)
{
free(group_topic->topic);
}
non_null()
static void tox_event_group_topic_set_group_number(Tox_Event_Group_Topic *group_topic,
uint32_t group_number)
{
assert(group_topic != nullptr);
group_topic->group_number = group_number;
}
uint32_t tox_event_group_topic_get_group_number(const Tox_Event_Group_Topic *group_topic)
{
assert(group_topic != nullptr);
return group_topic->group_number;
}
non_null()
static void tox_event_group_topic_set_peer_id(Tox_Event_Group_Topic *group_topic,
uint32_t peer_id)
{
assert(group_topic != nullptr);
group_topic->peer_id = peer_id;
}
uint32_t tox_event_group_topic_get_peer_id(const Tox_Event_Group_Topic *group_topic)
{
assert(group_topic != nullptr);
return group_topic->peer_id;
}
non_null()
static bool tox_event_group_topic_set_topic(Tox_Event_Group_Topic *group_topic,
const uint8_t *topic, uint32_t topic_length)
{
assert(group_topic != nullptr);
if (group_topic->topic != nullptr) {
free(group_topic->topic);
group_topic->topic = nullptr;
group_topic->topic_length = 0;
}
group_topic->topic = (uint8_t *)malloc(topic_length);
if (group_topic->topic == nullptr) {
return false;
}
memcpy(group_topic->topic, topic, topic_length);
group_topic->topic_length = topic_length;
return true;
}
size_t tox_event_group_topic_get_topic_length(const Tox_Event_Group_Topic *group_topic)
{
assert(group_topic != nullptr);
return group_topic->topic_length;
}
const uint8_t *tox_event_group_topic_get_topic(const Tox_Event_Group_Topic *group_topic)
{
assert(group_topic != nullptr);
return group_topic->topic;
}
non_null()
static bool tox_event_group_topic_pack(
const Tox_Event_Group_Topic *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_TOPIC)
&& bin_pack_array(bp, 3)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->peer_id)
&& bin_pack_bin(bp, event->topic, event->topic_length);
}
non_null()
static bool tox_event_group_topic_unpack(
Tox_Event_Group_Topic *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 3)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& bin_unpack_u32(bu, &event->peer_id)
&& bin_unpack_bin(bu, &event->topic, &event->topic_length);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Topic *tox_events_add_group_topic(Tox_Events *events)
{
if (events->group_topic_size == UINT32_MAX) {
return nullptr;
}
if (events->group_topic_size == events->group_topic_capacity) {
const uint32_t new_group_topic_capacity = events->group_topic_capacity * 2 + 1;
Tox_Event_Group_Topic *new_group_topic = (Tox_Event_Group_Topic *)
realloc(
events->group_topic,
new_group_topic_capacity * sizeof(Tox_Event_Group_Topic));
if (new_group_topic == nullptr) {
return nullptr;
}
events->group_topic = new_group_topic;
events->group_topic_capacity = new_group_topic_capacity;
}
Tox_Event_Group_Topic *const group_topic =
&events->group_topic[events->group_topic_size];
tox_event_group_topic_construct(group_topic);
++events->group_topic_size;
return group_topic;
}
void tox_events_clear_group_topic(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_topic_size; ++i) {
tox_event_group_topic_destruct(&events->group_topic[i]);
}
free(events->group_topic);
events->group_topic = nullptr;
events->group_topic_size = 0;
events->group_topic_capacity = 0;
}
uint32_t tox_events_get_group_topic_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_topic_size;
}
const Tox_Event_Group_Topic *tox_events_get_group_topic(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_topic_size);
assert(events->group_topic != nullptr);
return &events->group_topic[index];
}
bool tox_events_pack_group_topic(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_topic_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_topic_pack(tox_events_get_group_topic(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_topic(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Topic *event = tox_events_add_group_topic(events);
if (event == nullptr) {
return false;
}
return tox_event_group_topic_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_topic(Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *topic, size_t length,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Topic *group_topic = tox_events_add_group_topic(state->events);
if (group_topic == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_topic_set_group_number(group_topic, group_number);
tox_event_group_topic_set_peer_id(group_topic, peer_id);
tox_event_group_topic_set_topic(group_topic, topic, length);
}

View File

@ -0,0 +1,214 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Topic_Lock {
uint32_t group_number;
Tox_Group_Topic_Lock topic_lock;
};
non_null()
static void tox_event_group_topic_lock_construct(Tox_Event_Group_Topic_Lock *group_topic_lock)
{
*group_topic_lock = (Tox_Event_Group_Topic_Lock) {
0
};
}
non_null()
static void tox_event_group_topic_lock_destruct(Tox_Event_Group_Topic_Lock *group_topic_lock)
{
return;
}
non_null()
static void tox_event_group_topic_lock_set_group_number(Tox_Event_Group_Topic_Lock *group_topic_lock,
uint32_t group_number)
{
assert(group_topic_lock != nullptr);
group_topic_lock->group_number = group_number;
}
uint32_t tox_event_group_topic_lock_get_group_number(const Tox_Event_Group_Topic_Lock *group_topic_lock)
{
assert(group_topic_lock != nullptr);
return group_topic_lock->group_number;
}
non_null()
static void tox_event_group_topic_lock_set_topic_lock(Tox_Event_Group_Topic_Lock *group_topic_lock,
Tox_Group_Topic_Lock topic_lock)
{
assert(group_topic_lock != nullptr);
group_topic_lock->topic_lock = topic_lock;
}
Tox_Group_Topic_Lock tox_event_group_topic_lock_get_topic_lock(const Tox_Event_Group_Topic_Lock *group_topic_lock)
{
assert(group_topic_lock != nullptr);
return group_topic_lock->topic_lock;
}
non_null()
static bool tox_event_group_topic_lock_pack(
const Tox_Event_Group_Topic_Lock *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_TOPIC_LOCK)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->topic_lock);
}
non_null()
static bool tox_event_group_topic_lock_unpack(
Tox_Event_Group_Topic_Lock *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& tox_unpack_group_topic_lock(bu, &event->topic_lock);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Topic_Lock *tox_events_add_group_topic_lock(Tox_Events *events)
{
if (events->group_topic_lock_size == UINT32_MAX) {
return nullptr;
}
if (events->group_topic_lock_size == events->group_topic_lock_capacity) {
const uint32_t new_group_topic_lock_capacity = events->group_topic_lock_capacity * 2 + 1;
Tox_Event_Group_Topic_Lock *new_group_topic_lock = (Tox_Event_Group_Topic_Lock *)
realloc(
events->group_topic_lock,
new_group_topic_lock_capacity * sizeof(Tox_Event_Group_Topic_Lock));
if (new_group_topic_lock == nullptr) {
return nullptr;
}
events->group_topic_lock = new_group_topic_lock;
events->group_topic_lock_capacity = new_group_topic_lock_capacity;
}
Tox_Event_Group_Topic_Lock *const group_topic_lock =
&events->group_topic_lock[events->group_topic_lock_size];
tox_event_group_topic_lock_construct(group_topic_lock);
++events->group_topic_lock_size;
return group_topic_lock;
}
void tox_events_clear_group_topic_lock(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_topic_lock_size; ++i) {
tox_event_group_topic_lock_destruct(&events->group_topic_lock[i]);
}
free(events->group_topic_lock);
events->group_topic_lock = nullptr;
events->group_topic_lock_size = 0;
events->group_topic_lock_capacity = 0;
}
uint32_t tox_events_get_group_topic_lock_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_topic_lock_size;
}
const Tox_Event_Group_Topic_Lock *tox_events_get_group_topic_lock(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_topic_lock_size);
assert(events->group_topic_lock != nullptr);
return &events->group_topic_lock[index];
}
bool tox_events_pack_group_topic_lock(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_topic_lock_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_topic_lock_pack(tox_events_get_group_topic_lock(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_topic_lock(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Topic_Lock *event = tox_events_add_group_topic_lock(events);
if (event == nullptr) {
return false;
}
return tox_event_group_topic_lock_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_topic_lock(Tox *tox, uint32_t group_number, Tox_Group_Topic_Lock topic_lock,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Topic_Lock *group_topic_lock = tox_events_add_group_topic_lock(state->events);
if (group_topic_lock == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_topic_lock_set_group_number(group_topic_lock, group_number);
tox_event_group_topic_lock_set_topic_lock(group_topic_lock, topic_lock);
}

View File

@ -0,0 +1,214 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2023 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Group_Voice_State {
uint32_t group_number;
Tox_Group_Voice_State voice_state;
};
non_null()
static void tox_event_group_voice_state_construct(Tox_Event_Group_Voice_State *group_voice_state)
{
*group_voice_state = (Tox_Event_Group_Voice_State) {
0
};
}
non_null()
static void tox_event_group_voice_state_destruct(Tox_Event_Group_Voice_State *group_voice_state)
{
return;
}
non_null()
static void tox_event_group_voice_state_set_group_number(Tox_Event_Group_Voice_State *group_voice_state,
uint32_t group_number)
{
assert(group_voice_state != nullptr);
group_voice_state->group_number = group_number;
}
uint32_t tox_event_group_voice_state_get_group_number(const Tox_Event_Group_Voice_State *group_voice_state)
{
assert(group_voice_state != nullptr);
return group_voice_state->group_number;
}
non_null()
static void tox_event_group_voice_state_set_voice_state(Tox_Event_Group_Voice_State *group_voice_state,
Tox_Group_Voice_State voice_state)
{
assert(group_voice_state != nullptr);
group_voice_state->voice_state = voice_state;
}
Tox_Group_Voice_State tox_event_group_voice_state_get_voice_state(const Tox_Event_Group_Voice_State *group_voice_state)
{
assert(group_voice_state != nullptr);
return group_voice_state->voice_state;
}
non_null()
static bool tox_event_group_voice_state_pack(
const Tox_Event_Group_Voice_State *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_GROUP_VOICE_STATE)
&& bin_pack_array(bp, 2)
&& bin_pack_u32(bp, event->group_number)
&& bin_pack_u32(bp, event->voice_state);
}
non_null()
static bool tox_event_group_voice_state_unpack(
Tox_Event_Group_Voice_State *event, Bin_Unpack *bu)
{
assert(event != nullptr);
if (!bin_unpack_array_fixed(bu, 2)) {
return false;
}
return bin_unpack_u32(bu, &event->group_number)
&& tox_unpack_group_voice_state(bu, &event->voice_state);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Group_Voice_State *tox_events_add_group_voice_state(Tox_Events *events)
{
if (events->group_voice_state_size == UINT32_MAX) {
return nullptr;
}
if (events->group_voice_state_size == events->group_voice_state_capacity) {
const uint32_t new_group_voice_state_capacity = events->group_voice_state_capacity * 2 + 1;
Tox_Event_Group_Voice_State *new_group_voice_state = (Tox_Event_Group_Voice_State *)
realloc(
events->group_voice_state,
new_group_voice_state_capacity * sizeof(Tox_Event_Group_Voice_State));
if (new_group_voice_state == nullptr) {
return nullptr;
}
events->group_voice_state = new_group_voice_state;
events->group_voice_state_capacity = new_group_voice_state_capacity;
}
Tox_Event_Group_Voice_State *const group_voice_state =
&events->group_voice_state[events->group_voice_state_size];
tox_event_group_voice_state_construct(group_voice_state);
++events->group_voice_state_size;
return group_voice_state;
}
void tox_events_clear_group_voice_state(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->group_voice_state_size; ++i) {
tox_event_group_voice_state_destruct(&events->group_voice_state[i]);
}
free(events->group_voice_state);
events->group_voice_state = nullptr;
events->group_voice_state_size = 0;
events->group_voice_state_capacity = 0;
}
uint32_t tox_events_get_group_voice_state_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->group_voice_state_size;
}
const Tox_Event_Group_Voice_State *tox_events_get_group_voice_state(const Tox_Events *events, uint32_t index)
{
assert(index < events->group_voice_state_size);
assert(events->group_voice_state != nullptr);
return &events->group_voice_state[index];
}
bool tox_events_pack_group_voice_state(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_group_voice_state_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_group_voice_state_pack(tox_events_get_group_voice_state(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_group_voice_state(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Group_Voice_State *event = tox_events_add_group_voice_state(events);
if (event == nullptr) {
return false;
}
return tox_event_group_voice_state_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_group_voice_state(Tox *tox, uint32_t group_number, Tox_Group_Voice_State voice_state,
void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Group_Voice_State *group_voice_state = tox_events_add_group_voice_state(state->events);
if (group_voice_state == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_group_voice_state_set_group_number(group_voice_state, group_number);
tox_event_group_voice_state_set_voice_state(group_voice_state, voice_state);
}

View File

@ -0,0 +1,190 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2022 The TokTok team.
*/
#include "events_alloc.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "../bin_pack.h"
#include "../bin_unpack.h"
#include "../ccompat.h"
#include "../tox.h"
#include "../tox_events.h"
#include "../tox_unpack.h"
/*****************************************************
*
* :: struct and accessors
*
*****************************************************/
struct Tox_Event_Self_Connection_Status {
Tox_Connection connection_status;
};
non_null()
static void tox_event_self_connection_status_construct(Tox_Event_Self_Connection_Status *self_connection_status)
{
*self_connection_status = (Tox_Event_Self_Connection_Status) {
TOX_CONNECTION_NONE
};
}
non_null()
static void tox_event_self_connection_status_destruct(Tox_Event_Self_Connection_Status *self_connection_status)
{
return;
}
non_null()
static void tox_event_self_connection_status_set_connection_status(Tox_Event_Self_Connection_Status
*self_connection_status, Tox_Connection connection_status)
{
assert(self_connection_status != nullptr);
self_connection_status->connection_status = connection_status;
}
Tox_Connection tox_event_self_connection_status_get_connection_status(const Tox_Event_Self_Connection_Status
*self_connection_status)
{
assert(self_connection_status != nullptr);
return self_connection_status->connection_status;
}
non_null()
static bool tox_event_self_connection_status_pack(
const Tox_Event_Self_Connection_Status *event, Bin_Pack *bp)
{
assert(event != nullptr);
return bin_pack_array(bp, 2)
&& bin_pack_u32(bp, TOX_EVENT_SELF_CONNECTION_STATUS)
&& bin_pack_u32(bp, event->connection_status);
}
non_null()
static bool tox_event_self_connection_status_unpack(
Tox_Event_Self_Connection_Status *event, Bin_Unpack *bu)
{
assert(event != nullptr);
return tox_unpack_connection(bu, &event->connection_status);
}
/*****************************************************
*
* :: add/clear/get
*
*****************************************************/
non_null()
static Tox_Event_Self_Connection_Status *tox_events_add_self_connection_status(Tox_Events *events)
{
if (events->self_connection_status_size == UINT32_MAX) {
return nullptr;
}
if (events->self_connection_status_size == events->self_connection_status_capacity) {
const uint32_t new_self_connection_status_capacity = events->self_connection_status_capacity * 2 + 1;
Tox_Event_Self_Connection_Status *new_self_connection_status = (Tox_Event_Self_Connection_Status *)realloc(
events->self_connection_status, new_self_connection_status_capacity * sizeof(Tox_Event_Self_Connection_Status));
if (new_self_connection_status == nullptr) {
return nullptr;
}
events->self_connection_status = new_self_connection_status;
events->self_connection_status_capacity = new_self_connection_status_capacity;
}
Tox_Event_Self_Connection_Status *const self_connection_status =
&events->self_connection_status[events->self_connection_status_size];
tox_event_self_connection_status_construct(self_connection_status);
++events->self_connection_status_size;
return self_connection_status;
}
void tox_events_clear_self_connection_status(Tox_Events *events)
{
if (events == nullptr) {
return;
}
for (uint32_t i = 0; i < events->self_connection_status_size; ++i) {
tox_event_self_connection_status_destruct(&events->self_connection_status[i]);
}
free(events->self_connection_status);
events->self_connection_status = nullptr;
events->self_connection_status_size = 0;
events->self_connection_status_capacity = 0;
}
uint32_t tox_events_get_self_connection_status_size(const Tox_Events *events)
{
if (events == nullptr) {
return 0;
}
return events->self_connection_status_size;
}
const Tox_Event_Self_Connection_Status *tox_events_get_self_connection_status(const Tox_Events *events, uint32_t index)
{
assert(index < events->self_connection_status_size);
assert(events->self_connection_status != nullptr);
return &events->self_connection_status[index];
}
bool tox_events_pack_self_connection_status(const Tox_Events *events, Bin_Pack *bp)
{
const uint32_t size = tox_events_get_self_connection_status_size(events);
for (uint32_t i = 0; i < size; ++i) {
if (!tox_event_self_connection_status_pack(tox_events_get_self_connection_status(events, i), bp)) {
return false;
}
}
return true;
}
bool tox_events_unpack_self_connection_status(Tox_Events *events, Bin_Unpack *bu)
{
Tox_Event_Self_Connection_Status *event = tox_events_add_self_connection_status(events);
if (event == nullptr) {
return false;
}
return tox_event_self_connection_status_unpack(event, bu);
}
/*****************************************************
*
* :: event handler
*
*****************************************************/
void tox_events_handle_self_connection_status(Tox *tox, Tox_Connection connection_status, void *user_data)
{
Tox_Events_State *state = tox_events_alloc(user_data);
assert(state != nullptr);
if (state->events == nullptr) {
return;
}
Tox_Event_Self_Connection_Status *self_connection_status = tox_events_add_self_connection_status(state->events);
if (self_connection_status == nullptr) {
state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;
return;
}
tox_event_self_connection_status_set_connection_status(self_connection_status, connection_status);
}

View File

@ -0,0 +1,395 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2019-2022 The TokTok team.
*/
#include "forwarding.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "DHT.h"
#include "ccompat.h"
#include "timed_auth.h"
struct Forwarding {
const Logger *log;
const Random *rng;
DHT *dht;
const Mono_Time *mono_time;
Networking_Core *net;
uint8_t hmac_key[CRYPTO_HMAC_KEY_SIZE];
forward_reply_cb *forward_reply_callback;
void *forward_reply_callback_object;
forwarded_request_cb *forwarded_request_callback;
void *forwarded_request_callback_object;
forwarded_response_cb *forwarded_response_callback;
void *forwarded_response_callback_object;
};
DHT *forwarding_get_dht(Forwarding *forwarding)
{
return forwarding->dht;
}
#define SENDBACK_TIMEOUT 3600
bool send_forward_request(Networking_Core *net, const IP_Port *forwarder,
const uint8_t *chain_keys, uint16_t chain_length,
const uint8_t *data, uint16_t data_length)
{
if (chain_length == 0 || chain_length > MAX_FORWARD_CHAIN_LENGTH
|| data_length > MAX_FORWARD_DATA_SIZE) {
return false;
}
const uint16_t len = forward_chain_packet_size(chain_length, data_length);
VLA(uint8_t, packet, len);
return create_forward_chain_packet(chain_keys, chain_length, data, data_length, packet)
&& sendpacket(net, forwarder, packet, len) == len;
}
uint16_t forward_chain_packet_size(uint16_t chain_length, uint16_t data_length)
{
return chain_length * (1 + CRYPTO_PUBLIC_KEY_SIZE) + data_length;
}
bool create_forward_chain_packet(const uint8_t *chain_keys, uint16_t chain_length,
const uint8_t *data, uint16_t data_length,
uint8_t *packet)
{
if (chain_length == 0 || chain_length > MAX_FORWARD_CHAIN_LENGTH
|| data_length > MAX_FORWARD_DATA_SIZE) {
return false;
}
uint16_t offset = 0;
for (uint16_t j = 0; j < chain_length; ++j) {
packet[offset] = NET_PACKET_FORWARD_REQUEST;
++offset;
memcpy(packet + offset, chain_keys + j * CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_PUBLIC_KEY_SIZE);
offset += CRYPTO_PUBLIC_KEY_SIZE;
}
memcpy(packet + offset, data, data_length);
return true;
}
non_null()
static uint16_t forwarding_packet_length(uint16_t sendback_data_len, uint16_t data_length)
{
const uint16_t sendback_len = sendback_data_len == 0 ? 0 : TIMED_AUTH_SIZE + sendback_data_len;
return 1 + 1 + sendback_len + data_length;
}
non_null(1, 4, 6) nullable(2)
static bool create_forwarding_packet(const Forwarding *forwarding,
const uint8_t *sendback_data, uint16_t sendback_data_len,
const uint8_t *data, uint16_t length,
uint8_t *packet)
{
packet[0] = NET_PACKET_FORWARDING;
if (sendback_data_len == 0) {
packet[1] = 0;
memcpy(packet + 1 + 1, data, length);
} else {
const uint16_t sendback_len = TIMED_AUTH_SIZE + sendback_data_len;
if (sendback_len > MAX_SENDBACK_SIZE) {
return false;
}
packet[1] = sendback_len;
generate_timed_auth(forwarding->mono_time, SENDBACK_TIMEOUT, forwarding->hmac_key, sendback_data,
sendback_data_len, packet + 1 + 1);
if (sendback_data_len != 0) {
assert(sendback_data != nullptr);
memcpy(packet + 1 + 1 + TIMED_AUTH_SIZE, sendback_data, sendback_data_len);
}
memcpy(packet + 1 + 1 + sendback_len, data, length);
}
return true;
}
bool send_forwarding(const Forwarding *forwarding, const IP_Port *dest,
const uint8_t *sendback_data, uint16_t sendback_data_len,
const uint8_t *data, uint16_t length)
{
if (length > MAX_FORWARD_DATA_SIZE) {
return false;
}
const uint16_t len = forwarding_packet_length(sendback_data_len, length);
VLA(uint8_t, packet, len);
create_forwarding_packet(forwarding, sendback_data, sendback_data_len, data, length, packet);
return sendpacket(forwarding->net, dest, packet, len) == len;
}
#define FORWARD_REQUEST_MIN_PACKET_SIZE (1 + CRYPTO_PUBLIC_KEY_SIZE)
non_null(1) nullable(2, 4)
static bool handle_forward_request_dht(const Forwarding *forwarding,
const uint8_t *sendback_data, uint16_t sendback_data_len,
const uint8_t *packet, uint16_t length)
{
if (length < FORWARD_REQUEST_MIN_PACKET_SIZE) {
return false;
}
const uint8_t *const public_key = packet + 1;
const uint8_t *const forward_data = packet + (1 + CRYPTO_PUBLIC_KEY_SIZE);
const uint16_t forward_data_len = length - (1 + CRYPTO_PUBLIC_KEY_SIZE);
if (TIMED_AUTH_SIZE + sendback_data_len > MAX_SENDBACK_SIZE ||
forward_data_len > MAX_FORWARD_DATA_SIZE) {
return false;
}
const uint16_t len = forwarding_packet_length(sendback_data_len, forward_data_len);
VLA(uint8_t, forwarding_packet, len);
create_forwarding_packet(forwarding, sendback_data, sendback_data_len, forward_data, forward_data_len,
forwarding_packet);
return route_packet(forwarding->dht, public_key, forwarding_packet, len) == len;
}
non_null(1, 2) nullable(3, 5)
static int handle_forward_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
const Forwarding *forwarding = (const Forwarding *)object;
uint8_t sendback_data[1 + MAX_PACKED_IPPORT_SIZE];
sendback_data[0] = SENDBACK_IPPORT;
const int ipport_length = pack_ip_port(forwarding->log, sendback_data + 1, MAX_PACKED_IPPORT_SIZE, source);
if (ipport_length == -1) {
return 1;
}
return handle_forward_request_dht(forwarding, sendback_data, 1 + ipport_length, packet, length) ? 0 : 1;
}
#define MIN_NONEMPTY_SENDBACK_SIZE TIMED_AUTH_SIZE
#define FORWARD_REPLY_MIN_PACKET_SIZE (1 + 1 + MIN_NONEMPTY_SENDBACK_SIZE)
non_null(1, 2) nullable(3, 5)
static int handle_forward_reply(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
const Forwarding *forwarding = (const Forwarding *)object;
if (length < FORWARD_REPLY_MIN_PACKET_SIZE) {
return 1;
}
const uint8_t sendback_len = packet[1];
const uint8_t *const sendback_auth = packet + 1 + 1;
const uint8_t *const sendback_data = sendback_auth + TIMED_AUTH_SIZE;
if (sendback_len > MAX_SENDBACK_SIZE) {
/* value 0xff is reserved for possible future expansion */
return 1;
}
if (sendback_len < TIMED_AUTH_SIZE + 1) {
return 1;
}
const uint16_t sendback_data_len = sendback_len - TIMED_AUTH_SIZE;
if (length < 1 + 1 + sendback_len) {
return 1;
}
const uint8_t *const to_forward = packet + (1 + 1 + sendback_len);
const uint16_t to_forward_len = length - (1 + 1 + sendback_len);
if (!check_timed_auth(forwarding->mono_time, SENDBACK_TIMEOUT, forwarding->hmac_key, sendback_data, sendback_data_len,
sendback_auth)) {
return 1;
}
if (sendback_data[0] == SENDBACK_IPPORT) {
IP_Port dest;
if (unpack_ip_port(&dest, sendback_data + 1, sendback_data_len - 1, false)
!= sendback_data_len - 1) {
return 1;
}
return send_forwarding(forwarding, &dest, nullptr, 0, to_forward, to_forward_len) ? 0 : 1;
}
if (sendback_data[0] == SENDBACK_FORWARD) {
IP_Port forwarder;
const int ipport_length = unpack_ip_port(&forwarder, sendback_data + 1, sendback_data_len - 1, false);
if (ipport_length == -1) {
return 1;
}
const uint8_t *const forward_sendback = sendback_data + (1 + ipport_length);
const uint16_t forward_sendback_len = sendback_data_len - (1 + ipport_length);
return forward_reply(forwarding->net, &forwarder, forward_sendback, forward_sendback_len, to_forward,
to_forward_len) ? 0 : 1;
}
if (forwarding->forward_reply_callback == nullptr) {
return 1;
}
return forwarding->forward_reply_callback(forwarding->forward_reply_callback_object,
sendback_data, sendback_data_len,
to_forward, to_forward_len) ? 0 : 1;
}
#define FORWARDING_MIN_PACKET_SIZE (1 + 1)
non_null(1, 2) nullable(3, 5)
static int handle_forwarding(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
const Forwarding *forwarding = (const Forwarding *)object;
if (length < FORWARDING_MIN_PACKET_SIZE) {
return 1;
}
const uint8_t sendback_len = packet[1];
if (length < 1 + 1 + sendback_len) {
return 1;
}
const uint8_t *const sendback = packet + 1 + 1;
const uint8_t *const forwarded = sendback + sendback_len;
const uint16_t forwarded_len = length - (1 + 1 + sendback_len);
if (forwarded_len >= 1 && forwarded[0] == NET_PACKET_FORWARD_REQUEST) {
VLA(uint8_t, sendback_data, 1 + MAX_PACKED_IPPORT_SIZE + sendback_len);
sendback_data[0] = SENDBACK_FORWARD;
const int ipport_length = pack_ip_port(forwarding->log, sendback_data + 1, MAX_PACKED_IPPORT_SIZE, source);
if (ipport_length == -1) {
return 1;
}
memcpy(sendback_data + 1 + ipport_length, sendback, sendback_len);
return handle_forward_request_dht(forwarding, sendback_data, 1 + ipport_length + sendback_len, forwarded,
forwarded_len) ? 0 : 1;
}
if (sendback_len > 0) {
if (forwarding->forwarded_request_callback == nullptr) {
return 1;
}
forwarding->forwarded_request_callback(forwarding->forwarded_request_callback_object,
source, sendback, sendback_len,
forwarded, forwarded_len, userdata);
return 0;
} else {
if (forwarding->forwarded_response_callback == nullptr) {
return 1;
}
forwarding->forwarded_response_callback(forwarding->forwarded_response_callback_object,
forwarded, forwarded_len, userdata);
return 0;
}
}
bool forward_reply(Networking_Core *net, const IP_Port *forwarder,
const uint8_t *sendback, uint16_t sendback_length,
const uint8_t *data, uint16_t length)
{
if (sendback_length > MAX_SENDBACK_SIZE ||
length > MAX_FORWARD_DATA_SIZE) {
return false;
}
const uint16_t len = 1 + 1 + sendback_length + length;
VLA(uint8_t, packet, len);
packet[0] = NET_PACKET_FORWARD_REPLY;
packet[1] = (uint8_t) sendback_length;
memcpy(packet + 1 + 1, sendback, sendback_length);
memcpy(packet + 1 + 1 + sendback_length, data, length);
return sendpacket(net, forwarder, packet, len) == len;
}
void set_callback_forwarded_request(Forwarding *forwarding, forwarded_request_cb *function, void *object)
{
forwarding->forwarded_request_callback = function;
forwarding->forwarded_request_callback_object = object;
}
void set_callback_forwarded_response(Forwarding *forwarding, forwarded_response_cb *function, void *object)
{
forwarding->forwarded_response_callback = function;
forwarding->forwarded_response_callback_object = object;
}
void set_callback_forward_reply(Forwarding *forwarding, forward_reply_cb *function, void *object)
{
forwarding->forward_reply_callback = function;
forwarding->forward_reply_callback_object = object;
}
Forwarding *new_forwarding(const Logger *log, const Random *rng, const Mono_Time *mono_time, DHT *dht)
{
if (log == nullptr || mono_time == nullptr || dht == nullptr) {
return nullptr;
}
Forwarding *forwarding = (Forwarding *)calloc(1, sizeof(Forwarding));
if (forwarding == nullptr) {
return nullptr;
}
forwarding->log = log;
forwarding->rng = rng;
forwarding->mono_time = mono_time;
forwarding->dht = dht;
forwarding->net = dht_get_net(dht);
networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REQUEST, &handle_forward_request, forwarding);
networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REPLY, &handle_forward_reply, forwarding);
networking_registerhandler(forwarding->net, NET_PACKET_FORWARDING, &handle_forwarding, forwarding);
new_hmac_key(forwarding->rng, forwarding->hmac_key);
return forwarding;
}
void kill_forwarding(Forwarding *forwarding)
{
if (forwarding == nullptr) {
return;
}
networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REQUEST, nullptr, nullptr);
networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REPLY, nullptr, nullptr);
networking_registerhandler(forwarding->net, NET_PACKET_FORWARDING, nullptr, nullptr);
crypto_memzero(forwarding->hmac_key, CRYPTO_HMAC_KEY_SIZE);
free(forwarding);
}

View File

@ -0,0 +1,125 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2019-2022 The TokTok team.
*/
#ifndef C_TOXCORE_TOXCORE_FORWARDING_H
#define C_TOXCORE_TOXCORE_FORWARDING_H
#include "DHT.h"
#include "network.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SENDBACK_IPPORT 0
#define SENDBACK_FORWARD 1
#define SENDBACK_TCP 2
#define MAX_SENDBACK_SIZE (0xff - 1)
#define MAX_FORWARD_DATA_SIZE (MAX_UDP_PACKET_SIZE - (1 + 1 + MAX_SENDBACK_SIZE))
#define MAX_FORWARD_CHAIN_LENGTH 4
#define MAX_PACKED_IPPORT_SIZE (1 + SIZE_IP6 + sizeof(uint16_t))
typedef struct Forwarding Forwarding;
non_null()
DHT *forwarding_get_dht(Forwarding *forwarding);
/**
* @brief Send data to forwarder for forwarding via chain of dht nodes.
* Destination is last key in the chain.
*
* @param data Must be of length at most MAX_FORWARD_DATA_SIZE.
* @param chain_length Number of intermediate nodes in chain.
* Must be at least 1 and at most MAX_FORWARD_CHAIN_LENGTH.
* @param chain_keys Public keys of chain nodes. Must be of length
* `chain_length * CRYPTO_PUBLIC_KEY_SIZE`.
*
* @return true on success, false otherwise.
*/
non_null()
bool send_forward_request(Networking_Core *net, const IP_Port *forwarder,
const uint8_t *chain_keys, uint16_t chain_length,
const uint8_t *data, uint16_t data_length);
/** Returns size of packet written by create_forward_chain_packet. */
uint16_t forward_chain_packet_size(uint16_t chain_length, uint16_t data_length);
/**
* @brief Create forward request packet for forwarding data via chain of dht nodes.
* Destination is last key in the chain.
*
* @param data Must be of length at most MAX_FORWARD_DATA_SIZE.
* @param chain_length Number of intermediate nodes in chain.
* Must be at least 1 and at most MAX_FORWARD_CHAIN_LENGTH.
* @param chain_keys Public keys of chain nodes. Must be of length
* `chain_length * CRYPTO_PUBLIC_KEY_SIZE`.
* @param packet Must be of size at least
* `forward_chain_packet_size(chain_length, data_length)` bytes.
*
* @return true on success, false otherwise.
*/
non_null()
bool create_forward_chain_packet(const uint8_t *chain_keys, uint16_t chain_length,
const uint8_t *data, uint16_t data_length,
uint8_t *packet);
/**
* @brief Send reply to forwarded packet via forwarder.
* @param sendback Must be of size at most MAX_SENDBACK_SIZE.
* @param data Must be of size at most MAX_FORWARD_DATA_SIZE.
*
* @return true on success, false otherwise.
*/
non_null()
bool forward_reply(Networking_Core *net, const IP_Port *forwarder,
const uint8_t *sendback, uint16_t sendback_length,
const uint8_t *data, uint16_t length);
/**
* @brief Set callback to handle a forwarded request.
* To reply to the packet, callback should use `forward_reply()` to send a reply
* forwarded via forwarder, passing the provided sendback.
*/
typedef void forwarded_request_cb(void *object, const IP_Port *forwarder, const uint8_t *sendback,
uint16_t sendback_length, const uint8_t *data,
uint16_t length, void *userdata);
non_null(1) nullable(2, 3)
void set_callback_forwarded_request(Forwarding *forwarding, forwarded_request_cb *function, void *object);
/** @brief Set callback to handle a forwarded response. */
typedef void forwarded_response_cb(void *object, const uint8_t *data, uint16_t length, void *userdata);
non_null(1) nullable(2, 3)
void set_callback_forwarded_response(Forwarding *forwarding, forwarded_response_cb *function, void *object);
/** @brief Send forwarding packet to dest with given sendback data and data. */
non_null(1, 2, 5) nullable(3)
bool send_forwarding(const Forwarding *forwarding, const IP_Port *dest,
const uint8_t *sendback_data, uint16_t sendback_data_len,
const uint8_t *data, uint16_t length);
typedef bool forward_reply_cb(void *object, const uint8_t *sendback_data, uint16_t sendback_data_len,
const uint8_t *data, uint16_t length);
/**
* @brief Set callback to handle a forward reply with an otherwise unhandled
* sendback.
*/
non_null(1) nullable(2, 3)
void set_callback_forward_reply(Forwarding *forwarding, forward_reply_cb *function, void *object);
non_null()
Forwarding *new_forwarding(const Logger *log, const Random *rng, const Mono_Time *mono_time, DHT *dht);
nullable(1)
void kill_forwarding(Forwarding *forwarding);
#ifdef __cplusplus
} // extern "C"
#endif
#endif

View File

@ -0,0 +1,50 @@
#include "forwarding.h"
#include <cassert>
#include <memory>
#include "../testing/fuzzing/fuzz_support.h"
#include "../testing/fuzzing/fuzz_tox.h"
namespace {
void TestSendForwardRequest(Fuzz_Data &input)
{
const Network *ns = system_network(); // TODO(iphydf): fuzz_network
assert(ns != nullptr);
with<Logger>{} >> with<Networking_Core>{input, ns} >> [&input](Ptr<Networking_Core> net) {
with<IP_Port>{input} >> [net = std::move(net), &input](const IP_Port &forwarder) {
CONSUME1_OR_RETURN(const uint16_t chain_length, input);
const uint16_t chain_keys_size = chain_length * CRYPTO_PUBLIC_KEY_SIZE;
CONSUME_OR_RETURN(const uint8_t *chain_keys, input, chain_keys_size);
send_forward_request(
net.get(), &forwarder, chain_keys, chain_length, input.data, input.size);
};
};
}
void TestForwardReply(Fuzz_Data &input)
{
const Network *ns = system_network(); // TODO(iphydf): fuzz_network
assert(ns != nullptr);
with<Logger>{} >> with<Networking_Core>{input, ns} >> [&input](Ptr<Networking_Core> net) {
with<IP_Port>{input} >> [net = std::move(net), &input](const IP_Port &forwarder) {
CONSUME1_OR_RETURN(const uint16_t sendback_length, input);
CONSUME_OR_RETURN(const uint8_t *sendback, input, sendback_length);
forward_reply(net.get(), &forwarder, sendback, sendback_length, input.data, input.size);
};
};
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
fuzz_select_target(data, size, TestSendForwardRequest, TestForwardReply);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,176 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2014 Tox project.
*/
/**
* Connection to friends.
*/
#ifndef C_TOXCORE_TOXCORE_FRIEND_CONNECTION_H
#define C_TOXCORE_TOXCORE_FRIEND_CONNECTION_H
#include "DHT.h"
#include "LAN_discovery.h"
#include "net_crypto.h"
#include "onion_client.h"
#define MAX_FRIEND_CONNECTION_CALLBACKS 2
#define MESSENGER_CALLBACK_INDEX 0
#define GROUPCHAT_CALLBACK_INDEX 1
#define PACKET_ID_ALIVE 16
#define PACKET_ID_SHARE_RELAYS 17
#define PACKET_ID_FRIEND_REQUESTS 18
/** Interval between the sending of ping packets. */
#define FRIEND_PING_INTERVAL 8
/** If no packets are received from friend in this time interval, kill the connection. */
#define FRIEND_CONNECTION_TIMEOUT (FRIEND_PING_INTERVAL * 4)
/** Time before friend is removed from the DHT after last hearing about him. */
#define FRIEND_DHT_TIMEOUT BAD_NODE_TIMEOUT
#define FRIEND_MAX_STORED_TCP_RELAYS (MAX_FRIEND_TCP_CONNECTIONS * 4)
/** Max number of tcp relays sent to friends */
#define MAX_SHARED_RELAYS RECOMMENDED_FRIEND_TCP_CONNECTIONS
/** How often we share our TCP relays with each friend connection */
#define SHARE_RELAYS_INTERVAL (60 * 2)
typedef enum Friendconn_Status {
FRIENDCONN_STATUS_NONE,
FRIENDCONN_STATUS_CONNECTING,
FRIENDCONN_STATUS_CONNECTED,
} Friendconn_Status;
typedef struct Friend_Connections Friend_Connections;
non_null() Net_Crypto *friendconn_net_crypto(const Friend_Connections *fr_c);
/** @return friendcon_id corresponding to the real public key on success.
* @retval -1 on failure.
*/
non_null()
int getfriend_conn_id_pk(const Friend_Connections *fr_c, const uint8_t *real_pk);
/** @brief Increases lock_count for the connection with friendcon_id by 1.
*
* @retval 0 on success.
* @retval -1 on failure.
*/
non_null()
int friend_connection_lock(const Friend_Connections *fr_c, int friendcon_id);
/**
* @retval FRIENDCONN_STATUS_CONNECTED if the friend is connected.
* @retval FRIENDCONN_STATUS_CONNECTING if the friend isn't connected.
* @retval FRIENDCONN_STATUS_NONE on failure.
*/
non_null()
unsigned int friend_con_connected(const Friend_Connections *fr_c, int friendcon_id);
/** @brief Copy public keys associated to friendcon_id.
*
* @retval 0 on success.
* @retval -1 on failure.
*/
non_null(3) nullable(1, 2)
int get_friendcon_public_keys(uint8_t *real_pk, uint8_t *dht_temp_pk, const Friend_Connections *fr_c, int friendcon_id);
/** Set temp dht key for connection. */
non_null()
void set_dht_temp_pk(Friend_Connections *fr_c, int friendcon_id, const uint8_t *dht_temp_pk, void *userdata);
typedef int global_status_cb(void *object, int id, bool status, void *userdata);
typedef int fc_status_cb(void *object, int id, bool status, void *userdata);
typedef int fc_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata);
typedef int fc_lossy_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata);
/** Set global status callback for friend connections. */
non_null(1) nullable(2, 3)
void set_global_status_callback(Friend_Connections *fr_c, global_status_cb *global_status_callback, void *object);
/** @brief Set the callbacks for the friend connection.
* @param index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we
* want the callback to set in the array.
*
* @retval 0 on success.
* @retval -1 on failure
*/
non_null(1) nullable(4, 5, 6, 7)
int friend_connection_callbacks(const Friend_Connections *fr_c, int friendcon_id, unsigned int index,
fc_status_cb *status_callback,
fc_data_cb *data_callback,
fc_lossy_data_cb *lossy_data_callback,
void *object, int number);
/** @brief return the crypt_connection_id for the connection.
*
* @return crypt_connection_id on success.
* @retval -1 on failure.
*/
non_null()
int friend_connection_crypt_connection_id(const Friend_Connections *fr_c, int friendcon_id);
/** @brief Create a new friend connection.
* If one to that real public key already exists, increase lock count and return it.
*
* @retval -1 on failure.
* @return connection id on success.
*/
non_null()
int new_friend_connection(Friend_Connections *fr_c, const uint8_t *real_public_key);
/** @brief Kill a friend connection.
*
* @retval -1 on failure.
* @retval 0 on success.
*/
non_null()
int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id);
/** @brief Send a Friend request packet.
*
* @retval -1 if failure.
* @retval 0 if it sent the friend request directly to the friend.
* @return the number of peers it was routed through if it did not send it directly.
*/
non_null()
int send_friend_request_packet(
Friend_Connections *fr_c, int friendcon_id, uint32_t nospam_num, const uint8_t *data, uint16_t length);
typedef int fr_request_cb(
void *object, const uint8_t *source_pubkey, const uint8_t *data, uint16_t len, void *userdata);
/** @brief Set friend request callback.
*
* This function will be called every time a friend request packet is received.
*/
non_null()
void set_friend_request_callback(Friend_Connections *fr_c, fr_request_cb *fr_request_callback, void *object);
/** Create new friend_connections instance. */
non_null()
Friend_Connections *new_friend_connections(
const Logger *logger, const Mono_Time *mono_time, const Network *ns,
Onion_Client *onion_c, bool local_discovery_enabled);
/** main friend_connections loop. */
non_null()
void do_friend_connections(Friend_Connections *fr_c, void *userdata);
/** Free everything related with friend_connections. */
nullable(1)
void kill_friend_connections(Friend_Connections *fr_c);
typedef struct Friend_Conn Friend_Conn;
non_null() Friend_Conn *get_conn(const Friend_Connections *fr_c, int friendcon_id);
non_null() int friend_conn_get_onion_friendnum(const Friend_Conn *fc);
non_null() const IP_Port *friend_conn_get_dht_ip_port(const Friend_Conn *fc);
#endif

View File

@ -0,0 +1,14 @@
#include "friend_connection.h"
#include <gtest/gtest.h>
namespace {
// TODO(Jfreegman) make this useful or remove it after NGC is merged
TEST(friend_connection, NullTest)
{
(void)friend_conn_get_onion_friendnum;
(void)friend_conn_get_dht_ip_port;
}
} // namespace

View File

@ -0,0 +1,171 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2013 Tox project.
*/
/**
* Handle friend requests.
*/
#include "friend_requests.h"
#include <stdlib.h>
#include <string.h>
#include "ccompat.h"
#include "util.h"
/**
* NOTE: The following is just a temporary fix for the multiple friend requests received at the same time problem.
* TODO(irungentoo): Make this better (This will most likely tie in with the way we will handle spam).
*/
#define MAX_RECEIVED_STORED 32
struct Received_Requests {
uint8_t requests[MAX_RECEIVED_STORED][CRYPTO_PUBLIC_KEY_SIZE];
uint16_t requests_index;
};
struct Friend_Requests {
uint32_t nospam;
fr_friend_request_cb *handle_friendrequest;
uint8_t handle_friendrequest_isset;
void *handle_friendrequest_object;
filter_function_cb *filter_function;
void *filter_function_userdata;
struct Received_Requests received;
};
/** Set and get the nospam variable used to prevent one type of friend request spam. */
void set_nospam(Friend_Requests *fr, uint32_t num)
{
fr->nospam = num;
}
uint32_t get_nospam(const Friend_Requests *fr)
{
return fr->nospam;
}
/** Set the function that will be executed when a friend request for us is received. */
void callback_friendrequest(Friend_Requests *fr, fr_friend_request_cb *function, void *object)
{
fr->handle_friendrequest = function;
fr->handle_friendrequest_isset = 1;
fr->handle_friendrequest_object = object;
}
/** @brief Set the function used to check if a friend request should be displayed to the user or not.
* It must return 0 if the request is ok (anything else if it is bad).
*/
void set_filter_function(Friend_Requests *fr, filter_function_cb *function, void *userdata)
{
fr->filter_function = function;
fr->filter_function_userdata = userdata;
}
/** Add to list of received friend requests. */
non_null()
static void addto_receivedlist(Friend_Requests *fr, const uint8_t *real_pk)
{
if (fr->received.requests_index >= MAX_RECEIVED_STORED) {
fr->received.requests_index = 0;
}
pk_copy(fr->received.requests[fr->received.requests_index], real_pk);
++fr->received.requests_index;
}
/** @brief Check if a friend request was already received.
*
* @retval false if it did not.
* @retval true if it did.
*/
non_null()
static bool request_received(const Friend_Requests *fr, const uint8_t *real_pk)
{
for (uint32_t i = 0; i < MAX_RECEIVED_STORED; ++i) {
if (pk_equal(fr->received.requests[i], real_pk)) {
return true;
}
}
return false;
}
/** @brief Remove real_pk from received_requests list.
*
* @retval 0 if it removed it successfully.
* @retval -1 if it didn't find it.
*/
int remove_request_received(Friend_Requests *fr, const uint8_t *real_pk)
{
for (uint32_t i = 0; i < MAX_RECEIVED_STORED; ++i) {
if (pk_equal(fr->received.requests[i], real_pk)) {
crypto_memzero(fr->received.requests[i], CRYPTO_PUBLIC_KEY_SIZE);
return 0;
}
}
return -1;
}
non_null()
static int friendreq_handlepacket(void *object, const uint8_t *source_pubkey, const uint8_t *packet, uint16_t length,
void *userdata)
{
Friend_Requests *const fr = (Friend_Requests *)object;
if (length <= 1 + sizeof(fr->nospam) || length > ONION_CLIENT_MAX_DATA_SIZE) {
return 1;
}
++packet;
--length;
if (fr->handle_friendrequest_isset == 0) {
return 1;
}
if (request_received(fr, source_pubkey)) {
return 1;
}
if (memcmp(packet, &fr->nospam, sizeof(fr->nospam)) != 0) {
return 1;
}
if (fr->filter_function != nullptr) {
if (fr->filter_function(source_pubkey, fr->filter_function_userdata) != 0) {
return 1;
}
}
addto_receivedlist(fr, source_pubkey);
const uint32_t message_len = length - sizeof(fr->nospam);
VLA(uint8_t, message, message_len + 1);
memcpy(message, packet + sizeof(fr->nospam), message_len);
message[SIZEOF_VLA(message) - 1] = 0; /* Be sure the message is null terminated. */
fr->handle_friendrequest(fr->handle_friendrequest_object, source_pubkey, message, message_len, userdata);
return 0;
}
void friendreq_init(Friend_Requests *fr, Friend_Connections *fr_c)
{
set_friend_request_callback(fr_c, &friendreq_handlepacket, fr);
}
Friend_Requests *friendreq_new(void)
{
return (Friend_Requests *)calloc(1, sizeof(Friend_Requests));
}
void friendreq_kill(Friend_Requests *fr)
{
free(fr);
}

View File

@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2014 Tox project.
*/
/**
* Handle friend requests.
*/
#ifndef C_TOXCORE_TOXCORE_FRIEND_REQUESTS_H
#define C_TOXCORE_TOXCORE_FRIEND_REQUESTS_H
#include "friend_connection.h"
#define MAX_FRIEND_REQUEST_DATA_SIZE (ONION_CLIENT_MAX_DATA_SIZE - (1 + sizeof(uint32_t)))
typedef struct Friend_Requests Friend_Requests;
/** Set and get the nospam variable used to prevent one type of friend request spam. */
non_null() void set_nospam(Friend_Requests *fr, uint32_t num);
non_null() uint32_t get_nospam(const Friend_Requests *fr);
/** @brief Remove real_pk from received_requests list.
*
* @retval 0 if it removed it successfully.
* @retval -1 if it didn't find it.
*/
non_null()
int remove_request_received(Friend_Requests *fr, const uint8_t *real_pk);
typedef void fr_friend_request_cb(void *object, const uint8_t *public_key, const uint8_t *message, size_t length,
void *user_data);
/** Set the function that will be executed when a friend request for us is received. */
non_null()
void callback_friendrequest(Friend_Requests *fr, fr_friend_request_cb *function, void *object);
typedef int filter_function_cb(const uint8_t *public_key, void *user_data);
/** @brief Set the function used to check if a friend request should be displayed to the user or not.
* It must return 0 if the request is ok (anything else if it is bad).
*/
non_null()
void set_filter_function(Friend_Requests *fr, filter_function_cb *function, void *userdata);
/** Sets up friendreq packet handlers. */
non_null()
void friendreq_init(Friend_Requests *fr, Friend_Connections *fr_c);
Friend_Requests *friendreq_new(void);
nullable(1)
void friendreq_kill(Friend_Requests *fr);
#endif

3867
external/toxcore/c-toxcore/toxcore/group.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,398 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2014 Tox project.
*/
/**
* Slightly better groupchats implementation.
*/
#ifndef C_TOXCORE_TOXCORE_GROUP_H
#define C_TOXCORE_TOXCORE_GROUP_H
#include "Messenger.h"
typedef enum Groupchat_Type {
GROUPCHAT_TYPE_TEXT,
GROUPCHAT_TYPE_AV,
} Groupchat_Type;
typedef void peer_on_join_cb(void *object, uint32_t conference_number, uint32_t peer_number);
typedef void peer_on_leave_cb(void *object, uint32_t conference_number, void *peer_object);
typedef void group_on_delete_cb(void *object, uint32_t conference_number);
/** @brief Callback for group invites.
*
* data of length is what needs to be passed to `join_groupchat()`.
*/
typedef void g_conference_invite_cb(Messenger *m, uint32_t friend_number, int type, const uint8_t *cookie,
size_t length, void *user_data);
/** Callback for group connection. */
typedef void g_conference_connected_cb(Messenger *m, uint32_t conference_number, void *user_data);
/** Callback for group messages. */
typedef void g_conference_message_cb(Messenger *m, uint32_t conference_number, uint32_t peer_number, int type,
const uint8_t *message, size_t length, void *user_data);
/** Callback for peer nickname changes. */
typedef void peer_name_cb(Messenger *m, uint32_t conference_number, uint32_t peer_number, const uint8_t *name,
size_t length, void *user_data);
/** Set callback function for peer list changes. */
typedef void peer_list_changed_cb(Messenger *m, uint32_t conference_number, void *user_data);
/** @brief Callback for title changes.
*
* If peer_number == -1, then author is unknown (e.g. initial joining the group).
*/
typedef void title_cb(Messenger *m, uint32_t conference_number, uint32_t peer_number, const uint8_t *title,
size_t length, void *user_data);
/** @brief Callback for lossy packets.
*
* NOTE: Handler must return 0 if packet is to be relayed, -1 if the packet should not be relayed.
*/
typedef int lossy_packet_cb(void *object, uint32_t conference_number, uint32_t peer_number, void *peer_object,
const uint8_t *packet, uint16_t length);
typedef struct Group_Chats Group_Chats;
non_null()
const Mono_Time *g_mono_time(const Group_Chats *g_c);
/** Set the callback for group invites. */
non_null()
void g_callback_group_invite(Group_Chats *g_c, g_conference_invite_cb *function);
/** Set the callback for group connection. */
non_null()
void g_callback_group_connected(Group_Chats *g_c, g_conference_connected_cb *function);
/** Set the callback for group messages. */
non_null()
void g_callback_group_message(Group_Chats *g_c, g_conference_message_cb *function);
/** Set callback function for title changes. */
non_null()
void g_callback_group_title(Group_Chats *g_c, title_cb *function);
/** @brief Set callback function for peer nickname changes.
*
* It gets called every time a peer changes their nickname.
*/
non_null()
void g_callback_peer_name(Group_Chats *g_c, peer_name_cb *function);
/** @brief Set callback function for peer list changes.
*
* It gets called every time the name list changes(new peer, deleted peer)
*/
non_null()
void g_callback_peer_list_changed(Group_Chats *g_c, peer_list_changed_cb *function);
/** @brief Creates a new groupchat and puts it in the chats array.
*
* @param rng Random number generator used for generating the group ID.
* @param type is one of `GROUPCHAT_TYPE_*`
*
* @return group number on success.
* @retval -1 on failure.
*/
non_null()
int add_groupchat(Group_Chats *g_c, const Random *rng, uint8_t type);
/** @brief Delete a groupchat from the chats array, informing the group first as
* appropriate.
*
* @retval true on success.
* @retval false if groupnumber is invalid.
*/
non_null()
bool del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently);
/**
* @brief Copy the public key of (frozen, if frozen is true) peernumber who is in
* groupnumber to pk.
*
* @param pk must be CRYPTO_PUBLIC_KEY_SIZE long.
*
* @retval 0 on success
* @retval -1 if groupnumber is invalid.
* @retval -2 if peernumber is invalid.
*/
non_null()
int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *pk, bool frozen);
/**
* @brief Return the size of (frozen, if frozen is true) peernumber's name.
*
* @retval -1 if groupnumber is invalid.
* @retval -2 if peernumber is invalid.
*/
non_null()
int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, bool frozen);
/**
* @brief Copy the name of (frozen, if frozen is true) peernumber who is in
* groupnumber to name.
*
* @param name must be at least MAX_NAME_LENGTH long.
*
* @return length of name if success
* @retval -1 if groupnumber is invalid.
* @retval -2 if peernumber is invalid.
*/
non_null()
int group_peername(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *name,
bool frozen);
/**
* @brief Copy last active timestamp of frozen peernumber who is in groupnumber to
* last_active.
*
* @retval 0 on success.
* @retval -1 if groupnumber is invalid.
* @retval -2 if peernumber is invalid.
*/
non_null()
int group_frozen_last_active(
const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint64_t *last_active);
/** @brief Set maximum number of frozen peers.
*
* @retval 0 on success.
* @retval -1 if groupnumber is invalid.
*/
non_null()
int group_set_max_frozen(const Group_Chats *g_c, uint32_t groupnumber, uint32_t maxfrozen);
/** @brief invite friendnumber to groupnumber.
*
* @retval 0 on success.
* @retval -1 if groupnumber is invalid.
* @retval -2 if invite packet failed to send.
* @retval -3 if we are not connected to the group chat.
*/
non_null()
int invite_friend(const Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber);
/** @brief Join a group (we need to have been invited first).
*
* @param expected_type is the groupchat type we expect the chat we are joining
* to have.
*
* @return group number on success.
* @retval -1 if data length is invalid.
* @retval -2 if group is not the expected type.
* @retval -3 if friendnumber is invalid.
* @retval -4 if client is already in this group.
* @retval -5 if group instance failed to initialize.
* @retval -6 if join packet fails to send.
*/
non_null()
int join_groupchat(
Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_type, const uint8_t *data, uint16_t length);
/** @brief send a group message
* @retval 0 on success
* @see send_message_group for error codes.
*/
non_null()
int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *message, uint16_t length);
/** @brief send a group action
* @retval 0 on success
* @see send_message_group for error codes.
*/
non_null()
int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length);
/** @brief set the group's title, limited to MAX_NAME_LENGTH.
* @retval 0 on success
* @retval -1 if groupnumber is invalid.
* @retval -2 if title is too long or empty.
* @retval -3 if packet fails to send.
*/
non_null()
int group_title_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *title, uint8_t title_len);
/** @brief return the group's title size.
* @retval -1 of groupnumber is invalid.
* @retval -2 if title is too long or empty.
*/
non_null()
int group_title_get_size(const Group_Chats *g_c, uint32_t groupnumber);
/** @brief Get group title from groupnumber and put it in title.
*
* Title needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes.
*
* @return length of copied title if success.
* @retval -1 if groupnumber is invalid.
* @retval -2 if title is too long or empty.
*/
non_null()
int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title);
/**
* @return the number of (frozen, if frozen is true) peers in the group chat on success.
* @retval -1 if groupnumber is invalid.
*/
non_null()
int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber, bool frozen);
/**
* @retval 1 if the peernumber corresponds to ours.
* @retval 0 if the peernumber is not ours.
* @retval -1 if groupnumber is invalid.
* @retval -2 if peernumber is invalid.
* @retval -3 if we are not connected to the group chat.
*/
non_null()
int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber);
/** Set handlers for custom lossy packets. */
non_null()
void group_lossy_packet_registerhandler(Group_Chats *g_c, uint8_t byte, lossy_packet_cb *function);
/** @brief High level function to send custom lossy packets.
*
* @retval -1 on failure.
* @retval 0 on success.
*/
non_null()
int send_group_lossy_packet(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length);
/**
* @brief Return the number of chats in the instance m.
*
* You should use this to determine how much memory to allocate
* for copy_chatlist.
*/
non_null()
uint32_t count_chatlist(const Group_Chats *g_c);
/** @brief Copy a list of valid chat IDs into the array out_list.
*
* If out_list is NULL, returns 0.
* Otherwise, returns the number of elements copied.
* If the array was too small, the contents
* of out_list will be truncated to list_size.
*/
non_null()
uint32_t copy_chatlist(const Group_Chats *g_c, uint32_t *out_list, uint32_t list_size);
/** @brief return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is.
*
* @retval -1 on failure.
* @return type on success.
*/
non_null()
int group_get_type(const Group_Chats *g_c, uint32_t groupnumber);
/** @brief Copies the unique id of `group_chat[groupnumber]` into `id`.
*
* @retval false on failure.
* @retval true on success.
*/
non_null()
bool conference_get_id(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *id);
non_null() int32_t conference_by_id(const Group_Chats *g_c, const uint8_t *id);
/** Send current name (set in messenger) to all online groups. */
non_null()
void send_name_all_groups(const Group_Chats *g_c);
/** @brief Set the object that is tied to the group chat.
*
* @retval 0 on success.
* @retval -1 on failure
*/
non_null(1) nullable(3)
int group_set_object(const Group_Chats *g_c, uint32_t groupnumber, void *object);
/** @brief Set the object that is tied to the group peer.
*
* @retval 0 on success.
* @retval -1 on failure
*/
non_null(1) nullable(4)
int group_peer_set_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, void *object);
/** @brief Return the object tied to the group chat previously set by group_set_object.
*
* @retval NULL on failure.
* @return object on success.
*/
non_null()
void *group_get_object(const Group_Chats *g_c, uint32_t groupnumber);
/** @brief Return the object tied to the group chat peer previously set by group_peer_set_object.
*
* @retval NULL on failure.
* @return object on success.
*/
non_null()
void *group_peer_get_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber);
/** @brief Set a function to be called when a new peer joins a group chat.
*
* @retval 0 on success.
* @retval -1 on failure.
*/
non_null(1) nullable(3)
int callback_groupchat_peer_new(const Group_Chats *g_c, uint32_t groupnumber, peer_on_join_cb *function);
/** @brief Set a function to be called when a peer leaves a group chat.
*
* @retval 0 on success.
* @retval -1 on failure.
*/
non_null(1) nullable(3)
int callback_groupchat_peer_delete(const Group_Chats *g_c, uint32_t groupnumber, peer_on_leave_cb *function);
/** @brief Set a function to be called when the group chat is deleted.
*
* @retval 0 on success.
* @retval -1 on failure.
*/
non_null(1) nullable(3)
int callback_groupchat_delete(const Group_Chats *g_c, uint32_t groupnumber, group_on_delete_cb *function);
/** Return size of the conferences data (for saving). */
non_null()
uint32_t conferences_size(const Group_Chats *g_c);
/** Save the conferences in data (must be allocated memory of size at least `conferences_size()`). */
non_null()
uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data);
/**
* Load a state section.
*
* @param data Data to load
* @param length Length of data
* @param type Type of section (`STATE_TYPE_*`)
* @param status Result of loading section is stored here if the section is handled.
* @return true iff section handled.
*/
non_null()
bool conferences_load_state_section(
Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type, State_Load_Status *status);
/** Create new groupchat instance. */
non_null()
Group_Chats *new_groupchats(const Mono_Time *mono_time, Messenger *m);
/** main groupchats loop. */
non_null(1) nullable(2)
void do_groupchats(Group_Chats *g_c, void *userdata);
/** Free everything related with group chats. */
nullable(1)
void kill_groupchats(Group_Chats *g_c);
#endif

View File

@ -0,0 +1,462 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2020 The TokTok team.
* Copyright © 2015 Tox project.
*/
#include "group_announce.h"
#include <stdlib.h>
#include <string.h>
#include "LAN_discovery.h"
#include "ccompat.h"
#include "mono_time.h"
#include "util.h"
/**
* Removes `announces` from `gc_announces_list`.
*/
non_null()
static void remove_announces(GC_Announces_List *gc_announces_list, GC_Announces *announces)
{
if (announces == nullptr || gc_announces_list == nullptr) {
return;
}
if (announces->prev_announce != nullptr) {
announces->prev_announce->next_announce = announces->next_announce;
} else {
gc_announces_list->root_announces = announces->next_announce;
}
if (announces->next_announce != nullptr) {
announces->next_announce->prev_announce = announces->prev_announce;
}
free(announces);
}
/**
* Returns the announce designated by `chat_id`.
* Returns null if no announce is found.
*/
non_null()
static GC_Announces *get_announces_by_chat_id(const GC_Announces_List *gc_announces_list, const uint8_t *chat_id)
{
GC_Announces *announces = gc_announces_list->root_announces;
while (announces != nullptr) {
if (memcmp(announces->chat_id, chat_id, CHAT_ID_SIZE) == 0) {
return announces;
}
announces = announces->next_announce;
}
return nullptr;
}
int gca_get_announces(const 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)
{
if (gc_announces == nullptr || gc_announces_list == nullptr || chat_id == nullptr || max_nodes == 0
|| except_public_key == nullptr) {
return -1;
}
const GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, chat_id);
if (announces == nullptr) {
return 0;
}
uint16_t added_count = 0;
for (size_t i = 0; i < announces->index && i < GCA_MAX_SAVED_ANNOUNCES_PER_GC && added_count < max_nodes; ++i) {
const size_t index = i % GCA_MAX_SAVED_ANNOUNCES_PER_GC;
if (memcmp(except_public_key, &announces->peer_announces[index].base_announce.peer_public_key,
ENC_PUBLIC_KEY_SIZE) == 0) {
continue;
}
bool already_added = false;
for (size_t j = 0; j < added_count; ++j) {
if (memcmp(&gc_announces[j].peer_public_key, &announces->peer_announces[index].base_announce.peer_public_key,
ENC_PUBLIC_KEY_SIZE) == 0) {
already_added = true;
break;
}
}
if (!already_added) {
gc_announces[added_count] = announces->peer_announces[index].base_announce;
++added_count;
}
}
return added_count;
}
uint16_t gca_pack_announces_list_size(uint16_t count)
{
return count * GCA_ANNOUNCE_MAX_SIZE;
}
int gca_pack_announce(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announce)
{
if (length < GCA_ANNOUNCE_MAX_SIZE) {
LOGGER_ERROR(log, "Invalid announce length: %u", length);
return -1;
}
if (data == nullptr) {
LOGGER_ERROR(log, "data is null");
return -1;
}
if (announce == nullptr) {
LOGGER_ERROR(log, "announce is null");
return -1;
}
uint16_t offset = 0;
memcpy(data + offset, announce->peer_public_key, ENC_PUBLIC_KEY_SIZE);
offset += ENC_PUBLIC_KEY_SIZE;
data[offset] = announce->ip_port_is_set ? 1 : 0;
++offset;
data[offset] = announce->tcp_relays_count;
++offset;
if (!announce->ip_port_is_set && announce->tcp_relays_count == 0) {
LOGGER_ERROR(log, "Failed to pack announce: no valid ip_port or tcp relay");
return -1;
}
if (announce->ip_port_is_set) {
const int ip_port_length = pack_ip_port(log, data + offset, length - offset, &announce->ip_port);
if (ip_port_length == -1) {
LOGGER_ERROR(log, "Failed to pack ip_port");
return -1;
}
offset += ip_port_length;
}
const int nodes_length = pack_nodes(log, data + offset, length - offset, announce->tcp_relays,
announce->tcp_relays_count);
if (nodes_length == -1) {
LOGGER_ERROR(log, "Failed to pack TCP nodes");
return -1;
}
return nodes_length + offset;
}
/**
* Unpacks `announce` into `data` buffer of size `length`.
*
* Returns the size of the unpacked data on success.
* Returns -1 on failure.
*/
non_null()
static int gca_unpack_announce(const Logger *log, const uint8_t *data, uint16_t length, GC_Announce *announce)
{
if (length < ENC_PUBLIC_KEY_SIZE + 2) {
LOGGER_ERROR(log, "Invalid announce length: %u", length);
return -1;
}
if (data == nullptr) {
LOGGER_ERROR(log, "data is null");
return -1;
}
if (announce == nullptr) {
LOGGER_ERROR(log, "announce is null");
return -1;
}
uint16_t offset = 0;
memcpy(announce->peer_public_key, data + offset, ENC_PUBLIC_KEY_SIZE);
offset += ENC_PUBLIC_KEY_SIZE;
net_unpack_bool(&data[offset], &announce->ip_port_is_set);
++offset;
announce->tcp_relays_count = data[offset];
++offset;
if (announce->tcp_relays_count > GCA_MAX_ANNOUNCED_TCP_RELAYS) {
return -1;
}
if (announce->ip_port_is_set) {
if (length - offset == 0) {
return -1;
}
const int ip_port_length = unpack_ip_port(&announce->ip_port, data + offset, length - offset, false);
if (ip_port_length == -1) {
LOGGER_ERROR(log, "Failed to unpack ip_port");
return -1;
}
offset += ip_port_length;
}
uint16_t nodes_length;
const int nodes_count = unpack_nodes(announce->tcp_relays, announce->tcp_relays_count, &nodes_length,
data + offset, length - offset, true);
if (nodes_count != announce->tcp_relays_count) {
LOGGER_ERROR(log, "Failed to unpack TCP nodes");
return -1;
}
return offset + nodes_length;
}
int gca_pack_public_announce(const Logger *log, uint8_t *data, uint16_t length,
const GC_Public_Announce *public_announce)
{
if (public_announce == nullptr || data == nullptr || length < CHAT_ID_SIZE) {
return -1;
}
memcpy(data, public_announce->chat_public_key, CHAT_ID_SIZE);
const int packed_size = gca_pack_announce(log, data + CHAT_ID_SIZE, length - CHAT_ID_SIZE,
&public_announce->base_announce);
if (packed_size < 0) {
LOGGER_ERROR(log, "Failed to pack public group announce");
return -1;
}
return packed_size + CHAT_ID_SIZE;
}
int gca_unpack_public_announce(const Logger *log, const uint8_t *data, uint16_t length,
GC_Public_Announce *public_announce)
{
if (length < CHAT_ID_SIZE) {
LOGGER_ERROR(log, "invalid public announce length: %u", length);
return -1;
}
if (data == nullptr) {
LOGGER_ERROR(log, "data is null");
return -1;
}
if (public_announce == nullptr) {
LOGGER_ERROR(log, "public_announce is null");
return -1;
}
memcpy(public_announce->chat_public_key, data, CHAT_ID_SIZE);
const int base_announce_size = gca_unpack_announce(log, data + ENC_PUBLIC_KEY_SIZE, length - ENC_PUBLIC_KEY_SIZE,
&public_announce->base_announce);
if (base_announce_size == -1) {
LOGGER_ERROR(log, "Failed to unpack group announce");
return -1;
}
return base_announce_size + CHAT_ID_SIZE;
}
int gca_pack_announces_list(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announces,
uint8_t announces_count, size_t *processed)
{
if (data == nullptr) {
LOGGER_ERROR(log, "data is null");
return -1;
}
if (announces == nullptr) {
LOGGER_ERROR(log, "announces is null");
return -1;
}
uint16_t offset = 0;
for (size_t i = 0; i < announces_count; ++i) {
const int packed_length = gca_pack_announce(log, data + offset, length - offset, &announces[i]);
if (packed_length < 0) {
LOGGER_ERROR(log, "Failed to pack group announce");
return -1;
}
offset += packed_length;
}
if (processed != nullptr) {
*processed = offset;
}
return announces_count;
}
int gca_unpack_announces_list(const Logger *log, const uint8_t *data, uint16_t length, GC_Announce *announces,
uint8_t max_count)
{
if (data == nullptr) {
LOGGER_ERROR(log, "data is null");
return -1;
}
if (announces == nullptr) {
LOGGER_ERROR(log, "announces is null");
return -1;
}
uint16_t offset = 0;
int announces_count = 0;
for (size_t i = 0; i < max_count && length > offset; ++i) {
const int unpacked_length = gca_unpack_announce(log, data + offset, length - offset, &announces[i]);
if (unpacked_length == -1) {
LOGGER_WARNING(log, "Failed to unpack group announce: %d %d", length, offset);
return -1;
}
offset += unpacked_length;
++announces_count;
}
return announces_count;
}
GC_Peer_Announce *gca_add_announce(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list,
const GC_Public_Announce *public_announce)
{
if (gc_announces_list == nullptr || public_announce == nullptr) {
return nullptr;
}
GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, public_announce->chat_public_key);
// No entry for this chat_id exists so we create one
if (announces == nullptr) {
announces = (GC_Announces *)calloc(1, sizeof(GC_Announces));
if (announces == nullptr) {
return nullptr;
}
announces->index = 0;
announces->prev_announce = nullptr;
if (gc_announces_list->root_announces != nullptr) {
gc_announces_list->root_announces->prev_announce = announces;
}
announces->next_announce = gc_announces_list->root_announces;
gc_announces_list->root_announces = announces;
memcpy(announces->chat_id, public_announce->chat_public_key, CHAT_ID_SIZE);
}
const uint64_t cur_time = mono_time_get(mono_time);
announces->last_announce_received_timestamp = cur_time;
const uint64_t index = announces->index % GCA_MAX_SAVED_ANNOUNCES_PER_GC;
GC_Peer_Announce *gc_peer_announce = &announces->peer_announces[index];
gc_peer_announce->base_announce = public_announce->base_announce;
gc_peer_announce->timestamp = cur_time;
++announces->index;
return gc_peer_announce;
}
bool gca_is_valid_announce(const GC_Announce *announce)
{
if (announce == nullptr) {
return false;
}
return announce->tcp_relays_count > 0 || announce->ip_port_is_set;
}
GC_Announces_List *new_gca_list(void)
{
GC_Announces_List *announces_list = (GC_Announces_List *)calloc(1, sizeof(GC_Announces_List));
return announces_list;
}
void kill_gca(GC_Announces_List *announces_list)
{
if (announces_list == nullptr) {
return;
}
GC_Announces *root = announces_list->root_announces;
while (root != nullptr) {
GC_Announces *next = root->next_announce;
free(root);
root = next;
}
free(announces_list);
}
/* How long we save a peer's announce before we consider it stale and remove it. */
#define GCA_ANNOUNCE_SAVE_TIMEOUT 30
/* How often we run do_gca() */
#define GCA_DO_GCA_TIMEOUT 1
void do_gca(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list)
{
if (gc_announces_list == nullptr) {
return;
}
if (!mono_time_is_timeout(mono_time, gc_announces_list->last_timeout_check, GCA_DO_GCA_TIMEOUT)) {
return;
}
gc_announces_list->last_timeout_check = mono_time_get(mono_time);
GC_Announces *announces = gc_announces_list->root_announces;
while (announces != nullptr) {
if (mono_time_is_timeout(mono_time, announces->last_announce_received_timestamp, GCA_ANNOUNCE_SAVE_TIMEOUT)) {
GC_Announces *to_delete = announces;
announces = announces->next_announce;
remove_announces(gc_announces_list, to_delete);
continue;
}
announces = announces->next_announce;
}
}
void cleanup_gca(GC_Announces_List *gc_announces_list, const uint8_t *chat_id)
{
if (gc_announces_list == nullptr || chat_id == nullptr) {
return;
}
GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, chat_id);
if (announces != nullptr) {
remove_announces(gc_announces_list, announces);
}
}

View File

@ -0,0 +1,218 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2020 The TokTok team.
* Copyright © 2015 Tox project.
*/
/**
* Similar to ping.h, but designed for group chat purposes
*/
#ifndef GROUP_ANNOUNCE_H
#define GROUP_ANNOUNCE_H
#include <stdbool.h>
#include "DHT.h"
#ifdef __cplusplus
extern "C" {
#endif
/* The maximum number of announces to save for a particular group chat. */
#define GCA_MAX_SAVED_ANNOUNCES_PER_GC 16
/* Maximum number of TCP relays that can be in an announce. */
#define GCA_MAX_ANNOUNCED_TCP_RELAYS 1
/* Maximum number of announces we can send in an announce response. */
#define GCA_MAX_SENT_ANNOUNCES 4
/* Maximum size of an announce. */
#define GCA_ANNOUNCE_MAX_SIZE (ENC_PUBLIC_KEY_SIZE + 1 + 1 + (PACKED_NODE_SIZE_IP6 * 2))
/* Maximum size of a public announce. */
#define GCA_PUBLIC_ANNOUNCE_MAX_SIZE (ENC_PUBLIC_KEY_SIZE + GCA_ANNOUNCE_MAX_SIZE)
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;
/* Base announce. */
struct GC_Announce {
Node_format tcp_relays[GCA_MAX_ANNOUNCED_TCP_RELAYS];
uint8_t tcp_relays_count;
bool ip_port_is_set;
IP_Port ip_port;
uint8_t peer_public_key[ENC_PUBLIC_KEY_SIZE];
};
/* Peer announce for specific group. */
struct GC_Peer_Announce {
GC_Announce base_announce;
uint64_t timestamp;
};
/* Used for announces in public groups. */
struct GC_Public_Announce {
GC_Announce base_announce;
uint8_t chat_public_key[ENC_PUBLIC_KEY_SIZE];
};
/* A linked list that holds all announces for a particular group. */
struct GC_Announces {
uint8_t chat_id[CHAT_ID_SIZE];
uint64_t index;
uint64_t last_announce_received_timestamp;
GC_Peer_Announce peer_announces[GCA_MAX_SAVED_ANNOUNCES_PER_GC];
GC_Announces *next_announce;
GC_Announces *prev_announce;
};
/* A list of all announces. */
struct GC_Announces_List {
GC_Announces *root_announces;
uint64_t last_timeout_check;
};
/** @brief Returns a new group announces list.
*
* The caller is responsible for freeing the memory with `kill_gca`.
*/
GC_Announces_List *new_gca_list(void);
/** @brief Frees all dynamically allocated memory associated with `announces_list`. */
nullable(1)
void kill_gca(GC_Announces_List *announces_list);
/** @brief Iterates through the announces list and removes announces that are considered stale.
*
* @param gc_announces_list The list of announces to iterate.
*
* This function should be called from the main loop, and will iterate the list a
* maxmimum of once per second.
*/
non_null()
void do_gca(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list);
/** @brief Frees all dynamically allocated memory associated with an announces list entry.
*
* @param gc_announces_list The announces list we want to search through.
* @param chat_id The chat ID that designates the entry we want to remove.
*/
non_null()
void cleanup_gca(GC_Announces_List *gc_announces_list, const uint8_t *chat_id);
/** @brief Puts a set of announces from the announces list in supplied list.
*
* @param gc_announces_list The announces list we want to search for entries in.
* @param gc_announces An empty announces list that will be filled with matches.
* @param max_nodes The maximum number of matches that we want to add to the list.
* @param chat_id The chat ID associated with the announces that we want to add.
* @param except_public_key The public key associated with announces that we want to ignore.
*
* @return the number of added nodes on success.
* @retval -1 on failure.
*/
non_null()
int gca_get_announces(const 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);
/** @brief Adds a public_announce to list of announces.
*
* @param gc_announces_list The announces list that we want to add an entry to.
* @param public_announce The public announce that we want to add.
*
* @return the peer announce on success.
* @retval null on failure.
*/
non_null()
GC_Peer_Announce *gca_add_announce(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list,
const GC_Public_Announce *public_announce);
/** @brief Packs an announce into a data buffer.
*
* @param data The data buffer being packed.
* @param length The size in bytes of the data buffer. Must be at least GCA_ANNOUNCE_MAX_SIZE.
* @param announce The announce being packed into the data buffer.
*
* @return the size of the packed data on success.
* @retval -1 on failure.
*/
non_null()
int gca_pack_announce(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announce);
/** @brief Returns the number of bytes needed for a buff in which to pack `count` announces. */
uint16_t gca_pack_announces_list_size(uint16_t count);
/** @brief Packs a list of announces into a data buffer.
*
* @param data The data buffer being packed.
* @param length The size in bytes of the data buffer. Use gca_pack_announces_list_size to get the
* required length.
* @param announces The announces to be packed into the data buffer.
* @param announces_count The number of announces in the announces list.
* @param processed If non-null, will contain the number of bytes packed (only on success).
*
* @return the number of packed announces on success.
* @retval -1 on failure.
*/
non_null(1, 2, 4) nullable(6)
int gca_pack_announces_list(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announces,
uint8_t announces_count, size_t *processed);
/** @brief Unpacks packed announces from a data buffer into a supplied list.
*
* @param data The data buffer to unpack from.
* @param length The size of the data buffer.
* @param announces The announces list that the data buffer will be unpacked to.
* @param max_count The maximum number of announces to unpack.
*
* @return the number of unpacked announces on success.
* @retval -1 on failure.
*/
non_null()
int gca_unpack_announces_list(const Logger *log, const uint8_t *data, uint16_t length, GC_Announce *announces,
uint8_t max_count);
/** @brief Packs a public announce into a data buffer.
*
* @param data The data buffer being packed.
* @param length The size in bytes of the data buffer. Must be at least GCA_PUBLIC_ANNOUNCE_MAX_SIZE.
* @param public_announce The public announce being packed into the data buffer.
*
* @return the size of the packed data on success.
* @retval -1 on failure.
*/
non_null()
int gca_pack_public_announce(const Logger *log, uint8_t *data, uint16_t length,
const GC_Public_Announce *public_announce);
/** @brief Unpacks a public announce from a data buffer into a supplied public announce.
*
* @param data The data buffer to unpack from.
* @param length The size of the data buffer.
* @param public_announce The public announce to unpack the data buffer into.
*
* @return the size of the unpacked data on success.
* @retval -1 on failure.
*/
non_null()
int gca_unpack_public_announce(const Logger *log, const uint8_t *data, uint16_t length,
GC_Public_Announce *public_announce);
/** @brief Returns true if the announce is valid.
*
* An announce is considered valid if there is at least one TCP relay, or the ip_port is set.
*/
non_null()
bool gca_is_valid_announce(const GC_Announce *announce);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // GROUP_ANNOUNCE_H

View File

@ -0,0 +1,106 @@
#include "group_announce.h"
#include <cassert>
#include <memory>
#include <vector>
#include "../testing/fuzzing/fuzz_support.h"
namespace {
void TestUnpackAnnouncesList(Fuzz_Data &input)
{
CONSUME1_OR_RETURN(const uint8_t max_count, input);
std::vector<GC_Announce> announces(max_count);
// TODO(iphydf): How do we know the packed size?
CONSUME1_OR_RETURN(const uint16_t packed_size, input);
Logger *logger = logger_new();
if (gca_unpack_announces_list(logger, input.data, input.size, announces.data(), max_count)
!= -1) {
std::vector<uint8_t> packed(packed_size);
size_t processed;
gca_pack_announces_list(
logger, packed.data(), packed.size(), announces.data(), announces.size(), &processed);
}
logger_kill(logger);
}
void TestUnpackPublicAnnounce(Fuzz_Data &input)
{
GC_Public_Announce public_announce;
// TODO(iphydf): How do we know the packed size?
CONSUME1_OR_RETURN(const uint16_t packed_size, input);
Logger *logger = logger_new();
if (gca_unpack_public_announce(logger, input.data, input.size, &public_announce) != -1) {
std::vector<uint8_t> packed(packed_size);
gca_pack_public_announce(logger, packed.data(), packed.size(), &public_announce);
}
logger_kill(logger);
}
void TestDoGca(Fuzz_Data &input)
{
std::unique_ptr<Logger, void (*)(Logger *)> logger(logger_new(), logger_kill);
std::unique_ptr<Mono_Time, void (*)(Mono_Time *)> mono_time(
mono_time_new(nullptr, nullptr), mono_time_free);
assert(mono_time != nullptr);
uint64_t clock = 1;
mono_time_set_current_time_callback(
mono_time.get(), [](void *user_data) { return *static_cast<uint64_t *>(user_data); },
&clock);
std::unique_ptr<GC_Announces_List, void (*)(GC_Announces_List *)> gca(new_gca_list(), kill_gca);
assert(gca != nullptr);
while (input.size > 0) {
CONSUME1_OR_RETURN(const uint8_t choice, input);
switch (choice) {
case 0: {
// Add an announce.
CONSUME1_OR_RETURN(const uint16_t length, input);
CONSUME_OR_RETURN(const uint8_t *data, input, length);
GC_Public_Announce public_announce;
if (gca_unpack_public_announce(logger.get(), data, length, &public_announce) != -1) {
gca_add_announce(mono_time.get(), gca.get(), &public_announce);
}
break;
}
case 1: {
// Advance the time by a number of tox_iteration_intervals.
CONSUME1_OR_RETURN(const uint8_t iterations, input);
clock += iterations * 20;
// Do an iteration.
do_gca(mono_time.get(), gca.get());
break;
}
case 2: {
// Get announces.
CONSUME1_OR_RETURN(const uint8_t max_nodes, input);
std::vector<GC_Announce> gc_announces(max_nodes);
CONSUME_OR_RETURN(const uint8_t *chat_id, input, CHAT_ID_SIZE);
CONSUME_OR_RETURN(const uint8_t *except_public_key, input, ENC_PUBLIC_KEY_SIZE);
gca_get_announces(
gca.get(), gc_announces.data(), max_nodes, chat_id, except_public_key);
break;
}
case 3: {
// Remove a chat.
CONSUME_OR_RETURN(const uint8_t *chat_id, input, CHAT_ID_SIZE);
cleanup_gca(gca.get(), chat_id);
break;
}
}
}
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
fuzz_select_target(data, size, TestUnpackAnnouncesList, TestUnpackPublicAnnounce, TestDoGca);
return 0;
}

View File

@ -0,0 +1,261 @@
#include "group_announce.h"
#include <gtest/gtest.h>
#include "mono_time.h"
namespace {
struct Announces : ::testing::Test {
protected:
uint64_t clock_ = 0;
Mono_Time *mono_time_ = nullptr;
GC_Announces_List *gca_ = nullptr;
GC_Announce _ann1;
GC_Announce _ann2;
void SetUp() override
{
mono_time_ = mono_time_new(nullptr, nullptr);
ASSERT_NE(mono_time_, nullptr);
mono_time_set_current_time_callback(
mono_time_, [](void *user_data) { return *static_cast<uint64_t *>(user_data); },
&clock_);
gca_ = new_gca_list();
ASSERT_NE(gca_, nullptr);
}
~Announces() override
{
kill_gca(gca_);
mono_time_free(mono_time_);
}
void advance_clock(uint64_t increment)
{
clock_ += increment;
mono_time_update(mono_time_);
}
};
TEST_F(Announces, KillGcaOnNullptrIsNoop)
{
// All kill functions should be nullable.
kill_gca(nullptr);
}
TEST_F(Announces, CanBeCreatedAndDeleted)
{
GC_Public_Announce ann{};
ann.chat_public_key[0] = 0x88;
ASSERT_NE(gca_add_announce(mono_time_, gca_, &ann), nullptr);
ASSERT_EQ(gca_add_announce(mono_time_, gca_, nullptr), nullptr);
ASSERT_EQ(gca_add_announce(mono_time_, nullptr, &ann), nullptr);
}
TEST_F(Announces, AnnouncesCanTimeOut)
{
advance_clock(100);
ASSERT_EQ(gca_->root_announces, nullptr);
GC_Public_Announce ann{};
ann.chat_public_key[0] = 0xae;
ASSERT_NE(gca_add_announce(mono_time_, gca_, &ann), nullptr);
ASSERT_NE(gca_->root_announces, nullptr);
ASSERT_EQ(gca_->root_announces->chat_id[0], 0xae);
// One iteration without having any time passed => announce is still here.
do_gca(mono_time_, gca_);
ASSERT_NE(gca_->root_announces, nullptr);
// 29 seconds later, still there
advance_clock(29000);
do_gca(mono_time_, gca_);
ASSERT_NE(gca_->root_announces, nullptr);
// One more second and it's gone.
advance_clock(1000);
do_gca(mono_time_, gca_);
ASSERT_EQ(gca_->root_announces, nullptr);
}
TEST_F(Announces, AnnouncesGetAndCleanup)
{
GC_Public_Announce ann1{};
GC_Public_Announce ann2{};
ann1.chat_public_key[0] = 0x91;
ann1.base_announce.peer_public_key[0] = 0x7f;
ann2.chat_public_key[0] = 0x92;
ann2.base_announce.peer_public_key[0] = 0x7c;
ASSERT_NE(gca_add_announce(mono_time_, gca_, &ann1), nullptr);
ASSERT_NE(gca_add_announce(mono_time_, gca_, &ann2), nullptr);
ASSERT_NE(gca_add_announce(mono_time_, gca_, &ann2), nullptr);
uint8_t empty_pk[ENC_PUBLIC_KEY_SIZE] = {0};
GC_Announce announces;
ASSERT_EQ(gca_get_announces(gca_, &announces, 1, ann1.chat_public_key, empty_pk), 1);
ASSERT_EQ(gca_get_announces(gca_, &announces, 1, ann2.chat_public_key, empty_pk), 1);
cleanup_gca(gca_, ann1.chat_public_key);
ASSERT_EQ(gca_get_announces(gca_, &announces, 1, ann1.chat_public_key, empty_pk), 0);
cleanup_gca(gca_, ann2.chat_public_key);
ASSERT_EQ(gca_get_announces(gca_, &announces, 1, ann2.chat_public_key, empty_pk), 0);
ASSERT_EQ(gca_get_announces(gca_, nullptr, 1, ann2.chat_public_key, empty_pk), -1);
}
struct AnnouncesPack : ::testing::Test {
protected:
std::vector<GC_Announce> announces_;
Logger *logger_ = nullptr;
void SetUp() override
{
logger_ = logger_new();
ASSERT_NE(logger_, nullptr);
// Add an announce without TCP relay.
announces_.emplace_back();
auto &ann1 = announces_.back();
ann1.peer_public_key[0] = 0xae;
ann1.ip_port.ip.family = net_family_ipv4();
ann1.ip_port.ip.ip.v4.uint8[0] = 0x7f; // 127.0.0.1
ann1.ip_port.ip.ip.v4.uint8[3] = 0x1;
ann1.ip_port_is_set = 1;
// Add an announce with TCP relay.
announces_.emplace_back();
auto &ann2 = announces_.back();
ann2.peer_public_key[0] = 0xaf; // different key
ann2.ip_port.ip.family = net_family_ipv4();
ann2.ip_port.ip.ip.v4.uint8[0] = 0x7f; // 127.0.0.2
ann2.ip_port.ip.ip.v4.uint8[3] = 0x2;
ann2.ip_port_is_set = 1;
ann2.tcp_relays_count = 1;
ann2.tcp_relays[0].ip_port.ip.family = net_family_ipv4();
ann2.tcp_relays[0].ip_port.ip.ip.v4 = ip4_broadcast;
ann2.tcp_relays[0].public_key[0] = 0xea;
}
~AnnouncesPack() override { logger_kill(logger_); }
};
TEST_F(AnnouncesPack, PublicAnnounceCanBePackedAndUnpacked)
{
GC_Public_Announce ann{};
ann.chat_public_key[0] = 0x88;
ann.base_announce = announces_[0];
std::vector<uint8_t> packed(GCA_PUBLIC_ANNOUNCE_MAX_SIZE);
const int packed_size = gca_pack_public_announce(logger_, packed.data(), packed.size(), &ann);
EXPECT_GT(packed_size, 0);
GC_Public_Announce unpacked_ann{};
EXPECT_EQ(gca_unpack_public_announce(logger_, packed.data(), packed.size(), &unpacked_ann),
packed_size);
}
TEST_F(AnnouncesPack, UnpackEmptyPublicAnnounce)
{
GC_Public_Announce ann{};
std::vector<uint8_t> packed(GCA_PUBLIC_ANNOUNCE_MAX_SIZE);
EXPECT_EQ(gca_unpack_public_announce(logger_, nullptr, 0, &ann), -1);
EXPECT_EQ(gca_unpack_public_announce(logger_, packed.data(), packed.size(), nullptr), -1);
}
TEST_F(AnnouncesPack, PackEmptyPublicAnnounce)
{
GC_Public_Announce ann{};
std::vector<uint8_t> packed(GCA_PUBLIC_ANNOUNCE_MAX_SIZE);
EXPECT_EQ(gca_pack_public_announce(logger_, packed.data(), packed.size(), nullptr), -1);
EXPECT_EQ(gca_pack_public_announce(logger_, nullptr, 0, &ann), -1);
}
TEST_F(AnnouncesPack, PublicAnnouncePackNull)
{
GC_Public_Announce ann{};
std::vector<uint8_t> packed(GCA_PUBLIC_ANNOUNCE_MAX_SIZE);
EXPECT_EQ(gca_pack_public_announce(logger_, packed.data(), packed.size(), &ann), -1);
ann.chat_public_key[0] = 0x88;
ann.base_announce = announces_[0];
std::vector<uint8_t> packedTooSmall(GCA_PUBLIC_ANNOUNCE_MAX_SIZE - 1);
EXPECT_EQ(
gca_pack_public_announce(logger_, packedTooSmall.data(), packedTooSmall.size(), &ann), -1);
ann.base_announce.ip_port_is_set = 0;
ann.base_announce.tcp_relays_count = 0;
EXPECT_EQ(gca_pack_public_announce(logger_, packed.data(), packed.size(), &ann), -1);
}
TEST_F(AnnouncesPack, AnnouncesValidationCheck)
{
EXPECT_EQ(gca_is_valid_announce(nullptr), false);
GC_Announce announce = {0};
EXPECT_EQ(gca_is_valid_announce(&announce), false);
EXPECT_EQ(gca_is_valid_announce(&announces_[0]), true);
EXPECT_EQ(gca_is_valid_announce(&announces_[1]), true);
announces_[0].ip_port_is_set = 0;
announces_[0].tcp_relays_count = 0;
EXPECT_EQ(gca_is_valid_announce(&announces_[0]), false);
}
TEST_F(AnnouncesPack, UnpackIncompleteAnnouncesList)
{
const uint8_t data[] = {0x00, 0x24, 0x3d, 0x00, 0x3d, 0xff, 0xff, 0x5b, 0x04, 0x20, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00};
GC_Announce announce;
EXPECT_EQ(gca_unpack_announces_list(logger_, data, sizeof(data), &announce, 1), -1);
EXPECT_EQ(gca_unpack_announces_list(logger_, data, sizeof(data), nullptr, 1), -1);
EXPECT_EQ(gca_unpack_announces_list(logger_, nullptr, 0, &announce, 1), -1);
}
TEST_F(AnnouncesPack, PackedAnnouncesListCanBeUnpacked)
{
const uint16_t size = gca_pack_announces_list_size(announces_.size());
std::vector<uint8_t> packed(size);
size_t processed = 0;
EXPECT_GT(gca_pack_announces_list(logger_, packed.data(), packed.size(), announces_.data(),
announces_.size(), &processed),
0);
ASSERT_GE(processed, ENC_PUBLIC_KEY_SIZE + 2);
ASSERT_LE(processed, size);
std::vector<GC_Announce> announces_unpacked(announces_.size());
ASSERT_EQ(gca_unpack_announces_list(logger_, packed.data(), packed.size(),
announces_unpacked.data(), announces_unpacked.size()),
announces_unpacked.size());
}
TEST_F(AnnouncesPack, PackingEmptyAnnounceFails)
{
GC_Announce announce{}; // all zeroes
std::vector<uint8_t> packed(gca_pack_announces_list_size(1));
EXPECT_EQ(
gca_pack_announces_list(logger_, packed.data(), packed.size(), &announce, 1, nullptr), -1);
EXPECT_EQ(
gca_pack_announces_list(logger_, packed.data(), packed.size(), nullptr, 1, nullptr), -1);
EXPECT_EQ(gca_pack_announces_list(logger_, nullptr, 0, &announce, 1, nullptr), -1);
}
TEST_F(AnnouncesPack, PackAnnounceNull)
{
std::vector<uint8_t> data(GCA_ANNOUNCE_MAX_SIZE);
GC_Announce announce;
ASSERT_EQ(gca_pack_announce(logger_, nullptr, 0, &announce), -1);
ASSERT_EQ(gca_pack_announce(logger_, data.data(), data.size(), nullptr), -1);
}
} // namespace

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,782 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2020 The TokTok team.
* Copyright © 2015 Tox project.
*/
/**
* An implementation of massive text only group chats.
*/
#ifndef GROUP_CHATS_H
#define GROUP_CHATS_H
#include <stdbool.h>
#include <stdint.h>
#include "TCP_connection.h"
#include "bin_pack.h"
#include "bin_unpack.h"
#include "group_announce.h"
#include "group_common.h"
#include "group_connection.h"
#include "logger.h"
#define GC_PING_TIMEOUT 12
#define GC_SEND_IP_PORT_INTERVAL (GC_PING_TIMEOUT * 5)
#define GC_CONFIRMED_PEER_TIMEOUT (GC_PING_TIMEOUT * 4 + 10)
#define GC_UNCONFIRMED_PEER_TIMEOUT GC_PING_TIMEOUT
#define GC_JOIN_DATA_LENGTH (ENC_PUBLIC_KEY_SIZE + CHAT_ID_SIZE)
/** Group topic lock states. */
typedef enum Group_Topic_Lock {
TL_ENABLED = 0x00, // Only the Founder and moderators may set the topic
TL_DISABLED = 0x01, // Anyone except Observers may set the topic
} Group_Topic_Lock;
/** Group moderation events. */
typedef enum Group_Moderation_Event {
MV_KICK = 0x00, // A peer has been kicked
MV_OBSERVER = 0x01, // A peer has been demoted to Observer
MV_USER = 0x02, // A peer has been demoted or promoted to User
MV_MOD = 0x03, // A peer has been promoted to or demoted from Moderator
} Group_Moderation_Event;
/** Messenger level group invite types */
typedef enum Group_Invite_Message_Type {
GROUP_INVITE = 0x00, // Peer has initiated an invite
GROUP_INVITE_ACCEPTED = 0x01, // Peer has accepted the invite
GROUP_INVITE_CONFIRMATION = 0x02, // Peer has confirmed the accepted invite
} Group_Invite_Message_Type;
/** Group join rejection types. */
typedef enum Group_Join_Rejected {
GJ_GROUP_FULL = 0x00,
GJ_INVALID_PASSWORD = 0x01,
GJ_INVITE_FAILED = 0x02,
GJ_INVALID = 0x03,
} Group_Join_Rejected;
/** Group broadcast packet types */
typedef enum Group_Broadcast_Type {
GM_STATUS = 0x00, // Peer changed their status
GM_NICK = 0x01, // Peer changed their nickname
GM_PLAIN_MESSAGE = 0x02, // Peer sent a normal message
GM_ACTION_MESSAGE = 0x03, // Peer sent an action message
GM_PRIVATE_MESSAGE = 0x04, // Peer sent a private message
GM_PEER_EXIT = 0x05, // Peer left the group
GM_KICK_PEER = 0x06, // Peer was kicked from the group
GM_SET_MOD = 0x07, // Peer was promoted to or demoted from Moderator role
GM_SET_OBSERVER = 0x08, // Peer was demoted to or promoted from Observer role
} Group_Broadcast_Type;
/***
* Group packet types.
*
* For a detailed spec, see docs/DHT_Group_Chats_Packet_Spec.md
*/
typedef enum Group_Packet_Type {
/* lossy packets (ID 0 is reserved) */
GP_PING = 0x01,
GP_MESSAGE_ACK = 0x02,
GP_INVITE_RESPONSE_REJECT = 0x03,
/* lossless packets */
GP_CUSTOM_PRIVATE_PACKET = 0xee,
GP_FRAGMENT = 0xef,
GP_KEY_ROTATION = 0xf0,
GP_TCP_RELAYS = 0xf1,
GP_CUSTOM_PACKET = 0xf2,
GP_BROADCAST = 0xf3,
GP_PEER_INFO_REQUEST = 0xf4,
GP_PEER_INFO_RESPONSE = 0xf5,
GP_INVITE_REQUEST = 0xf6,
GP_INVITE_RESPONSE = 0xf7,
GP_SYNC_REQUEST = 0xf8,
GP_SYNC_RESPONSE = 0xf9,
GP_TOPIC = 0xfa,
GP_SHARED_STATE = 0xfb,
GP_MOD_LIST = 0xfc,
GP_SANCTIONS_LIST = 0xfd,
GP_FRIEND_INVITE = 0xfe,
GP_HS_RESPONSE_ACK = 0xff,
} Group_Packet_Type;
/** Lossless message acknowledgement types. */
typedef enum Group_Message_Ack_Type {
GR_ACK_RECV = 0x00, // indicates a message has been received
GR_ACK_REQ = 0x01, // indicates a message needs to be re-sent
} Group_Message_Ack_Type;
/** @brief Returns the GC_Connection object associated with `peer_number`.
* Returns null if peer_number does not designate a valid peer.
*/
non_null()
GC_Connection *get_gc_connection(const GC_Chat *chat, int peer_number);
/** @brief Returns the jenkins hash of a 32 byte public encryption key. */
non_null()
uint32_t gc_get_pk_jenkins_hash(const uint8_t *public_key);
/** @brief Check if peer with the public encryption key is in peer list.
*
* Returns the peer number if peer is in the peer list.
* Returns -1 if peer is not in the peer list.
*
* If `confirmed` is true the peer number will only be returned if the peer is confirmed.
*/
non_null()
int get_peer_number_of_enc_pk(const GC_Chat *chat, const uint8_t *public_enc_key, bool confirmed);
/** @brief Encrypts `data` of size `length` using the peer's shared key and a new nonce.
*
* Adds encrypted header consisting of: packet type, message_id (only for lossless packets).
* Adds plaintext header consisting of: packet identifier, self public encryption key, nonce.
*
* Return length of encrypted packet on success.
* Return -1 if plaintext length is invalid.
* Return -2 if malloc fails.
* Return -3 if encryption fails.
*/
non_null(1, 2, 3, 4, 5) nullable(7)
int group_packet_wrap(
const Logger *log, const Random *rng, const uint8_t *self_pk, const uint8_t *shared_key, uint8_t *packet,
uint16_t packet_size, const uint8_t *data, uint16_t length, uint64_t message_id,
uint8_t gp_packet_type, uint8_t net_packet_type);
/** @brief Returns the size of a wrapped/encrypted packet with a plain size of `length`.
*
* `packet_type` should be either NET_PACKET_GC_LOSSY or NET_PACKET_GC_LOSSLESS.
*/
uint16_t gc_get_wrapped_packet_size(uint16_t length, Net_Packet_Type packet_type);
/** @brief Sends a plain message or an action, depending on type.
*
* `length` must not exceed MAX_GC_MESSAGE_SIZE and must not be equal to zero.
* `message_id` should either point to a uint32_t or be NULL.
*
* Returns 0 on success.
* Returns -1 if the message is too long.
* Returns -2 if the message pointer is NULL or length is zero.
* Returns -3 if the message type is invalid.
* Returns -4 if the sender does not have permission to speak.
* Returns -5 if the packet fails to send.
*/
non_null(1, 2, 3, 4) nullable(5)
int gc_send_message(const GC_Chat *chat, const uint8_t *message, uint16_t length, uint8_t type,
uint32_t *message_id);
/** @brief Sends a private message to peer_id.
*
* `length` must not exceed MAX_GC_MESSAGE_SIZE and must not be equal to zero.
*
* Returns 0 on success.
* Returns -1 if the message is too long.
* Returns -2 if the message pointer is NULL or length is zero.
* Returns -3 if the peer_id is invalid.
* Returns -4 if the message type is invalid.
* Returns -5 if the sender has the observer role.
* Returns -6 if the packet fails to send.
*/
non_null()
int gc_send_private_message(const GC_Chat *chat, uint32_t peer_id, uint8_t type, const uint8_t *message,
uint16_t length);
/** @brief Sends a custom packet to the group. If lossless is true, the packet will be lossless.
*
* `length` must not exceed MAX_GC_MESSAGE_SIZE and must not be equal to zero.
*
* Returns 0 on success.
* Returns -1 if the message is too long.
* Returns -2 if the message pointer is NULL or length is zero.
* Returns -3 if the sender has the observer role.
*/
non_null()
int gc_send_custom_packet(const GC_Chat *chat, bool lossless, const uint8_t *data, uint16_t length);
/** @brief Sends a custom private packet to the peer designated by peer_id.
*
* `length` must not exceed MAX_GC_MESSAGE_SIZE and must not be equal to zero.
*
* @retval 0 on success.
* @retval -1 if the message is too long.
* @retval -2 if the message pointer is NULL or length is zero.
* @retval -3 if the supplied peer_id does not designate a valid peer.
* @retval -4 if the sender has the observer role.
* @retval -5 if the packet fails to send.
*/
non_null()
int gc_send_custom_private_packet(const GC_Chat *chat, bool lossless, uint32_t peer_id, const uint8_t *message,
uint16_t length);
/** @brief Sets ignore for peer_id.
*
* Returns 0 on success.
* Returns -1 if the peer_id is invalid.
* Returns -2 if the caller attempted to ignore himself.
*/
non_null()
int gc_set_ignore(const GC_Chat *chat, uint32_t peer_id, bool ignore);
/** @brief Sets the group topic and broadcasts it to the group.
*
* If `length` is equal to zero the topic will be unset.
*
* Returns 0 on success.
* Returns -1 if the topic is too long (must be `<= MAX_GC_TOPIC_SIZE`).
* Returns -2 if the caller does not have the required permissions to set the topic.
* Returns -3 if the packet cannot be created or signing fails.
* Returns -4 if the packet fails
*/
non_null(1) nullable(2)
int gc_set_topic(GC_Chat *chat, const uint8_t *topic, uint16_t length);
/** @brief Copies the group topic to `topic`. If topic is null this function has no effect.
*
* Call `gc_get_topic_size` to determine the allocation size for the `topic` parameter.
*
* The data written to `topic` is equal to the data received by the last topic callback.
*/
non_null(1) nullable(2)
void gc_get_topic(const GC_Chat *chat, uint8_t *topic);
/** @brief Returns the topic length.
*
* The return value is equal to the `length` agument received by the last topic callback.
*/
non_null()
uint16_t gc_get_topic_size(const GC_Chat *chat);
/** @brief Copies group name to `group_name`. If `group_name` is null this function has no effect.
*
* Call `gc_get_group_name_size` to determine the allocation size for the `group_name`
* parameter.
*/
non_null()
void gc_get_group_name(const GC_Chat *chat, uint8_t *group_name);
/** @brief Returns the group name length. */
non_null()
uint16_t gc_get_group_name_size(const GC_Chat *chat);
/** @brief Copies the group password to password.
*
* If password is null this function has no effect.
*
* Call the `gc_get_password_size` function to determine the allocation size for
* the `password` buffer.
*
* The data received is equal to the data received by the last password callback.
*/
non_null()
void gc_get_password(const GC_Chat *chat, uint8_t *password);
/** @brief Returns the group password length. */
non_null()
uint16_t gc_get_password_size(const GC_Chat *chat);
/** @brief Returns the group privacy state.
*
* The value returned is equal to the data receieved by the last privacy_state callback.
*/
non_null()
Group_Privacy_State gc_get_privacy_state(const GC_Chat *chat);
/** @brief Returns the group topic lock state.
*
* The value returned is equal to the data received by the last last topic_lock callback.
*/
non_null()
Group_Topic_Lock gc_get_topic_lock_state(const GC_Chat *chat);
/** @brief Returns the group voice state.
*
* The value returned is equal to the data received by the last voice_state callback.
*/
non_null()
Group_Voice_State gc_get_voice_state(const GC_Chat *chat);
/** @brief Returns the group peer limit.
*
* The value returned is equal to the data receieved by the last peer_limit callback.
*/
non_null()
uint16_t gc_get_max_peers(const GC_Chat *chat);
/** @brief Sets your own nick to `nick`.
*
* `length` cannot exceed MAX_GC_NICK_SIZE. if `length` is zero or `name` is a
* null pointer the function call will fail.
*
* Returns 0 on success.
* 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 packet fails to send.
*/
non_null()
int gc_set_self_nick(const Messenger *m, int group_number, const uint8_t *nick, uint16_t length);
/** @brief Copies your own name to `nick`.
*
* If `nick` is null this function has no effect.
*/
non_null()
void gc_get_self_nick(const GC_Chat *chat, uint8_t *nick);
/** @brief Return your own nick length.
*
* If no nick was set before calling this function it will return 0.
*/
non_null()
uint16_t gc_get_self_nick_size(const GC_Chat *chat);
/** @brief Returns your own group role. */
non_null()
Group_Role gc_get_self_role(const GC_Chat *chat);
/** @brief Return your own status. */
non_null()
uint8_t gc_get_self_status(const GC_Chat *chat);
/** @brief Returns your own peer id. */
non_null()
uint32_t gc_get_self_peer_id(const GC_Chat *chat);
/** @brief Copies self public key to `public_key`.
*
* If `public_key` is null this function has no effect.
*
* This key is permanently tied to our identity for `chat` until we explicitly
* exit the group. This key is the only way for other peers to reliably identify
* us across client restarts.
*/
non_null(1) nullable(2)
void gc_get_self_public_key(const GC_Chat *chat, uint8_t *public_key);
/** @brief Copies nick designated by `peer_id` to `name`.
*
* Call `gc_get_peer_nick_size` to determine the allocation size for the `name` parameter.
*
* The data written to `name` is equal to the data received by the last nick_change callback.
*
* Returns true on success.
* Returns false if peer_id is invalid.
*/
non_null(1) nullable(3)
bool gc_get_peer_nick(const GC_Chat *chat, uint32_t peer_id, uint8_t *name);
/** @brief Returns the length of the nick for the peer designated by `peer_id`.
* Returns -1 if peer_id is invalid.
*
* The value returned is equal to the `length` argument received by the last
* nick_change callback.
*/
non_null()
int gc_get_peer_nick_size(const GC_Chat *chat, uint32_t peer_id);
/** @brief Copies peer_id's public key to `public_key`.
*
* This key is permanently tied to the peer's identity for `chat` until they explicitly
* exit the group. This key is the only way for to reliably identify the given peer
* across client restarts.
*
* `public_key` shold have room for at least ENC_PUBLIC_KEY_SIZE bytes.
*
* Returns 0 on success.
* Returns -1 if peer_id is invalid or doesn't correspond to a valid peer connection.
* Returns -2 if `public_key` is null.
*/
non_null(1) nullable(3)
int gc_get_peer_public_key_by_peer_id(const GC_Chat *chat, uint32_t peer_id, uint8_t *public_key);
/** @brief Gets the connection status for peer associated with `peer_id`.
*
* Returns 2 if we have a direct (UDP) connection with a peer.
* Returns 1 if we have an indirect (TCP) connection with a peer.
* Returns 0 if peer_id is invalid or corresponds to ourselves.
*
* Note: Return values must correspond to Tox_Connection enum in API.
*/
non_null()
unsigned int gc_get_peer_connection_status(const GC_Chat *chat, uint32_t peer_id);
/** @brief Sets the caller's status to `status`.
*
* Returns 0 on success.
* Returns -1 if the group_number is invalid.
* Returns -2 if the packet failed to send.
*/
non_null()
int gc_set_self_status(const Messenger *m, int group_number, Group_Peer_Status status);
/** @brief Returns the status of peer designated by `peer_id`.
* Returns UINT8_MAX on failure.
*
* The status returned is equal to the last status received through the status_change
* callback.
*/
non_null()
uint8_t gc_get_status(const GC_Chat *chat, uint32_t peer_id);
/** @brief Returns the group role of peer designated by `peer_id`.
* Returns UINT8_MAX on failure.
*
* The role returned is equal to the last role received through the moderation callback.
*/
non_null()
uint8_t gc_get_role(const GC_Chat *chat, uint32_t peer_id);
/** @brief 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 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.
*/
non_null()
int gc_set_peer_role(const Messenger *m, int group_number, uint32_t peer_id, Group_Role new_role);
/** @brief Sets the group password 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.
*
* If `password` is null or `password_length` is 0 the password will be unset for the group.
*
* Returns 0 on success.
* Returns -1 if the caller does not have sufficient permissions for the action.
* Returns -2 if the password is too long.
* Returns -3 if the packet failed to send.
* Returns -4 if malloc failed.
*/
non_null(1) nullable(2)
int gc_founder_set_password(GC_Chat *chat, const uint8_t *password, uint16_t password_length);
/** @brief Sets the topic lock and distributes the new shared state to the group.
*
* When the topic lock is enabled, only the group founder and moderators may set the topic.
* When disabled, all peers except those with the observer role may set the topic.
*
* 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 group_number is invalid.
* Returns -2 if `topic_lock` is an invalid type.
* Returns -3 if the caller does not have sufficient permissions for this action.
* Returns -4 if the group is disconnected.
* Returns -5 if the topic lock could not be set.
* Returns -6 if the packet failed to send.
*/
non_null()
int gc_founder_set_topic_lock(const Messenger *m, int group_number, Group_Topic_Lock new_lock_state);
/** @brief 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.
*
* 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.
*
* Returns 0 on success.
* Returns -1 if group_number is invalid.
* Returns -2 if the caller does not have sufficient permissions for this action.
* Returns -3 if the group is disconnected.
* Returns -4 if the privacy state could not be set.
* Returns -5 if the packet failed to send.
*/
non_null()
int gc_founder_set_privacy_state(const Messenger *m, int group_number, Group_Privacy_State new_privacy_state);
/** @brief Sets the group voice 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.
*
* If an attempt is made to set the voice state to the same state that the group is already
* in, the function call will be successful and no action will be taken.
*
* Returns 0 on success.
* Returns -1 if group_number is invalid.
* Returns -2 if the caller does not have sufficient permissions for this action.
* Returns -3 if the group is disconnected.
* Returns -4 if the voice state could not be set.
* Returns -5 if the packet failed to send.
*/
non_null()
int gc_founder_set_voice_state(const Messenger *m, int group_number, Group_Voice_State new_voice_state);
/** @brief Sets the peer limit to maxpeers 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 the caller does not have sufficient permissions for this action.
* Returns -2 if the peer limit could not be set.
* Returns -3 if the packet failed to send.
*/
non_null()
int gc_founder_set_max_peers(GC_Chat *chat, uint16_t max_peers);
/** @brief Removes peer designated by `peer_id` from peer list and sends a broadcast instructing
* all other peers to remove the peer from their peerlist as well.
*
* This function will not trigger the peer_exit callback for the caller.
*
* Returns 0 on success.
* 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.
*/
non_null()
int gc_kick_peer(const Messenger *m, int group_number, uint32_t peer_id);
/** @brief Copies the chat_id to dest. If dest is null this function has no effect.
*
* `dest` should have room for at least CHAT_ID_SIZE bytes.
*/
non_null(1) nullable(2)
void gc_get_chat_id(const GC_Chat *chat, uint8_t *dest);
/** Group callbacks */
non_null(1) nullable(2) void gc_callback_message(const Messenger *m, gc_message_cb *function);
non_null(1) nullable(2) void gc_callback_private_message(const Messenger *m, gc_private_message_cb *function);
non_null(1) nullable(2) void gc_callback_custom_packet(const Messenger *m, gc_custom_packet_cb *function);
non_null(1) nullable(2) void gc_callback_custom_private_packet(const Messenger *m,
gc_custom_private_packet_cb *function);
non_null(1) nullable(2) void gc_callback_moderation(const Messenger *m, gc_moderation_cb *function);
non_null(1) nullable(2) void gc_callback_nick_change(const Messenger *m, gc_nick_change_cb *function);
non_null(1) nullable(2) void gc_callback_status_change(const Messenger *m, gc_status_change_cb *function);
non_null(1) nullable(2) void gc_callback_topic_change(const Messenger *m, gc_topic_change_cb *function);
non_null(1) nullable(2) void gc_callback_peer_limit(const Messenger *m, gc_peer_limit_cb *function);
non_null(1) nullable(2) void gc_callback_privacy_state(const Messenger *m, gc_privacy_state_cb *function);
non_null(1) nullable(2) void gc_callback_topic_lock(const Messenger *m, gc_topic_lock_cb *function);
non_null(1) nullable(2) void gc_callback_password(const Messenger *m, gc_password_cb *function);
non_null(1) nullable(2) void gc_callback_peer_join(const Messenger *m, gc_peer_join_cb *function);
non_null(1) nullable(2) void gc_callback_peer_exit(const Messenger *m, gc_peer_exit_cb *function);
non_null(1) nullable(2) void gc_callback_self_join(const Messenger *m, gc_self_join_cb *function);
non_null(1) nullable(2) void gc_callback_rejected(const Messenger *m, gc_rejected_cb *function);
non_null(1) nullable(2) void gc_callback_voice_state(const Messenger *m, gc_voice_state_cb *function);
/** @brief The main loop. Should be called with every Messenger iteration. */
non_null(1) nullable(2)
void do_gc(GC_Session *c, void *userdata);
/**
* Make sure that DHT is initialized before calling this.
* Returns a NULL pointer on failure.
*/
nullable(1)
GC_Session *new_dht_groupchats(Messenger *m);
/** @brief Cleans up groupchat structures and calls `gc_group_exit()` for every group chat */
nullable(1)
void kill_dht_groupchats(GC_Session *c);
/** @brief Loads a previously saved group and attempts to join it.
*
* `bu` is the packed group info.
*
* Returns group_number on success.
* Returns -1 on failure.
*/
non_null()
int gc_group_load(GC_Session *c, Bin_Unpack *bu);
/**
* @brief Saves info from `chat` to `bp` in binary format.
*/
non_null()
void gc_group_save(const GC_Chat *chat, Bin_Pack *bp);
/** @brief Creates a new group and adds it to the group sessions group array.
*
* The caller of this function has founder role privileges.
*
* The client should initiate its peer list with self info after calling this function, as
* the peer_join callback will not be triggered.
*
* 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 the group object fails to initialize.
* Return -4 if the group state fails to initialize.
* Return -5 if the Messenger friend connection fails to initialize.
*/
non_null()
int gc_group_add(GC_Session *c, Group_Privacy_State privacy_state, const uint8_t *group_name,
uint16_t group_name_length,
const uint8_t *nick, size_t nick_length);
/** @brief Joins a group designated by `chat_id`.
*
* This function creates a new GC_Chat object, adds it to the chats array, and sends a DHT
* announcement to find peers in the group associated with `chat_id`. Once a peer has been
* found a join attempt will be initiated.
*
* If the group is not password protected password should be set to NULL and password_length should be 0.
*
* 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 the Messenger friend connection fails to initialize.
*/
non_null(1, 2, 3) nullable(5)
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);
/** @brief Disconnects from all peers in a group but saves the group state for later use.
*
* Return true on sucess.
* Return false if the group handler object or chat object is null.
*/
non_null()
bool gc_disconnect_from_group(const GC_Session *c, GC_Chat *chat);
/** @brief Disconnects from all peers in a group and attempts to reconnect.
*
* All self state and credentials are retained.
*
* Returns 0 on success.
* Returns -1 if the group handler object or chat object is null.
* Returns -2 if the Messenger friend connection fails to initialize.
*/
non_null()
int gc_rejoin_group(GC_Session *c, GC_Chat *chat);
/** @brief Joins a group using the invite data received in a friend's group invite.
*
* The invite is only valid while the inviter is present in the group.
*
* 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 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.
*/
non_null(1, 3, 5) nullable(7)
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 bool gc_send_group_invite_packet_cb(const Messenger *m, uint32_t friendnumber, const uint8_t *packet,
uint16_t length);
/** @brief Invites friend designated by `friendnumber` to chat.
* Packet includes: Type, chat_id, TCP node or packed IP_Port.
*
* 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.
*/
non_null()
int gc_invite_friend(const GC_Session *c, GC_Chat *chat, int32_t friend_number,
gc_send_group_invite_packet_cb *callback);
/** @brief Leaves a group and sends an exit broadcast packet with an optional parting message.
*
* All group state is permanently lost, including keys and roles.
*
* Return 0 on success.
* Return -1 if the parting message is too long.
* Return -2 if the parting message failed to send.
*/
non_null(1, 2) nullable(3)
int gc_group_exit(GC_Session *c, GC_Chat *chat, const uint8_t *message, uint16_t length);
/** @brief Returns true if `chat` is a valid group chat.
*
* A valid group chat constitutes an initialized chat instance with a non-zero shared state version.
* The shared state version will be non-zero either if a peer has created the group, or if
* they have ever successfully connected to the group.
*/
non_null()
bool gc_group_is_valid(const GC_Chat *chat);
/** @brief Returns the number of active groups in `c`. */
non_null()
uint32_t gc_count_groups(const GC_Session *c);
/** @brief Returns true if peer_number exists */
non_null()
bool gc_peer_number_is_valid(const GC_Chat *chat, int peer_number);
/** @brief Return group_number's GC_Chat pointer on success
* Return NULL on failure
*/
non_null()
GC_Chat *gc_get_group(const GC_Session *c, int group_number);
/** @brief Sends a lossy message acknowledgement to peer associated with `gconn`.
*
* If `type` is GR_ACK_RECV we send a read-receipt for read_id's packet. If `type` is GR_ACK_REQ
* we send a request for the respective id's packet.
*
* Requests are limited to one per second per peer.
*
* @retval true on success.
*/
non_null()
bool gc_send_message_ack(const GC_Chat *chat, GC_Connection *gconn, uint64_t message_id, Group_Message_Ack_Type type);
/** @brief Helper function for `handle_gc_lossless_packet()`.
*
* Note: This function may modify the peer list and change peer numbers.
*
* @retval true if packet is successfully handled.
*/
non_null(1, 2) nullable(4, 7)
bool handle_gc_lossless_helper(const GC_Session *c, GC_Chat *chat, uint32_t peer_number, const uint8_t *data,
uint16_t length, uint8_t packet_type, void *userdata);
/** @brief Handles an invite accept packet.
*
* @retval true on success.
*/
non_null()
bool handle_gc_invite_accepted_packet(const GC_Session *c, int friend_number, const uint8_t *data, uint16_t length);
/** @brief Return true if `chat_id` is not present in our group sessions array.
*
* `length` must be at least CHAT_ID_SIZE bytes in length.
*/
non_null()
bool group_not_added(const GC_Session *c, const uint8_t *chat_id, uint32_t length);
/** @brief Handles an invite confirmed packet.
*
* Return 0 on success.
* Return -1 if length is invalid.
* Return -2 if data contains invalid chat_id.
* Return -3 if data contains invalid peer info.
* Return -4 if `friend_number` does not designate a valid friend.
* Return -5 if data contains invalid connection info.
*/
non_null()
int handle_gc_invite_confirmed_packet(const GC_Session *c, int friend_number, const uint8_t *data, uint16_t length);
/** @brief Returns the group designated by `public_key`.
* Returns null if group does not exist.
*/
non_null()
GC_Chat *gc_get_group_by_public_key(const GC_Session *c, const uint8_t *public_key);
/** @brief Attempts to add peers from `announces` to our peer list and initiate an invite request.
*
* Returns the number of peers added on success.
* Returns -1 on failure.
*/
non_null()
int gc_add_peers_from_announces(GC_Chat *chat, const GC_Announce *announces, uint8_t gc_announces_count);
#endif // GROUP_CHATS_H

View File

@ -0,0 +1,409 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2022 The TokTok team.
*/
/**
* Common groupchat data structures.
*/
#ifndef GROUP_COMMON_H
#define GROUP_COMMON_H
#include <stdbool.h>
#include <stdint.h>
#include "DHT.h"
#include "TCP_connection.h"
#include "group_moderation.h"
#define MAX_GC_PART_MESSAGE_SIZE 128
#define MAX_GC_NICK_SIZE 128
#define MAX_GC_TOPIC_SIZE 512
#define MAX_GC_GROUP_NAME_SIZE 48
#define GC_MESSAGE_PSEUDO_ID_SIZE 4
#define GROUP_MAX_MESSAGE_LENGTH 1372
/* Max size of a packet chunk. Packets larger than this must be split up.
*
* For an explanation on why this value was chosen, see the following link: https://archive.ph/vsCOG
*/
#define MAX_GC_PACKET_CHUNK_SIZE 500
#define MAX_GC_MESSAGE_SIZE GROUP_MAX_MESSAGE_LENGTH
#define MAX_GC_MESSAGE_RAW_SIZE (MAX_GC_MESSAGE_SIZE + GC_MESSAGE_PSEUDO_ID_SIZE)
#define MAX_GC_CUSTOM_LOSSLESS_PACKET_SIZE 1373
#define MAX_GC_CUSTOM_LOSSY_PACKET_SIZE MAX_GC_PACKET_CHUNK_SIZE
#define MAX_GC_PASSWORD_SIZE 32
#define MAX_GC_SAVED_INVITES 10
#define MAX_GC_PEERS_DEFAULT 100
#define MAX_GC_SAVED_TIMEOUTS 12
#define GC_MAX_SAVED_PEERS 100
#define GC_SAVED_PEER_SIZE (ENC_PUBLIC_KEY_SIZE + sizeof(Node_format) + sizeof(IP_Port))
/* Max size of a complete encrypted packet including headers. */
#define MAX_GC_PACKET_SIZE (MAX_GC_PACKET_CHUNK_SIZE * 100)
/* Max number of messages to store in the send/recv arrays */
#define GCC_BUFFER_SIZE 8192
/** Self UDP status. Must correspond to return values from `ipport_self_copy()`. */
typedef enum Self_UDP_Status {
SELF_UDP_STATUS_NONE = 0x00,
SELF_UDP_STATUS_WAN = 0x01,
SELF_UDP_STATUS_LAN = 0x02,
} Self_UDP_Status;
/** Group exit types. */
typedef enum Group_Exit_Type {
GC_EXIT_TYPE_QUIT = 0x00, // Peer left the group
GC_EXIT_TYPE_TIMEOUT = 0x01, // Peer connection timed out
GC_EXIT_TYPE_DISCONNECTED = 0x02, // Peer diconnected from group
GC_EXIT_TYPE_SELF_DISCONNECTED = 0x03, // Self disconnected from group
GC_EXIT_TYPE_KICKED = 0x04, // Peer was kicked from the group
GC_EXIT_TYPE_SYNC_ERR = 0x05, // Peer failed to sync with the group
GC_EXIT_TYPE_NO_CALLBACK = 0x06, // The peer exit callback should not be triggered
} Group_Exit_Type;
typedef struct GC_Exit_Info {
uint8_t part_message[MAX_GC_PART_MESSAGE_SIZE];
uint16_t length;
Group_Exit_Type exit_type;
} GC_Exit_Info;
typedef struct GC_PeerAddress {
uint8_t public_key[EXT_PUBLIC_KEY_SIZE];
IP_Port ip_port;
} GC_PeerAddress;
typedef struct GC_Message_Array_Entry {
uint8_t *data;
uint16_t data_length;
uint8_t packet_type;
uint64_t message_id;
uint64_t time_added;
uint64_t last_send_try;
} GC_Message_Array_Entry;
typedef struct GC_Connection {
uint64_t send_message_id; /* message_id of the next message we send to peer */
uint16_t send_array_start; /* send_array index of oldest item */
GC_Message_Array_Entry *send_array;
uint64_t received_message_id; /* message_id of peer's last message to us */
GC_Message_Array_Entry *recv_array;
uint64_t last_chunk_id; /* The message ID of the last packet fragment we received */
GC_PeerAddress addr; /* holds peer's extended real public key and ip_port */
uint32_t public_key_hash; /* Jenkins one at a time hash of peer's real encryption public key */
uint8_t session_public_key[ENC_PUBLIC_KEY_SIZE]; /* self session public key for this peer */
uint8_t session_secret_key[ENC_SECRET_KEY_SIZE]; /* self session secret key for this peer */
uint8_t session_shared_key[CRYPTO_SHARED_KEY_SIZE]; /* made with our session sk and peer's session pk */
int tcp_connection_num;
uint64_t last_sent_tcp_relays_time; /* the last time we attempted to send this peer our tcp relays */
uint16_t tcp_relay_share_index;
uint64_t last_received_direct_time; /* the last time we received a direct UDP packet from this connection */
uint64_t last_sent_ip_time; /* the last time we sent our ip info to this peer in a ping packet */
Node_format connected_tcp_relays[MAX_FRIEND_TCP_CONNECTIONS];
uint16_t tcp_relays_count;
uint64_t last_received_packet_time; /* The last time we successfully processed any packet from this peer */
uint64_t last_requested_packet_time; /* The last time we requested a missing packet from this peer */
uint64_t last_sent_ping_time;
uint64_t last_sync_response; /* the last time we sent this peer a sync response */
uint8_t oob_relay_pk[CRYPTO_PUBLIC_KEY_SIZE];
bool self_is_closer; /* true if we're "closer" to the chat_id than this peer (uses real pk's) */
bool confirmed; /* true if this peer has given us their info */
bool handshaked; /* true if we've successfully handshaked with this peer */
uint16_t handshake_attempts;
uint64_t last_handshake_request;
uint64_t last_handshake_response;
uint8_t pending_handshake_type;
bool is_pending_handshake_response;
bool is_oob_handshake;
uint64_t last_key_rotation; /* the last time we rotated session keys for this peer */
bool pending_key_rotation_request;
bool pending_delete; /* true if this peer has been marked for deletion */
GC_Exit_Info exit_info;
} GC_Connection;
/***
* Group roles. Roles are hierarchical in that each role has a set of privileges plus
* all the privileges of the roles below it.
*/
typedef enum Group_Role {
/** Group creator. All-powerful. Cannot be demoted or kicked. */
GR_FOUNDER = 0x00,
/**
* May promote or demote peers below them to any role below them.
* May also kick peers below them and set the topic.
*/
GR_MODERATOR = 0x01,
/** may interact normally with the group. */
GR_USER = 0x02,
/** May not interact with the group but may observe. */
GR_OBSERVER = 0x03,
} Group_Role;
typedef enum Group_Peer_Status {
GS_NONE = 0x00,
GS_AWAY = 0x01,
GS_BUSY = 0x02,
} Group_Peer_Status;
/**
* Group voice states. The state determines which Group Roles have permission to speak.
*/
typedef enum Group_Voice_State {
/** Every group role except Observers may speak. */
GV_ALL = 0x00,
/** Only Moderators and the Founder may speak. */
GV_MODS = 0x01,
/** Only the Founder may speak. */
GV_FOUNDER = 0x02,
} Group_Voice_State;
/** Group connection states. */
typedef enum GC_Conn_State {
CS_NONE = 0x00, // Indicates a group is not initialized
CS_DISCONNECTED = 0x01, // Not receiving or sending any packets
CS_CONNECTING = 0x02, // Attempting to establish a connection with peers in the group
CS_CONNECTED = 0x03, // Has successfully received a sync response from a peer in the group
} GC_Conn_State;
/** Group privacy states. */
typedef enum Group_Privacy_State {
GI_PUBLIC = 0x00, // Anyone with the chat ID may join the group
GI_PRIVATE = 0x01, // Peers may only join the group via a friend invite
} Group_Privacy_State;
/** Handshake join types. */
typedef enum Group_Handshake_Join_Type {
HJ_PUBLIC = 0x00, // Indicates the group was joined via the DHT
HJ_PRIVATE = 0x01, // Indicates the group was joined via private friend invite
} Group_Handshake_Join_Type;
typedef struct GC_SavedPeerInfo {
uint8_t public_key[ENC_PUBLIC_KEY_SIZE];
Node_format tcp_relay;
IP_Port ip_port;
} GC_SavedPeerInfo;
/** Holds info about peers who recently timed out */
typedef struct GC_TimedOutPeer {
GC_SavedPeerInfo addr;
uint64_t last_seen; // the time the peer disconnected
uint64_t last_reconn_try; // the last time we tried to establish a new connection
} GC_TimedOutPeer;
typedef struct GC_Peer {
/* Below state is sent to other peers in peer info exchange */
uint8_t nick[MAX_GC_NICK_SIZE];
uint16_t nick_length;
uint8_t status;
/* Below state is local only */
Group_Role role;
uint32_t peer_id; // permanent ID (used for the public API)
bool ignore;
GC_Connection gconn;
} GC_Peer;
typedef struct GC_SharedState {
uint32_t version;
uint8_t founder_public_key[EXT_PUBLIC_KEY_SIZE];
uint16_t maxpeers;
uint16_t group_name_len;
uint8_t group_name[MAX_GC_GROUP_NAME_SIZE];
Group_Privacy_State privacy_state; // GI_PUBLIC (uses DHT) or GI_PRIVATE (invite only)
uint16_t password_length;
uint8_t password[MAX_GC_PASSWORD_SIZE];
uint8_t mod_list_hash[MOD_MODERATION_HASH_SIZE];
uint32_t topic_lock; // equal to GC_TOPIC_LOCK_ENABLED when lock is enabled
Group_Voice_State voice_state;
} GC_SharedState;
typedef struct GC_TopicInfo {
uint32_t version;
uint16_t length;
uint16_t checksum; // used for syncing problems. the checksum with the highest value gets priority.
uint8_t topic[MAX_GC_TOPIC_SIZE];
uint8_t public_sig_key[SIG_PUBLIC_KEY_SIZE]; // Public signature key of the topic setter
} GC_TopicInfo;
typedef struct GC_Chat {
Mono_Time *mono_time;
const Logger *log;
const Random *rng;
uint32_t connected_tcp_relays;
Self_UDP_Status self_udp_status;
IP_Port self_ip_port;
Networking_Core *net;
TCP_Connections *tcp_conn;
uint64_t last_checked_tcp_relays;
Group_Handshake_Join_Type join_type;
GC_Peer *group;
Moderation moderation;
GC_Conn_State connection_state;
GC_SharedState shared_state;
uint8_t shared_state_sig[SIGNATURE_SIZE]; // signed by founder using the chat secret key
GC_TopicInfo topic_info;
uint8_t topic_sig[SIGNATURE_SIZE]; // signed by the peer who set the current topic
uint16_t topic_prev_checksum; // checksum of the previous topic
uint64_t topic_time_set;
uint16_t peers_checksum; // sum of the public key hash of every confirmed peer in the group
uint16_t roles_checksum; // sum of every confirmed peer's role plus the first byte of their public key
uint32_t numpeers;
int group_number;
uint8_t chat_public_key[EXT_PUBLIC_KEY_SIZE]; // the chat_id is the sig portion
uint8_t chat_secret_key[EXT_SECRET_KEY_SIZE]; // only used by the founder
uint8_t self_public_key[EXT_PUBLIC_KEY_SIZE];
uint8_t self_secret_key[EXT_SECRET_KEY_SIZE];
uint64_t time_connected;
uint64_t last_ping_interval;
uint64_t last_sync_request; // The last time we sent a sync request to any peer
uint64_t last_sync_response_peer_list; // The last time we sent the peer list to any peer
uint64_t last_time_peers_loaded;
/* keeps track of frequency of new inbound connections */
uint8_t connection_O_metre;
uint64_t connection_cooldown_timer;
bool block_handshakes;
int32_t saved_invites[MAX_GC_SAVED_INVITES];
uint8_t saved_invites_index;
/** A list of recently seen peers in case we disconnect from a private group.
* Peers are added once they're confirmed, and only if there are vacant
* spots (older connections get priority). An entry is removed only when the list
* is full, its respective peer goes offline, and an online peer who isn't yet
* present in the list can be added.
*/
GC_SavedPeerInfo saved_peers[GC_MAX_SAVED_PEERS];
GC_TimedOutPeer timeout_list[MAX_GC_SAVED_TIMEOUTS];
size_t timeout_list_index;
uint64_t last_timed_out_reconn_try; // the last time we tried to reconnect to timed out peers
bool update_self_announces; // true if we should try to update our announcements
uint64_t last_self_announce_check; // the last time we checked if we should update our announcements
uint64_t last_time_self_announce; // the last time we announced the group
uint8_t announced_tcp_relay_pk[CRYPTO_PUBLIC_KEY_SIZE]; // The pk of the last TCP relay we announced
uint8_t m_group_public_key[CRYPTO_PUBLIC_KEY_SIZE]; // public key for group's messenger friend connection
int friend_connection_id; // identifier for group's messenger friend connection
} GC_Chat;
#ifndef MESSENGER_DEFINED
#define MESSENGER_DEFINED
typedef struct Messenger Messenger;
#endif /* MESSENGER_DEFINED */
typedef void gc_message_cb(const Messenger *m, uint32_t group_number, uint32_t peer_id, unsigned int type,
const uint8_t *data, size_t length, uint32_t message_id, void *user_data);
typedef void gc_private_message_cb(const 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(const Messenger *m, uint32_t group_number, uint32_t peer_id, const uint8_t *data,
size_t length, void *user_data);
typedef void gc_custom_private_packet_cb(const Messenger *m, uint32_t group_number, uint32_t peer_id,
const uint8_t *data,
size_t length, void *user_data);
typedef void gc_moderation_cb(const Messenger *m, uint32_t group_number, uint32_t peer_id, uint32_t target_peer,
unsigned int mod_event, void *user_data);
typedef void gc_nick_change_cb(const Messenger *m, uint32_t group_number, uint32_t peer_id, const uint8_t *data,
size_t length, void *user_data);
typedef void gc_status_change_cb(const Messenger *m, uint32_t group_number, uint32_t peer_id, unsigned int status,
void *user_data);
typedef void gc_topic_change_cb(const Messenger *m, uint32_t group_number, uint32_t peer_id, const uint8_t *data,
size_t length, void *user_data);
typedef void gc_topic_lock_cb(const Messenger *m, uint32_t group_number, unsigned int topic_lock, void *user_data);
typedef void gc_voice_state_cb(const Messenger *m, uint32_t group_number, unsigned int voice_state, void *user_data);
typedef void gc_peer_limit_cb(const Messenger *m, uint32_t group_number, uint32_t max_peers, void *user_data);
typedef void gc_privacy_state_cb(const Messenger *m, uint32_t group_number, unsigned int state, void *user_data);
typedef void gc_password_cb(const Messenger *m, uint32_t group_number, const uint8_t *data, size_t length,
void *user_data);
typedef void gc_peer_join_cb(const Messenger *m, uint32_t group_number, uint32_t peer_id, void *user_data);
typedef void gc_peer_exit_cb(const Messenger *m, uint32_t group_number, uint32_t peer_id, unsigned int exit_type,
const uint8_t *nick, size_t nick_len, const uint8_t *data, size_t length, void *user_data);
typedef void gc_self_join_cb(const Messenger *m, uint32_t group_number, void *user_data);
typedef void gc_rejected_cb(const Messenger *m, uint32_t group_number, unsigned int type, void *user_data);
typedef struct GC_Session {
Messenger *messenger;
GC_Chat *chats;
struct GC_Announces_List *announces_list;
uint32_t chats_index;
gc_message_cb *message;
gc_private_message_cb *private_message;
gc_custom_packet_cb *custom_packet;
gc_custom_private_packet_cb *custom_private_packet;
gc_moderation_cb *moderation;
gc_nick_change_cb *nick_change;
gc_status_change_cb *status_change;
gc_topic_change_cb *topic_change;
gc_topic_lock_cb *topic_lock;
gc_voice_state_cb *voice_state;
gc_peer_limit_cb *peer_limit;
gc_privacy_state_cb *privacy_state;
gc_password_cb *password;
gc_peer_join_cb *peer_join;
gc_peer_exit_cb *peer_exit;
gc_self_join_cb *self_join;
gc_rejected_cb *rejected;
} GC_Session;
/** @brief Adds a new peer to group_number's peer list.
*
* Return peer_number on success.
* Return -1 on failure.
* Return -2 if a peer with public_key is already in our peerlist.
*/
non_null(1, 3) nullable(2)
int peer_add(GC_Chat *chat, const IP_Port *ipp, const uint8_t *public_key);
/** @brief Unpacks saved peers from `data` of size `length` into `chat`.
*
* Returns the number of unpacked peers on success.
* Returns -1 on failure.
*/
non_null()
int unpack_gc_saved_peers(GC_Chat *chat, const uint8_t *data, uint16_t length);
/** @brief Packs all valid entries from saved peerlist into `data`.
*
* If `processed` is non-null it will be set to the length of the packed data.
*
* Return the number of packed saved peers on success.
* Return -1 if buffer is too small.
*/
non_null(1, 2) nullable(4)
int pack_gc_saved_peers(const GC_Chat *chat, uint8_t *data, uint16_t length, uint16_t *processed);
#endif // GROUP_COMMON_H

View File

@ -0,0 +1,707 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2020 The TokTok team.
* Copyright © 2015 Tox project.
*/
/**
* An implementation of massive text only group chats.
*/
#include "group_connection.h"
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "DHT.h"
#include "ccompat.h"
#include "crypto_core.h"
#include "group_chats.h"
#include "group_common.h"
#include "mono_time.h"
#include "util.h"
#ifndef VANILLA_NACL
/** Seconds since last direct UDP packet was received before the connection is considered dead */
#define GCC_UDP_DIRECT_TIMEOUT (GC_PING_TIMEOUT + 4)
/** Returns true if array entry does not contain an active packet. */
non_null()
static bool array_entry_is_empty(const GC_Message_Array_Entry *array_entry)
{
assert(array_entry != nullptr);
return array_entry->time_added == 0;
}
/** @brief Clears an array entry. */
non_null()
static void clear_array_entry(GC_Message_Array_Entry *const array_entry)
{
if (array_entry->data != nullptr) {
free(array_entry->data);
}
*array_entry = (GC_Message_Array_Entry) {
nullptr
};
}
/**
* Clears every send array message from queue starting at the index designated by
* `start_id` and ending at `end_id`, and sets the send_message_id for `gconn`
* to `start_id`.
*/
non_null()
static void clear_send_queue_id_range(GC_Connection *gconn, uint64_t start_id, uint64_t end_id)
{
const uint16_t start_idx = gcc_get_array_index(start_id);
const uint16_t end_idx = gcc_get_array_index(end_id);
for (uint16_t i = start_idx; i != end_idx; i = (i + 1) % GCC_BUFFER_SIZE) {
GC_Message_Array_Entry *entry = &gconn->send_array[i];
clear_array_entry(entry);
}
gconn->send_message_id = start_id;
}
uint16_t gcc_get_array_index(uint64_t message_id)
{
return message_id % GCC_BUFFER_SIZE;
}
void gcc_set_send_message_id(GC_Connection *gconn, uint64_t id)
{
gconn->send_message_id = id;
gconn->send_array_start = id % GCC_BUFFER_SIZE;
}
void gcc_set_recv_message_id(GC_Connection *gconn, uint64_t id)
{
gconn->received_message_id = id;
}
/** @brief Puts packet data in array_entry.
*
* Return true on success.
*/
non_null(1, 2) nullable(3)
static bool create_array_entry(const Mono_Time *mono_time, GC_Message_Array_Entry *array_entry, const uint8_t *data,
uint16_t length, uint8_t packet_type, uint64_t message_id)
{
if (length > 0) {
if (data == nullptr) {
return false;
}
array_entry->data = (uint8_t *)malloc(sizeof(uint8_t) * length);
if (array_entry->data == nullptr) {
return false;
}
memcpy(array_entry->data, data, length);
}
const uint64_t tm = 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 = tm;
array_entry->last_send_try = tm;
return true;
}
/** @brief Adds data of length to gconn's send_array.
*
* Returns true on success and increments gconn's send_message_id.
*/
non_null(1, 2, 3) nullable(4)
static bool add_to_send_array(const Logger *log, const Mono_Time *mono_time, GC_Connection *gconn, const uint8_t *data,
uint16_t length, uint8_t packet_type)
{
/* check if send_array is full */
if ((gconn->send_message_id % GCC_BUFFER_SIZE) == (uint16_t)(gconn->send_array_start - 1)) {
LOGGER_DEBUG(log, "Send array overflow");
return false;
}
const uint16_t idx = gcc_get_array_index(gconn->send_message_id);
GC_Message_Array_Entry *array_entry = &gconn->send_array[idx];
if (!array_entry_is_empty(array_entry)) {
LOGGER_DEBUG(log, "Send array entry isn't empty");
return false;
}
if (!create_array_entry(mono_time, array_entry, data, length, packet_type, gconn->send_message_id)) {
LOGGER_WARNING(log, "Failed to create array entry");
return false;
}
++gconn->send_message_id;
return true;
}
int gcc_send_lossless_packet(const GC_Chat *chat, GC_Connection *gconn, const uint8_t *data, uint16_t length,
uint8_t packet_type)
{
const uint64_t message_id = gconn->send_message_id;
if (!add_to_send_array(chat->log, chat->mono_time, gconn, data, length, packet_type)) {
LOGGER_WARNING(chat->log, "Failed to add payload to send array: (type: 0x%02x, length: %d)", packet_type, length);
return -1;
}
if (!gcc_encrypt_and_send_lossless_packet(chat, gconn, data, length, message_id, packet_type)) {
LOGGER_DEBUG(chat->log, "Failed to send payload: (type: 0x%02x, length: %d)", packet_type, length);
return -2;
}
return 0;
}
bool gcc_send_lossless_packet_fragments(const GC_Chat *chat, GC_Connection *gconn, const uint8_t *data,
uint16_t length, uint8_t packet_type)
{
if (length <= MAX_GC_PACKET_CHUNK_SIZE || data == nullptr) {
LOGGER_FATAL(chat->log, "invalid length or null data pointer");
return false;
}
const uint16_t start_id = gconn->send_message_id;
// First packet segment is comprised of packet type + first chunk of payload
uint8_t chunk[MAX_GC_PACKET_CHUNK_SIZE];
chunk[0] = packet_type;
memcpy(chunk + 1, data, MAX_GC_PACKET_CHUNK_SIZE - 1);
if (!add_to_send_array(chat->log, chat->mono_time, gconn, chunk, MAX_GC_PACKET_CHUNK_SIZE, GP_FRAGMENT)) {
return false;
}
uint16_t processed = MAX_GC_PACKET_CHUNK_SIZE - 1;
// The rest of the segments are added in chunks
while (length > processed) {
const uint16_t chunk_len = min_u16(MAX_GC_PACKET_CHUNK_SIZE, length - processed);
memcpy(chunk, data + processed, chunk_len);
processed += chunk_len;
if (!add_to_send_array(chat->log, chat->mono_time, gconn, chunk, chunk_len, GP_FRAGMENT)) {
clear_send_queue_id_range(gconn, start_id, gconn->send_message_id);
return false;
}
}
// empty packet signals the end of the sequence
if (!add_to_send_array(chat->log, chat->mono_time, gconn, nullptr, 0, GP_FRAGMENT)) {
clear_send_queue_id_range(gconn, start_id, gconn->send_message_id);
return false;
}
const uint16_t start_idx = gcc_get_array_index(start_id);
const uint16_t end_idx = gcc_get_array_index(gconn->send_message_id);
for (uint16_t i = start_idx; i != end_idx; i = (i + 1) % GCC_BUFFER_SIZE) {
GC_Message_Array_Entry *entry = &gconn->send_array[i];
if (array_entry_is_empty(entry)) {
LOGGER_FATAL(chat->log, "array entry for packet chunk is empty");
return false;
}
assert(entry->packet_type == GP_FRAGMENT);
gcc_encrypt_and_send_lossless_packet(chat, gconn, entry->data, entry->data_length,
entry->message_id, entry->packet_type);
}
return true;
}
bool gcc_handle_ack(const Logger *log, GC_Connection *gconn, uint64_t message_id)
{
uint16_t idx = gcc_get_array_index(message_id);
GC_Message_Array_Entry *array_entry = &gconn->send_array[idx];
if (array_entry_is_empty(array_entry)) {
return true;
}
if (array_entry->message_id != message_id) { // wrap-around indicates a connection problem
LOGGER_DEBUG(log, "Wrap-around on message %llu", (unsigned long long)message_id);
return false;
}
clear_array_entry(array_entry);
/* Put send_array_start in proper position */
if (idx == gconn->send_array_start) {
const uint16_t end = gconn->send_message_id % GCC_BUFFER_SIZE;
while (array_entry_is_empty(&gconn->send_array[idx]) && gconn->send_array_start != end) {
gconn->send_array_start = (gconn->send_array_start + 1) % GCC_BUFFER_SIZE;
idx = (idx + 1) % GCC_BUFFER_SIZE;
}
}
return true;
}
bool gcc_ip_port_is_set(const GC_Connection *gconn)
{
return ipport_isset(&gconn->addr.ip_port);
}
void gcc_set_ip_port(GC_Connection *gconn, const IP_Port *ipp)
{
if (ipp != nullptr && ipport_isset(ipp)) {
gconn->addr.ip_port = *ipp;
}
}
bool gcc_copy_tcp_relay(const Random *rng, Node_format *tcp_node, const GC_Connection *gconn)
{
if (gconn == nullptr || tcp_node == nullptr) {
return false;
}
if (gconn->tcp_relays_count == 0) {
return false;
}
const uint32_t rand_idx = random_range_u32(rng, gconn->tcp_relays_count);
if (!ipport_isset(&gconn->connected_tcp_relays[rand_idx].ip_port)) {
return false;
}
*tcp_node = gconn->connected_tcp_relays[rand_idx];
return true;
}
int gcc_save_tcp_relay(const Random *rng, GC_Connection *gconn, const Node_format *tcp_node)
{
if (gconn == nullptr || tcp_node == nullptr) {
return -1;
}
if (!ipport_isset(&tcp_node->ip_port)) {
return -1;
}
for (uint16_t i = 0; i < gconn->tcp_relays_count; ++i) {
if (pk_equal(gconn->connected_tcp_relays[i].public_key, tcp_node->public_key)) {
return -2;
}
}
uint32_t idx = gconn->tcp_relays_count;
if (gconn->tcp_relays_count >= MAX_FRIEND_TCP_CONNECTIONS) {
idx = random_range_u32(rng, gconn->tcp_relays_count);
} else {
++gconn->tcp_relays_count;
}
gconn->connected_tcp_relays[idx] = *tcp_node;
return 0;
}
/** @brief Stores `data` of length `length` in the receive array for `gconn`.
*
* Return true on success.
*/
non_null(1, 2, 3) nullable(4)
static bool store_in_recv_array(const Logger *log, const Mono_Time *mono_time, GC_Connection *gconn,
const uint8_t *data,
uint16_t length, uint8_t packet_type, uint64_t message_id)
{
const uint16_t idx = gcc_get_array_index(message_id);
GC_Message_Array_Entry *ary_entry = &gconn->recv_array[idx];
if (!array_entry_is_empty(ary_entry)) {
LOGGER_DEBUG(log, "Recv array is not empty");
return false;
}
if (!create_array_entry(mono_time, ary_entry, data, length, packet_type, message_id)) {
LOGGER_WARNING(log, "Failed to create array entry");
return false;
}
return true;
}
/**
* Reassembles a fragmented packet sequence ending with the data in the receive
* array at slot `message_id - 1` and starting with the last found slot containing
* a GP_FRAGMENT packet when searching backwards in the array.
*
* The fully reassembled packet is stored in `payload`, which must be passed as a
* null pointer, and must be free'd by the caller.
*
* Return the length of the fully reassembled packet on success.
* Return 0 on failure.
*/
non_null(1, 3) nullable(2)
static uint16_t reassemble_packet(const Logger *log, GC_Connection *gconn, uint8_t **payload, uint64_t message_id)
{
uint16_t end_idx = gcc_get_array_index(message_id - 1);
uint16_t start_idx = end_idx;
uint16_t packet_length = 0;
GC_Message_Array_Entry *entry = &gconn->recv_array[end_idx];
// search backwards in recv array until we find an empty slot or a non-fragment packet type
while (!array_entry_is_empty(entry) && entry->packet_type == GP_FRAGMENT) {
assert(entry->data != nullptr);
assert(entry->data_length <= MAX_GC_PACKET_CHUNK_SIZE);
const uint16_t diff = packet_length + entry->data_length;
assert(diff > packet_length); // overflow check
packet_length = diff;
if (packet_length > MAX_GC_PACKET_SIZE) {
LOGGER_ERROR(log, "Payload of size %u exceeded max packet size", packet_length); // should never happen
return 0;
}
start_idx = start_idx > 0 ? start_idx - 1 : GCC_BUFFER_SIZE - 1;
entry = &gconn->recv_array[start_idx];
if (start_idx == end_idx) {
LOGGER_ERROR(log, "Packet reassemble wrap-around");
return 0;
}
}
if (packet_length == 0) {
return 0;
}
assert(*payload == nullptr);
*payload = (uint8_t *)malloc(packet_length);
if (*payload == nullptr) {
LOGGER_ERROR(log, "Failed to allocate %u bytes for payload buffer", packet_length);
return 0;
}
start_idx = (start_idx + 1) % GCC_BUFFER_SIZE;
end_idx = (end_idx + 1) % GCC_BUFFER_SIZE;
uint16_t processed = 0;
for (uint16_t i = start_idx; i != end_idx; i = (i + 1) % GCC_BUFFER_SIZE) {
entry = &gconn->recv_array[i];
assert(processed + entry->data_length <= packet_length);
memcpy(*payload + processed, entry->data, entry->data_length);
processed += entry->data_length;
clear_array_entry(entry);
}
return processed;
}
int gcc_handle_packet_fragment(const GC_Session *c, GC_Chat *chat, uint32_t peer_number,
GC_Connection *gconn, const uint8_t *chunk, uint16_t length, uint8_t packet_type,
uint64_t message_id, void *userdata)
{
if (length > 0) {
if (!store_in_recv_array(chat->log, chat->mono_time, gconn, chunk, length, packet_type, message_id)) {
return -1;
}
gcc_set_recv_message_id(gconn, gconn->received_message_id + 1);
gconn->last_chunk_id = message_id;
return 1;
}
uint8_t sender_pk[ENC_PUBLIC_KEY_SIZE];
memcpy(sender_pk, get_enc_key(gconn->addr.public_key), ENC_PUBLIC_KEY_SIZE);
uint8_t *payload = nullptr;
const uint16_t processed_len = reassemble_packet(chat->log, gconn, &payload, message_id);
if (processed_len == 0) {
free(payload);
return -1;
}
if (!handle_gc_lossless_helper(c, chat, peer_number, payload + 1, processed_len - 1, payload[0], userdata)) {
free(payload);
return -1;
}
/* peer number can change from peer add operations in packet handlers */
peer_number = get_peer_number_of_enc_pk(chat, sender_pk, false);
gconn = get_gc_connection(chat, peer_number);
if (gconn == nullptr) {
return 0;
}
gcc_set_recv_message_id(gconn, gconn->received_message_id + 1);
gconn->last_chunk_id = 0;
free(payload);
return 0;
}
int gcc_handle_received_message(const Logger *log, const Mono_Time *mono_time, GC_Connection *gconn,
const uint8_t *data, uint16_t length, uint8_t packet_type, uint64_t message_id,
bool direct_conn)
{
if (direct_conn) {
gconn->last_received_direct_time = mono_time_get(mono_time);
}
/* Appears to be a duplicate packet so we discard it */
if (message_id < gconn->received_message_id + 1) {
return 0;
}
if (packet_type == GP_FRAGMENT) { // we handle packet fragments as a special case
return 3;
}
/* we're missing an older message from this peer so we store it in recv_array */
if (message_id > gconn->received_message_id + 1) {
if (!store_in_recv_array(log, mono_time, gconn, data, length, packet_type, message_id)) {
return -1;
}
return 1;
}
gcc_set_recv_message_id(gconn, gconn->received_message_id + 1);
return 2;
}
/** @brief Handles peer_number's array entry with appropriate handler and clears it from array.
*
* This function increments the received message ID for `gconn`.
*
* Return true on success.
*/
non_null(1, 2, 3, 5) nullable(6)
static bool process_recv_array_entry(const GC_Session *c, GC_Chat *chat, GC_Connection *gconn, uint32_t peer_number,
GC_Message_Array_Entry *const array_entry, void *userdata)
{
uint8_t sender_pk[ENC_PUBLIC_KEY_SIZE];
memcpy(sender_pk, get_enc_key(gconn->addr.public_key), ENC_PUBLIC_KEY_SIZE);
const bool ret = handle_gc_lossless_helper(c, chat, peer_number, array_entry->data, array_entry->data_length,
array_entry->packet_type, userdata);
/* peer number can change from peer add operations in packet handlers */
peer_number = get_peer_number_of_enc_pk(chat, sender_pk, false);
gconn = get_gc_connection(chat, peer_number);
clear_array_entry(array_entry);
if (gconn == nullptr) {
return true;
}
if (!ret) {
gc_send_message_ack(chat, gconn, array_entry->message_id, GR_ACK_REQ);
return false;
}
gc_send_message_ack(chat, gconn, array_entry->message_id, GR_ACK_RECV);
gcc_set_recv_message_id(gconn, gconn->received_message_id + 1);
return true;
}
void gcc_check_recv_array(const GC_Session *c, GC_Chat *chat, GC_Connection *gconn, uint32_t peer_number,
void *userdata)
{
if (gconn->last_chunk_id != 0) { // dont check array if we have an unfinished fragment sequence
return;
}
const uint16_t idx = (gconn->received_message_id + 1) % GCC_BUFFER_SIZE;
GC_Message_Array_Entry *const array_entry = &gconn->recv_array[idx];
if (!array_entry_is_empty(array_entry)) {
process_recv_array_entry(c, chat, gconn, peer_number, array_entry, userdata);
}
}
void gcc_resend_packets(const GC_Chat *chat, GC_Connection *gconn)
{
const uint64_t tm = mono_time_get(chat->mono_time);
const uint16_t start = gconn->send_array_start;
const uint16_t end = gconn->send_message_id % GCC_BUFFER_SIZE;
GC_Message_Array_Entry *array_entry = &gconn->send_array[start];
if (array_entry_is_empty(array_entry)) {
return;
}
if (mono_time_is_timeout(chat->mono_time, array_entry->time_added, GC_CONFIRMED_PEER_TIMEOUT)) {
gcc_mark_for_deletion(gconn, chat->tcp_conn, GC_EXIT_TYPE_TIMEOUT, nullptr, 0);
LOGGER_DEBUG(chat->log, "Send array stuck; timing out peer");
return;
}
for (uint16_t i = start; i != end; i = (i + 1) % GCC_BUFFER_SIZE) {
array_entry = &gconn->send_array[i];
if (array_entry_is_empty(array_entry)) {
continue;
}
if (tm == array_entry->last_send_try) {
continue;
}
const 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_encrypt_and_send_lossless_packet(chat, gconn, array_entry->data, array_entry->data_length,
array_entry->message_id, array_entry->packet_type);
}
}
}
bool gcc_send_packet(const GC_Chat *chat, const GC_Connection *gconn, const uint8_t *packet, uint16_t length)
{
if (packet == nullptr || length == 0) {
return false;
}
bool direct_send_attempt = false;
if (gcc_direct_conn_is_possible(chat, gconn)) {
if (gcc_conn_is_direct(chat->mono_time, gconn)) {
return (uint16_t) sendpacket(chat->net, &gconn->addr.ip_port, packet, length) == length;
}
if ((uint16_t) sendpacket(chat->net, &gconn->addr.ip_port, packet, length) == length) {
direct_send_attempt = true;
}
}
const int ret = send_packet_tcp_connection(chat->tcp_conn, gconn->tcp_connection_num, packet, length);
return ret == 0 || direct_send_attempt;
}
bool gcc_encrypt_and_send_lossless_packet(const GC_Chat *chat, const GC_Connection *gconn, const uint8_t *data,
uint16_t length, uint64_t message_id, uint8_t packet_type)
{
const uint16_t packet_size = gc_get_wrapped_packet_size(length, NET_PACKET_GC_LOSSLESS);
uint8_t *packet = (uint8_t *)malloc(packet_size);
if (packet == nullptr) {
LOGGER_ERROR(chat->log, "Failed to allocate memory for packet buffer");
return false;
}
const int enc_len = group_packet_wrap(
chat->log, chat->rng, chat->self_public_key, gconn->session_shared_key, packet,
packet_size, data, length, message_id, packet_type, NET_PACKET_GC_LOSSLESS);
if (enc_len < 0) {
LOGGER_ERROR(chat->log, "Failed to wrap packet (type: 0x%02x, error: %d)", packet_type, enc_len);
free(packet);
return false;
}
if (!gcc_send_packet(chat, gconn, packet, (uint16_t)enc_len)) {
LOGGER_DEBUG(chat->log, "Failed to send packet (type: 0x%02x, enc_len: %d)", packet_type, enc_len);
free(packet);
return false;
}
free(packet);
return true;
}
void gcc_make_session_shared_key(GC_Connection *gconn, const uint8_t *sender_pk)
{
encrypt_precompute(sender_pk, gconn->session_secret_key, gconn->session_shared_key);
}
bool gcc_conn_is_direct(const Mono_Time *mono_time, const GC_Connection *gconn)
{
return GCC_UDP_DIRECT_TIMEOUT + gconn->last_received_direct_time > mono_time_get(mono_time);
}
bool gcc_direct_conn_is_possible(const GC_Chat *chat, const GC_Connection *gconn)
{
return !net_family_is_unspec(gconn->addr.ip_port.ip.family) && !net_family_is_unspec(net_family(chat->net));
}
void gcc_mark_for_deletion(GC_Connection *gconn, TCP_Connections *tcp_conn, Group_Exit_Type type,
const uint8_t *part_message, uint16_t length)
{
if (gconn == nullptr) {
return;
}
if (gconn->pending_delete) {
return;
}
gconn->pending_delete = true;
gconn->exit_info.exit_type = type;
kill_tcp_connection_to(tcp_conn, gconn->tcp_connection_num);
if (length > 0 && length <= MAX_GC_PART_MESSAGE_SIZE && part_message != nullptr) {
memcpy(gconn->exit_info.part_message, part_message, length);
gconn->exit_info.length = length;
}
}
void gcc_peer_cleanup(GC_Connection *gconn)
{
for (size_t i = 0; i < GCC_BUFFER_SIZE; ++i) {
free(gconn->send_array[i].data);
free(gconn->recv_array[i].data);
}
free(gconn->recv_array);
free(gconn->send_array);
crypto_memunlock(gconn->session_secret_key, sizeof(gconn->session_secret_key));
crypto_memunlock(gconn->session_shared_key, sizeof(gconn->session_shared_key));
crypto_memzero(gconn, sizeof(GC_Connection));
}
void gcc_cleanup(const GC_Chat *chat)
{
for (uint32_t i = 0; i < chat->numpeers; ++i) {
GC_Connection *gconn = get_gc_connection(chat, i);
assert(gconn != nullptr);
gcc_peer_cleanup(gconn);
}
}
#endif // VANILLA_NACL

View File

@ -0,0 +1,189 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2020 The TokTok team.
* Copyright © 2015 Tox project.
*/
/**
* An implementation of massive text only group chats.
*/
#ifndef GROUP_CONNECTION_H
#define GROUP_CONNECTION_H
#include "group_common.h"
/* Max number of TCP relays we share with a peer on handshake */
#define GCC_MAX_TCP_SHARED_RELAYS 3
/** Marks a peer for deletion. If gconn is null or already marked for deletion this function has no effect. */
non_null(1, 2) nullable(4)
void gcc_mark_for_deletion(GC_Connection *gconn, TCP_Connections *tcp_conn, Group_Exit_Type type,
const uint8_t *part_message, uint16_t length);
/** @brief Decides if message need to be put in recv_array or immediately handled.
*
* Return 3 if message is in correct sequence and is a fragment packet.
* 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_array.
* Return 0 if message is a duplicate.
* Return -1 on failure
*/
non_null(1, 2, 3) nullable(4)
int gcc_handle_received_message(const Logger *log, const Mono_Time *mono_time, GC_Connection *gconn,
const uint8_t *data, uint16_t length, uint8_t packet_type, uint64_t message_id,
bool direct_conn);
/** @brief Handles a packet fragment.
*
* If the fragment is incomplete, it gets stored in the recv
* array. Otherwise the segment is re-assembled into a complete
* payload and processed.
*
* Return 1 if fragment is successfully handled and is not the end of the sequence.
* Return 0 if fragment is the end of a sequence and successfully handled.
* Return -1 on failure.
*/
non_null(1, 2, 4) nullable(5, 9)
int gcc_handle_packet_fragment(const GC_Session *c, GC_Chat *chat, uint32_t peer_number, GC_Connection *gconn,
const uint8_t *chunk, uint16_t length, uint8_t packet_type, uint64_t message_id,
void *userdata);
/** @brief Return array index for message_id */
uint16_t gcc_get_array_index(uint64_t message_id);
/** @brief Removes send_array item with message_id.
*
* Return true on success.
*/
non_null()
bool gcc_handle_ack(const Logger *log, GC_Connection *gconn, uint64_t message_id);
/** @brief Sets the send_message_id and send_array_start for `gconn` to `id`.
*
* This should only be used to initialize a new lossless connection.
*/
non_null()
void gcc_set_send_message_id(GC_Connection *gconn, uint64_t id);
/** @brief Sets the received_message_id for `gconn` to `id`. */
non_null()
void gcc_set_recv_message_id(GC_Connection *gconn, uint64_t id);
/**
* @brief Returns true if the ip_port is set for gconn.
*/
non_null()
bool gcc_ip_port_is_set(const GC_Connection *gconn);
/**
* @brief Sets the ip_port for gconn to ipp.
*
* If ipp is not set this function has no effect.
*/
non_null(1) nullable(2)
void gcc_set_ip_port(GC_Connection *gconn, const IP_Port *ipp);
/** @brief Copies a random TCP relay node from gconn to tcp_node.
*
* Return true on success.
*/
non_null()
bool gcc_copy_tcp_relay(const Random *rng, Node_format *tcp_node, const GC_Connection *gconn);
/** @brief Saves tcp_node to gconn's list of connected tcp relays.
*
* If relays list is full a random node is overwritten with the new node.
*
* Return 0 on success.
* Return -1 on failure.
* Return -2 if node is already in list.
*/
non_null()
int gcc_save_tcp_relay(const Random *rng, GC_Connection *gconn, const Node_format *tcp_node);
/** @brief Checks for and handles messages that are in proper sequence in gconn's recv_array.
* This should always be called after a new packet is successfully handled.
*/
non_null(1, 2, 3) nullable(5)
void gcc_check_recv_array(const GC_Session *c, GC_Chat *chat, GC_Connection *gconn, uint32_t peer_number,
void *userdata);
/** @brief Attempts to re-send lossless packets that have not yet received an ack. */
non_null()
void gcc_resend_packets(const GC_Chat *chat, GC_Connection *gconn);
/**
* Uses public encryption key `sender_pk` and the shared secret key associated with `gconn`
* to generate a shared 32-byte encryption key that can be used by the owners of both keys for symmetric
* encryption and decryption.
*
* Puts the result in the shared session key buffer for `gconn`, which must have room for
* CRYPTO_SHARED_KEY_SIZE bytes. This resulting shared key should be treated as a secret key.
*/
non_null()
void gcc_make_session_shared_key(GC_Connection *gconn, const uint8_t *sender_pk);
/** @brief Return true if we have a direct connection with `gconn`. */
non_null()
bool gcc_conn_is_direct(const Mono_Time *mono_time, const GC_Connection *gconn);
/** @brief Return true if a direct UDP connection is possible with `gconn`. */
non_null()
bool gcc_direct_conn_is_possible(const GC_Chat *chat, const GC_Connection *gconn);
/** @brief Sends a packet to the peer associated with gconn.
*
* This is a lower level function that does not encrypt or wrap the packet.
*
* Return true on success.
*/
non_null()
bool gcc_send_packet(const GC_Chat *chat, const GC_Connection *gconn, const uint8_t *packet, uint16_t length);
/** @brief Sends a lossless packet to `gconn` comprised of `data` of size `length`.
*
* This function will add the packet to the lossless send array, encrypt/wrap it using the
* shared key associated with `gconn`, and send it over the wire.
*
* Return 0 on success.
* Return -1 if the packet couldn't be added to the send array.
* Return -2 if the packet failed to be encrypted or failed to send.
*/
non_null(1, 2) nullable(3)
int gcc_send_lossless_packet(const GC_Chat *chat, GC_Connection *gconn, const uint8_t *data, uint16_t length,
uint8_t packet_type);
/** @brief Splits a lossless packet up into fragments, wraps each fragment in a GP_FRAGMENT
* header, encrypts them, and send them in succession.
*
* This function will first try to add each packet fragment to the send array as an atomic
* unit. If any chunk fails to be added the process will be reversed and an error will be
* returned. Otherwise it will then try to send all the fragments in succession.
*
* Return true if all fragments are successfully added to the send array.
*/
non_null()
bool gcc_send_lossless_packet_fragments(const GC_Chat *chat, GC_Connection *gconn, const uint8_t *data,
uint16_t length, uint8_t packet_type);
/** @brief Encrypts `data` of `length` bytes, designated by `message_id`, using the shared key
* associated with `gconn` and sends lossless packet over the wire.
*
* This function does not add the packet to the send array.
*
* Return true on success.
*/
non_null(1, 2) nullable(3)
bool gcc_encrypt_and_send_lossless_packet(const GC_Chat *chat, const GC_Connection *gconn, const uint8_t *data,
uint16_t length, uint64_t message_id, uint8_t packet_type);
/** @brief Called when a peer leaves the group. */
non_null()
void gcc_peer_cleanup(GC_Connection *gconn);
/** @brief Called on group exit. */
non_null()
void gcc_cleanup(const GC_Chat *chat);
#endif // GROUP_CONNECTION_H

View File

@ -0,0 +1,868 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2020 The TokTok team.
* Copyright © 2015 Tox project.
*/
/**
* An implementation of massive text only group chats.
*/
#include "group_moderation.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "ccompat.h"
#include "crypto_core.h"
#include "mono_time.h"
#include "network.h"
#include "util.h"
static_assert(MOD_SANCTIONS_CREDS_SIZE <= MAX_PACKET_SIZE_NO_HEADERS,
"MOD_SANCTIONS_CREDS_SIZE must be <= the maximum allowed payload size");
static_assert(MOD_MAX_NUM_SANCTIONS * MOD_SANCTION_PACKED_SIZE + MOD_SANCTIONS_CREDS_SIZE <= MAX_PACKET_SIZE_NO_HEADERS,
"MOD_MAX_NUM_SANCTIONS must be able to fit inside the maximum allowed payload size");
static_assert(MOD_MAX_NUM_MODERATORS * MOD_LIST_ENTRY_SIZE <= MAX_PACKET_SIZE_NO_HEADERS,
"MOD_MAX_NUM_MODERATORS must be able to fit insize the maximum allowed payload size");
static_assert(MOD_MAX_NUM_MODERATORS <= MOD_MAX_NUM_MODERATORS_LIMIT,
"MOD_MAX_NUM_MODERATORS must be <= MOD_MAX_NUM_MODERATORS_LIMIT");
static_assert(MOD_MAX_NUM_SANCTIONS <= MOD_MAX_NUM_SANCTIONS_LIMIT,
"MOD_MAX_NUM_SANCTIONS must be <= MOD_MAX_NUM_SANCTIONS_LIMIT");
uint16_t mod_list_packed_size(const Moderation *moderation)
{
return moderation->num_mods * MOD_LIST_ENTRY_SIZE;
}
int mod_list_unpack(Moderation *moderation, const uint8_t *data, uint16_t length, uint16_t num_mods)
{
if (length < num_mods * MOD_LIST_ENTRY_SIZE) {
return -1;
}
mod_list_cleanup(moderation);
if (num_mods == 0) {
return 0;
}
uint8_t **tmp_list = (uint8_t **)calloc(num_mods, sizeof(uint8_t *));
if (tmp_list == nullptr) {
return -1;
}
uint16_t unpacked_len = 0;
for (uint16_t i = 0; i < num_mods; ++i) {
tmp_list[i] = (uint8_t *)malloc(sizeof(uint8_t) * MOD_LIST_ENTRY_SIZE);
if (tmp_list[i] == nullptr) {
free_uint8_t_pointer_array(tmp_list, i);
return -1;
}
memcpy(tmp_list[i], &data[i * MOD_LIST_ENTRY_SIZE], MOD_LIST_ENTRY_SIZE);
unpacked_len += MOD_LIST_ENTRY_SIZE;
}
moderation->mod_list = tmp_list;
moderation->num_mods = num_mods;
return unpacked_len;
}
void mod_list_pack(const Moderation *moderation, uint8_t *data)
{
for (uint16_t i = 0; i < moderation->num_mods; ++i) {
memcpy(&data[i * MOD_LIST_ENTRY_SIZE], moderation->mod_list[i], MOD_LIST_ENTRY_SIZE);
}
}
void mod_list_get_data_hash(uint8_t *hash, const uint8_t *packed_mod_list, uint16_t length)
{
crypto_sha256(hash, packed_mod_list, length);
}
bool mod_list_make_hash(const Moderation *moderation, uint8_t *hash)
{
if (moderation->num_mods == 0) {
memset(hash, 0, MOD_MODERATION_HASH_SIZE);
return true;
}
const size_t data_buf_size = mod_list_packed_size(moderation);
assert(data_buf_size > 0);
uint8_t *data = (uint8_t *)malloc(data_buf_size);
if (data == nullptr) {
return false;
}
mod_list_pack(moderation, data);
mod_list_get_data_hash(hash, data, data_buf_size);
free(data);
return true;
}
/**
* Returns moderator list index for public_sig_key.
* Returns -1 if key is not in the list.
*/
non_null()
static int mod_list_index_of_sig_pk(const Moderation *moderation, const uint8_t *public_sig_key)
{
for (uint16_t i = 0; i < moderation->num_mods; ++i) {
if (memcmp(moderation->mod_list[i], public_sig_key, SIG_PUBLIC_KEY_SIZE) == 0) {
return i;
}
}
return -1;
}
bool mod_list_verify_sig_pk(const Moderation *moderation, const uint8_t *sig_pk)
{
if (memcmp(moderation->founder_public_sig_key, sig_pk, SIG_PUBLIC_KEY_SIZE) == 0) {
return true;
}
for (uint16_t i = 0; i < moderation->num_mods; ++i) {
if (memcmp(moderation->mod_list[i], sig_pk, SIG_PUBLIC_KEY_SIZE) == 0) {
return true;
}
}
return false;
}
bool mod_list_remove_index(Moderation *moderation, uint16_t index)
{
if (index >= moderation->num_mods) {
return false;
}
if ((moderation->num_mods - 1) == 0) {
mod_list_cleanup(moderation);
return true;
}
--moderation->num_mods;
if (index != moderation->num_mods) {
memcpy(moderation->mod_list[index], moderation->mod_list[moderation->num_mods],
MOD_LIST_ENTRY_SIZE);
}
free(moderation->mod_list[moderation->num_mods]);
moderation->mod_list[moderation->num_mods] = nullptr;
uint8_t **tmp_list = (uint8_t **)realloc(moderation->mod_list, moderation->num_mods * sizeof(uint8_t *));
if (tmp_list == nullptr) {
return false;
}
moderation->mod_list = tmp_list;
return true;
}
bool mod_list_remove_entry(Moderation *moderation, const uint8_t *public_sig_key)
{
if (moderation->num_mods == 0) {
return false;
}
const int idx = mod_list_index_of_sig_pk(moderation, public_sig_key);
if (idx == -1) {
return false;
}
assert(idx <= UINT16_MAX);
return mod_list_remove_index(moderation, (uint16_t)idx);
}
bool mod_list_add_entry(Moderation *moderation, const uint8_t *mod_data)
{
if (moderation->num_mods >= MOD_MAX_NUM_MODERATORS) {
return false;
}
uint8_t **tmp_list = (uint8_t **)realloc(moderation->mod_list, (moderation->num_mods + 1) * sizeof(uint8_t *));
if (tmp_list == nullptr) {
return false;
}
moderation->mod_list = tmp_list;
tmp_list[moderation->num_mods] = (uint8_t *)malloc(sizeof(uint8_t) * MOD_LIST_ENTRY_SIZE);
if (tmp_list[moderation->num_mods] == nullptr) {
return false;
}
memcpy(tmp_list[moderation->num_mods], mod_data, MOD_LIST_ENTRY_SIZE);
++moderation->num_mods;
return true;
}
void mod_list_cleanup(Moderation *moderation)
{
free_uint8_t_pointer_array(moderation->mod_list, moderation->num_mods);
moderation->num_mods = 0;
moderation->mod_list = nullptr;
}
uint16_t sanctions_creds_pack(const Mod_Sanction_Creds *creds, uint8_t *data)
{
uint16_t packed_len = 0;
net_pack_u32(data + packed_len, creds->version);
packed_len += sizeof(uint32_t);
memcpy(data + packed_len, creds->hash, MOD_SANCTION_HASH_SIZE);
packed_len += MOD_SANCTION_HASH_SIZE;
net_pack_u16(data + packed_len, creds->checksum);
packed_len += sizeof(uint16_t);
memcpy(data + packed_len, creds->sig_pk, SIG_PUBLIC_KEY_SIZE);
packed_len += SIG_PUBLIC_KEY_SIZE;
memcpy(data + packed_len, creds->sig, SIGNATURE_SIZE);
packed_len += SIGNATURE_SIZE;
return packed_len;
}
uint16_t sanctions_list_packed_size(uint16_t num_sanctions)
{
return MOD_SANCTION_PACKED_SIZE * num_sanctions;
}
int sanctions_list_pack(uint8_t *data, uint16_t length, const Mod_Sanction *sanctions, uint16_t num_sanctions,
const Mod_Sanction_Creds *creds)
{
assert(sanctions != nullptr || num_sanctions == 0);
assert(sanctions != nullptr || creds != nullptr);
uint16_t packed_len = 0;
for (uint16_t i = 0; i < num_sanctions; ++i) {
if (packed_len + sizeof(uint8_t) + SIG_PUBLIC_KEY_SIZE + TIME_STAMP_SIZE > length) {
return -1;
}
memcpy(data + packed_len, &sanctions[i].type, sizeof(uint8_t));
packed_len += sizeof(uint8_t);
memcpy(data + packed_len, sanctions[i].setter_public_sig_key, SIG_PUBLIC_KEY_SIZE);
packed_len += SIG_PUBLIC_KEY_SIZE;
net_pack_u64(data + packed_len, sanctions[i].time_set);
packed_len += TIME_STAMP_SIZE;
const uint8_t sanctions_type = sanctions[i].type;
if (sanctions_type == SA_OBSERVER) {
if (packed_len + ENC_PUBLIC_KEY_SIZE > length) {
return -1;
}
memcpy(data + packed_len, sanctions[i].target_public_enc_key, ENC_PUBLIC_KEY_SIZE);
packed_len += ENC_PUBLIC_KEY_SIZE;
} else {
return -1;
}
if (packed_len + SIGNATURE_SIZE > length) {
return -1;
}
/* Signature must be packed last */
memcpy(data + packed_len, sanctions[i].signature, SIGNATURE_SIZE);
packed_len += SIGNATURE_SIZE;
}
if (creds == nullptr) {
return packed_len;
}
if (length < packed_len || length - packed_len < MOD_SANCTIONS_CREDS_SIZE) {
return -1;
}
const uint16_t cred_len = sanctions_creds_pack(creds, data + packed_len);
if (cred_len != MOD_SANCTIONS_CREDS_SIZE) {
return -1;
}
return (int)(packed_len + cred_len);
}
uint16_t sanctions_creds_unpack(Mod_Sanction_Creds *creds, const uint8_t *data)
{
uint16_t len_processed = 0;
net_unpack_u32(data + len_processed, &creds->version);
len_processed += sizeof(uint32_t);
memcpy(creds->hash, data + len_processed, MOD_SANCTION_HASH_SIZE);
len_processed += MOD_SANCTION_HASH_SIZE;
net_unpack_u16(data + len_processed, &creds->checksum);
len_processed += sizeof(uint16_t);
memcpy(creds->sig_pk, data + len_processed, SIG_PUBLIC_KEY_SIZE);
len_processed += SIG_PUBLIC_KEY_SIZE;
memcpy(creds->sig, data + len_processed, SIGNATURE_SIZE);
len_processed += SIGNATURE_SIZE;
return len_processed;
}
int sanctions_list_unpack(Mod_Sanction *sanctions, Mod_Sanction_Creds *creds, uint16_t max_sanctions,
const uint8_t *data, uint16_t length, uint16_t *processed_data_len)
{
uint16_t num = 0;
uint16_t len_processed = 0;
while (num < max_sanctions && num < MOD_MAX_NUM_SANCTIONS && len_processed < length) {
if (len_processed + sizeof(uint8_t) + SIG_PUBLIC_KEY_SIZE + TIME_STAMP_SIZE > length) {
return -1;
}
memcpy(&sanctions[num].type, data + len_processed, sizeof(uint8_t));
len_processed += sizeof(uint8_t);
memcpy(sanctions[num].setter_public_sig_key, data + len_processed, SIG_PUBLIC_KEY_SIZE);
len_processed += SIG_PUBLIC_KEY_SIZE;
net_unpack_u64(data + len_processed, &sanctions[num].time_set);
len_processed += TIME_STAMP_SIZE;
if (sanctions[num].type == SA_OBSERVER) {
if (len_processed + ENC_PUBLIC_KEY_SIZE > length) {
return -1;
}
memcpy(sanctions[num].target_public_enc_key, data + len_processed, ENC_PUBLIC_KEY_SIZE);
len_processed += ENC_PUBLIC_KEY_SIZE;
} else {
return -1;
}
if (len_processed + SIGNATURE_SIZE > length) {
return -1;
}
memcpy(sanctions[num].signature, data + len_processed, SIGNATURE_SIZE);
len_processed += SIGNATURE_SIZE;
++num;
}
if (length <= len_processed || length - len_processed < MOD_SANCTIONS_CREDS_SIZE) {
if (length != len_processed) {
return -1;
}
if (processed_data_len != nullptr) {
*processed_data_len = len_processed;
}
return num;
}
const uint16_t creds_len = sanctions_creds_unpack(creds, data + len_processed);
if (creds_len != MOD_SANCTIONS_CREDS_SIZE) {
return -1;
}
if (processed_data_len != nullptr) {
*processed_data_len = len_processed + creds_len;
}
return num;
}
/** @brief Creates a new sanction list hash and puts it in hash.
*
* The hash is derived from the signature of all entries plus the version number.
* hash must have room for at least MOD_SANCTION_HASH_SIZE bytes.
*
* If num_sanctions is 0 the hash is zeroed.
*
* Return true on success.
*/
non_null(4) nullable(1)
static bool sanctions_list_make_hash(const Mod_Sanction *sanctions, uint32_t new_version, uint16_t num_sanctions,
uint8_t *hash)
{
if (num_sanctions == 0 || sanctions == nullptr) {
memset(hash, 0, MOD_SANCTION_HASH_SIZE);
return true;
}
const size_t sig_data_size = num_sanctions * SIGNATURE_SIZE;
const size_t data_buf_size = sig_data_size + sizeof(uint32_t);
// check for integer overflower
if (data_buf_size < num_sanctions) {
return false;
}
uint8_t *data = (uint8_t *)malloc(data_buf_size);
if (data == nullptr) {
return false;
}
for (uint16_t i = 0; i < num_sanctions; ++i) {
memcpy(&data[i * SIGNATURE_SIZE], sanctions[i].signature, SIGNATURE_SIZE);
}
memcpy(&data[sig_data_size], &new_version, sizeof(uint32_t));
crypto_sha256(hash, data, data_buf_size);
free(data);
return true;
}
/** @brief Verifies that sanction contains valid info and was assigned by a current mod or group founder.
*
* Returns true on success.
*/
non_null()
static bool sanctions_list_validate_entry(const Moderation *moderation, const Mod_Sanction *sanction)
{
if (!mod_list_verify_sig_pk(moderation, sanction->setter_public_sig_key)) {
return false;
}
if (sanction->type >= SA_INVALID) {
return false;
}
if (sanction->time_set == 0) {
return false;
}
uint8_t packed_data[MOD_SANCTION_PACKED_SIZE];
const int packed_len = sanctions_list_pack(packed_data, sizeof(packed_data), sanction, 1, nullptr);
if (packed_len <= (int) SIGNATURE_SIZE) {
return false;
}
return crypto_signature_verify(sanction->signature, packed_data, packed_len - SIGNATURE_SIZE,
sanction->setter_public_sig_key);
}
non_null()
static uint16_t sanctions_creds_get_checksum(const Mod_Sanction_Creds *creds)
{
return data_checksum(creds->hash, sizeof(creds->hash));
}
non_null()
static void sanctions_creds_set_checksum(Mod_Sanction_Creds *creds)
{
creds->checksum = sanctions_creds_get_checksum(creds);
}
bool sanctions_list_make_creds(Moderation *moderation)
{
const Mod_Sanction_Creds old_creds = moderation->sanctions_creds;
++moderation->sanctions_creds.version;
memcpy(moderation->sanctions_creds.sig_pk, moderation->self_public_sig_key, SIG_PUBLIC_KEY_SIZE);
uint8_t hash[MOD_SANCTION_HASH_SIZE];
if (!sanctions_list_make_hash(moderation->sanctions, moderation->sanctions_creds.version,
moderation->num_sanctions, hash)) {
moderation->sanctions_creds = old_creds;
return false;
}
memcpy(moderation->sanctions_creds.hash, hash, MOD_SANCTION_HASH_SIZE);
sanctions_creds_set_checksum(&moderation->sanctions_creds);
if (!crypto_signature_create(moderation->sanctions_creds.sig, moderation->sanctions_creds.hash,
MOD_SANCTION_HASH_SIZE, moderation->self_secret_sig_key)) {
moderation->sanctions_creds = old_creds;
return false;
}
return true;
}
/** @brief Validates sanction list credentials.
*
* Verifies that:
* - the public signature key belongs to a mod or the founder
* - the signature for the hash was made by the owner of the public signature key.
* - the received hash matches our own hash of the new sanctions list
* - the received checksum matches the received hash
* - the new version is >= our current version
*
* Returns true on success.
*/
non_null(1, 3) nullable(2)
static bool sanctions_creds_validate(const Moderation *moderation, const Mod_Sanction *sanctions,
const Mod_Sanction_Creds *creds, uint16_t num_sanctions)
{
if (!mod_list_verify_sig_pk(moderation, creds->sig_pk)) {
LOGGER_WARNING(moderation->log, "Invalid credentials signature pk");
return false;
}
uint8_t hash[MOD_SANCTION_HASH_SIZE];
if (!sanctions_list_make_hash(sanctions, creds->version, num_sanctions, hash)) {
return false;
}
if (memcmp(hash, creds->hash, MOD_SANCTION_HASH_SIZE) != 0) {
LOGGER_WARNING(moderation->log, "Invalid credentials hash");
return false;
}
if (creds->checksum != sanctions_creds_get_checksum(creds)) {
LOGGER_WARNING(moderation->log, "Invalid credentials checksum");
return false;
}
if (moderation->shared_state_version > 0) {
if ((creds->version < moderation->sanctions_creds.version)
&& !(creds->version == 0 && moderation->sanctions_creds.version == UINT32_MAX)) {
LOGGER_WARNING(moderation->log, "Invalid version");
return false;
}
}
if (!crypto_signature_verify(creds->sig, hash, MOD_SANCTION_HASH_SIZE, creds->sig_pk)) {
LOGGER_WARNING(moderation->log, "Invalid signature");
return false;
}
return true;
}
bool sanctions_list_check_integrity(const Moderation *moderation, const Mod_Sanction_Creds *creds,
const Mod_Sanction *sanctions, uint16_t num_sanctions)
{
for (uint16_t i = 0; i < num_sanctions; ++i) {
if (!sanctions_list_validate_entry(moderation, &sanctions[i])) {
LOGGER_WARNING(moderation->log, "Invalid entry");
return false;
}
}
return sanctions_creds_validate(moderation, sanctions, creds, num_sanctions);
}
/** @brief Validates a sanctions list if credentials are supplied. If successful,
* or if no credentials are supplied, assigns new sanctions list and credentials
* to moderation object.
*
* @param moderation The moderation object being operated on.
* @param new_sanctions The sanctions list to validate and assign to moderation object.
* @param new_creds The new sanctions credentials to be assigned to moderation object.
* @param num_sanctions The number of sanctions in the sanctions list.
*
* @retval false if sanctions credentials validation fails.
*/
non_null(1, 2) nullable(3)
static bool sanctions_apply_new(Moderation *moderation, Mod_Sanction *new_sanctions,
const Mod_Sanction_Creds *new_creds,
uint16_t num_sanctions)
{
if (new_creds != nullptr) {
if (!sanctions_creds_validate(moderation, new_sanctions, new_creds, num_sanctions)) {
LOGGER_WARNING(moderation->log, "Failed to validate credentials");
return false;
}
moderation->sanctions_creds = *new_creds;
}
sanctions_list_cleanup(moderation);
moderation->sanctions = new_sanctions;
moderation->num_sanctions = num_sanctions;
return true;
}
/** @brief Returns a copy of the sanctions list. The caller is responsible for freeing the
* memory returned by this function.
*/
non_null()
static Mod_Sanction *sanctions_list_copy(const Mod_Sanction *sanctions, uint16_t num_sanctions)
{
Mod_Sanction *copy = (Mod_Sanction *)calloc(num_sanctions, sizeof(Mod_Sanction));
if (copy == nullptr) {
return nullptr;
}
memcpy(copy, sanctions, num_sanctions * sizeof(Mod_Sanction));
return copy;
}
/** @brief Removes index-th sanction list entry.
*
* New credentials will be validated if creds is non-null.
*
* Returns true on success.
*/
non_null(1) nullable(3)
static bool sanctions_list_remove_index(Moderation *moderation, uint16_t index, const Mod_Sanction_Creds *creds)
{
if (index >= moderation->num_sanctions) {
return false;
}
const uint16_t new_num = moderation->num_sanctions - 1;
if (new_num == 0) {
if (creds != nullptr) {
if (!sanctions_creds_validate(moderation, nullptr, creds, 0)) {
return false;
}
moderation->sanctions_creds = *creds;
}
sanctions_list_cleanup(moderation);
return true;
}
/* Operate on a copy of the list in case something goes wrong. */
Mod_Sanction *sanctions_copy = sanctions_list_copy(moderation->sanctions, moderation->num_sanctions);
if (sanctions_copy == nullptr) {
return false;
}
if (index != new_num) {
sanctions_copy[index] = sanctions_copy[new_num];
}
Mod_Sanction *new_list = (Mod_Sanction *)realloc(sanctions_copy, new_num * sizeof(Mod_Sanction));
if (new_list == nullptr) {
free(sanctions_copy);
return false;
}
if (!sanctions_apply_new(moderation, new_list, creds, new_num)) {
free(new_list);
return false;
}
return true;
}
bool sanctions_list_remove_observer(Moderation *moderation, const uint8_t *public_key,
const Mod_Sanction_Creds *creds)
{
for (uint16_t i = 0; i < moderation->num_sanctions; ++i) {
const Mod_Sanction *curr_sanction = &moderation->sanctions[i];
if (curr_sanction->type != SA_OBSERVER) {
continue;
}
if (memcmp(public_key, curr_sanction->target_public_enc_key, ENC_PUBLIC_KEY_SIZE) == 0) {
if (!sanctions_list_remove_index(moderation, i, creds)) {
return false;
}
if (creds == nullptr) {
return sanctions_list_make_creds(moderation);
}
return true;
}
}
return false;
}
bool sanctions_list_is_observer(const Moderation *moderation, const uint8_t *public_key)
{
for (uint16_t i = 0; i < moderation->num_sanctions; ++i) {
const Mod_Sanction *curr_sanction = &moderation->sanctions[i];
if (curr_sanction->type != SA_OBSERVER) {
continue;
}
if (memcmp(curr_sanction->target_public_enc_key, public_key, ENC_PUBLIC_KEY_SIZE) == 0) {
return true;
}
}
return false;
}
bool sanctions_list_entry_exists(const Moderation *moderation, const Mod_Sanction *sanction)
{
if (sanction->type == SA_OBSERVER) {
return sanctions_list_is_observer(moderation, sanction->target_public_enc_key);
}
return false;
}
bool sanctions_list_add_entry(Moderation *moderation, const Mod_Sanction *sanction, const Mod_Sanction_Creds *creds)
{
if (moderation->num_sanctions >= MOD_MAX_NUM_SANCTIONS) {
LOGGER_WARNING(moderation->log, "num_sanctions %d exceeds maximum", moderation->num_sanctions);
return false;
}
if (!sanctions_list_validate_entry(moderation, sanction)) {
LOGGER_ERROR(moderation->log, "Failed to validate sanction");
return false;
}
if (sanctions_list_entry_exists(moderation, sanction)) {
LOGGER_WARNING(moderation->log, "Attempted to add duplicate sanction");
return false;
}
/* Operate on a copy of the list in case something goes wrong. */
Mod_Sanction *sanctions_copy = nullptr;
if (moderation->num_sanctions > 0) {
sanctions_copy = sanctions_list_copy(moderation->sanctions, moderation->num_sanctions);
if (sanctions_copy == nullptr) {
return false;
}
}
const uint16_t index = moderation->num_sanctions;
Mod_Sanction *new_list = (Mod_Sanction *)realloc(sanctions_copy, (index + 1) * sizeof(Mod_Sanction));
if (new_list == nullptr) {
free(sanctions_copy);
return false;
}
new_list[index] = *sanction;
if (!sanctions_apply_new(moderation, new_list, creds, index + 1)) {
free(new_list);
return false;
}
return true;
}
/** @brief Signs packed sanction data.
*
* This function must be called by the owner of the entry's public_sig_key.
*
* Returns true on success.
*/
non_null()
static bool sanctions_list_sign_entry(const Moderation *moderation, Mod_Sanction *sanction)
{
uint8_t packed_data[MOD_SANCTION_PACKED_SIZE];
const int packed_len = sanctions_list_pack(packed_data, sizeof(packed_data), sanction, 1, nullptr);
if (packed_len <= (int) SIGNATURE_SIZE) {
LOGGER_ERROR(moderation->log, "Failed to pack sanctions list: %d", packed_len);
return false;
}
return crypto_signature_create(sanction->signature, packed_data, packed_len - SIGNATURE_SIZE,
moderation->self_secret_sig_key);
}
bool sanctions_list_make_entry(Moderation *moderation, const uint8_t *public_key, Mod_Sanction *sanction,
uint8_t type)
{
*sanction = (Mod_Sanction) {
0
};
if (type == SA_OBSERVER) {
memcpy(sanction->target_public_enc_key, public_key, ENC_PUBLIC_KEY_SIZE);
} else {
LOGGER_ERROR(moderation->log, "Tried to create sanction with invalid type: %u", type);
return false;
}
memcpy(sanction->setter_public_sig_key, moderation->self_public_sig_key, SIG_PUBLIC_KEY_SIZE);
sanction->time_set = (uint64_t)time(nullptr);
sanction->type = type;
if (!sanctions_list_sign_entry(moderation, sanction)) {
LOGGER_ERROR(moderation->log, "Failed to sign sanction");
return false;
}
if (!sanctions_list_add_entry(moderation, sanction, nullptr)) {
return false;
}
if (!sanctions_list_make_creds(moderation)) {
LOGGER_ERROR(moderation->log, "Failed to make credentials for new sanction");
return false;
}
return true;
}
uint16_t sanctions_list_replace_sig(Moderation *moderation, const uint8_t *public_sig_key)
{
uint16_t count = 0;
for (uint16_t i = 0; i < moderation->num_sanctions; ++i) {
if (memcmp(moderation->sanctions[i].setter_public_sig_key, public_sig_key, SIG_PUBLIC_KEY_SIZE) != 0) {
continue;
}
memcpy(moderation->sanctions[i].setter_public_sig_key, moderation->self_public_sig_key, SIG_PUBLIC_KEY_SIZE);
if (!sanctions_list_sign_entry(moderation, &moderation->sanctions[i])) {
LOGGER_ERROR(moderation->log, "Failed to sign sanction");
continue;
}
++count;
}
if (count > 0) {
if (!sanctions_list_make_creds(moderation)) {
return 0;
}
}
return count;
}
void sanctions_list_cleanup(Moderation *moderation)
{
if (moderation->sanctions != nullptr) {
free(moderation->sanctions);
}
moderation->sanctions = nullptr;
moderation->num_sanctions = 0;
}

View File

@ -0,0 +1,296 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2020 The TokTok team.
* Copyright © 2015 Tox project.
*/
/**
* An implementation of massive text only group chats.
*/
#ifndef C_TOXCORE_TOXCORE_GROUP_MODERATION_H
#define C_TOXCORE_TOXCORE_GROUP_MODERATION_H
#include <stdbool.h>
#include <stdint.h>
#include "DHT.h"
#include "logger.h"
#ifdef __cplusplus
extern "C" {
#endif
#define MOD_MODERATION_HASH_SIZE CRYPTO_SHA256_SIZE
#define MOD_LIST_ENTRY_SIZE SIG_PUBLIC_KEY_SIZE
#define MOD_SANCTION_HASH_SIZE CRYPTO_SHA256_SIZE
#define TIME_STAMP_SIZE sizeof(uint64_t)
/* The packed size of a Mod_Sanction_Creds */
#define MOD_SANCTIONS_CREDS_SIZE (sizeof(uint32_t) + MOD_SANCTION_HASH_SIZE + sizeof(uint16_t) +\
SIG_PUBLIC_KEY_SIZE + SIGNATURE_SIZE)
/* The packed size of a Mod_Sanction */
#define MOD_SANCTION_PACKED_SIZE (SIG_PUBLIC_KEY_SIZE + TIME_STAMP_SIZE + 1 + ENC_PUBLIC_KEY_SIZE + SIGNATURE_SIZE)
/* The max size of a groupchat packet with 100 bytes reserved for header data */
#define MAX_PACKET_SIZE_NO_HEADERS 49900
/* The maximum possible number of moderators that can be sent in a group packet sequence. */
#define MOD_MAX_NUM_MODERATORS_LIMIT (((MAX_PACKET_SIZE_NO_HEADERS) / (MOD_LIST_ENTRY_SIZE)))
/* The maximum number of moderators that we allow in a group: 100 */
#define MOD_MAX_NUM_MODERATORS ((MOD_MAX_NUM_MODERATORS_LIMIT / 16) + 3)
/* The maximum number of sanctions that be sent in a group packet sequence. */
#define MOD_MAX_NUM_SANCTIONS_LIMIT (((MAX_PACKET_SIZE_NO_HEADERS - (MOD_SANCTIONS_CREDS_SIZE)) / (MOD_SANCTION_PACKED_SIZE)))
/* The maximum number of sanctions that we allow in a group: 30 */
#define MOD_MAX_NUM_SANCTIONS (MOD_MAX_NUM_SANCTIONS_LIMIT / 12)
typedef enum Mod_Sanction_Type {
SA_OBSERVER = 0x00,
SA_INVALID = 0x01,
} Mod_Sanction_Type;
typedef struct Mod_Sanction_Creds {
uint32_t version;
uint8_t hash[MOD_SANCTION_HASH_SIZE]; // hash of all sanctions list signatures + version
uint16_t checksum; // a sum of the hash
uint8_t sig_pk[SIG_PUBLIC_KEY_SIZE]; // Last mod to have modified the sanctions list
uint8_t sig[SIGNATURE_SIZE]; // signature of hash, signed by sig_pk
} Mod_Sanction_Creds;
/** Holds data pertaining to a peer who has been sanctioned. */
typedef struct Mod_Sanction {
uint8_t setter_public_sig_key[SIG_PUBLIC_KEY_SIZE];
// TODO(Jfreegman): This timestamp can potentially be used to track a user across
// different group chats if they're a moderator and set many sanctions across the
// different groups. This should be addressed in the future.
uint64_t time_set;
uint8_t type;
uint8_t target_public_enc_key[ENC_PUBLIC_KEY_SIZE];
/* Signature of all above packed data signed by the owner of public_sig_key */
uint8_t signature[SIGNATURE_SIZE];
} Mod_Sanction;
typedef struct Moderation {
const Logger *log;
Mod_Sanction *sanctions;
uint16_t num_sanctions;
Mod_Sanction_Creds sanctions_creds;
uint8_t **mod_list; // array of public signature keys of all the mods
uint16_t num_mods;
// copies from parent/sibling chat/shared state objects
uint8_t founder_public_sig_key[SIG_PUBLIC_KEY_SIZE];
uint8_t self_public_sig_key[SIG_PUBLIC_KEY_SIZE];
uint8_t self_secret_sig_key[SIG_SECRET_KEY_SIZE];
uint32_t shared_state_version;
} Moderation;
/** @brief Returns the size in bytes of the packed moderation list. */
non_null()
uint16_t mod_list_packed_size(const Moderation *moderation);
/** @brief Unpacks data into the moderator list.
*
* @param data should contain num_mods entries of size MOD_LIST_ENTRY_SIZE.
*
* Returns length of unpacked data on success.
* Returns -1 on failure.
*/
non_null()
int mod_list_unpack(Moderation *moderation, const uint8_t *data, uint16_t length, uint16_t num_mods);
/** @brief Packs moderator list into data.
* @param data must have room for the number of bytes returned by `mod_list_packed_size`.
*/
non_null()
void mod_list_pack(const Moderation *moderation, uint8_t *data);
/** @brief Creates a new moderator list hash and puts it in `hash`.
*
* @param hash must have room for at least MOD_MODERATION_HASH_SIZE bytes.
*
* If num_mods is 0 the hash is zeroed.
*
* Returns true on sucess.
*/
non_null()
bool mod_list_make_hash(const Moderation *moderation, uint8_t *hash);
/** @brief Puts a sha256 hash of `packed_mod_list` of `length` bytes in `hash`.
*
* @param hash must have room for at least MOD_MODERATION_HASH_SIZE bytes.
*/
non_null()
void mod_list_get_data_hash(uint8_t *hash, const uint8_t *packed_mod_list, uint16_t length);
/** @brief Removes moderator at index-th position in the moderator list.
*
* Returns true on success.
*/
non_null()
bool mod_list_remove_index(Moderation *moderation, uint16_t index);
/** @brief Removes public_sig_key from the moderator list.
*
* Returns true on success.
*/
non_null()
bool mod_list_remove_entry(Moderation *moderation, const uint8_t *public_sig_key);
/** @brief Adds a mod to the moderator list.
*
* @param mod_data must be MOD_LIST_ENTRY_SIZE bytes.
*
* Returns true on success.
*/
non_null()
bool mod_list_add_entry(Moderation *moderation, const uint8_t *mod_data);
/** @return true if the public signature key belongs to a moderator or the founder */
non_null()
bool mod_list_verify_sig_pk(const Moderation *moderation, const uint8_t *sig_pk);
/** @brief Frees all memory associated with the moderator list and sets num_mods to 0. */
nullable(1)
void mod_list_cleanup(Moderation *moderation);
/** @brief Returns the size in bytes of num_sanctions packed sanctions. */
uint16_t sanctions_list_packed_size(uint16_t num_sanctions);
/** @brief Packs sanctions into data. Additionally packs the sanctions credentials into creds.
*
* @param data The byte array being packed. Must have room for the number of bytes returned
* by `sanctions_list_packed_size`.
* @param length The size of the byte array.
* @param sanctions The sanctions list.
* @param num_sanctions The number of sanctions in the sanctions list. This value must be the same
* value used when calling `sanctions_list_packed_size`.
* @param creds The credentials object to fill.
*
* @retval The length of packed data on success.
* @retval -1 on failure.
*/
non_null(1) nullable(3, 5)
int sanctions_list_pack(uint8_t *data, uint16_t length, const Mod_Sanction *sanctions, uint16_t num_sanctions,
const Mod_Sanction_Creds *creds);
/** @brief Unpacks sanctions and new sanctions credentials.
*
* @param sanctions The sanctions array the sanctions data is unpacked into.
* @param creds The creds object the creds data is unpacked into.
* @param max_sanctions The maximum number of sanctions that the sanctions array can hold.
* @param data The packed data array.
* @param length The size of the packed data.
* @param processed_data_len If non-null, will contain the number of processed bytes on success.
*
* @retval The number of unpacked entries on success.
* @retval -1 on failure.
*/
non_null(1, 2, 4) nullable(6)
int sanctions_list_unpack(Mod_Sanction *sanctions, Mod_Sanction_Creds *creds, uint16_t max_sanctions,
const uint8_t *data, uint16_t length, uint16_t *processed_data_len);
/** @brief Packs sanction list credentials into data.
*
* @param data must have room for MOD_SANCTIONS_CREDS_SIZE bytes.
*
* Returns length of packed data.
*/
non_null()
uint16_t sanctions_creds_pack(const Mod_Sanction_Creds *creds, uint8_t *data);
/** @brief Unpacks sanctions credentials into creds from data.
*
* @param data must have room for MOD_SANCTIONS_CREDS_SIZE bytes.
*
* Returns the length of the data processed.
*/
non_null()
uint16_t sanctions_creds_unpack(Mod_Sanction_Creds *creds, const uint8_t *data);
/** @brief Updates sanction list credentials.
*
* Increment version, replace sig_pk with your own, update hash to reflect new
* sanction list, and sign new hash signature.
*
* Returns true on success.
*/
non_null()
bool sanctions_list_make_creds(Moderation *moderation);
/** @brief Validates all sanctions list entries as well as the list itself.
*
* Returns true if all entries are valid.
* Returns false if one or more entries are invalid.
*/
non_null()
bool sanctions_list_check_integrity(const Moderation *moderation, const Mod_Sanction_Creds *creds,
const Mod_Sanction *sanctions, uint16_t num_sanctions);
/** @brief Adds an entry to the sanctions list.
*
* The entry is first validated and the resulting new sanction list is
* compared against the new credentials.
*
* Entries must be unique.
*
* Returns true on success.
*/
non_null(1, 2) nullable(3)
bool sanctions_list_add_entry(Moderation *moderation, const Mod_Sanction *sanction, const Mod_Sanction_Creds *creds);
/** @brief Creates a new sanction entry for `public_key` where type is one of Mod_Sanction_Type.
*
* New entry is signed and placed in the sanctions list.
*
* Returns true on success.
*/
non_null()
bool sanctions_list_make_entry(Moderation *moderation, const uint8_t *public_key, Mod_Sanction *sanction,
uint8_t type);
/** @return true if public key is in the observer list. */
non_null()
bool sanctions_list_is_observer(const Moderation *moderation, const uint8_t *public_key);
/** @return true if sanction already exists in the sanctions list. */
non_null()
bool sanctions_list_entry_exists(const Moderation *moderation, const Mod_Sanction *sanction);
/** @brief 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)
*
* Returns false on failure or if entry was not found.
*/
non_null(1, 2) nullable(3)
bool sanctions_list_remove_observer(Moderation *moderation, const uint8_t *public_key,
const Mod_Sanction_Creds *creds);
/** @brief Replaces all sanctions list signatures made by public_sig_key with the caller's.
*
* This is called whenever the founder demotes a moderator.
*
* Returns the number of entries re-signed.
*/
non_null()
uint16_t sanctions_list_replace_sig(Moderation *moderation, const uint8_t *public_sig_key);
non_null()
void sanctions_list_cleanup(Moderation *moderation);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // C_TOXCORE_TOXCORE_GROUP_MODERATION_H

View File

@ -0,0 +1,38 @@
#include "group_moderation.h"
#include "../testing/fuzzing/fuzz_support.h"
namespace {
void TestModListUnpack(Fuzz_Data &input)
{
CONSUME1_OR_RETURN(const uint16_t num_mods, input);
Moderation mods{};
mod_list_unpack(&mods, input.data, input.size, num_mods);
mod_list_cleanup(&mods);
}
void TestSanctionsListUnpack(Fuzz_Data &input)
{
Mod_Sanction sanctions[10];
Mod_Sanction_Creds creds;
uint16_t processed_data_len;
sanctions_list_unpack(sanctions, &creds, 10, input.data, input.size, &processed_data_len);
}
void TestSanctionCredsUnpack(Fuzz_Data &input)
{
CONSUME_OR_RETURN(const uint8_t *data, input, MOD_SANCTIONS_CREDS_SIZE);
Mod_Sanction_Creds creds;
sanctions_creds_unpack(&creds, data);
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
fuzz_select_target(
data, size, TestModListUnpack, TestSanctionsListUnpack, TestSanctionCredsUnpack);
return 0;
}

View File

@ -0,0 +1,251 @@
#include "group_moderation.h"
#include <gtest/gtest.h>
#include <algorithm>
#include <array>
#include <vector>
#include "crypto_core.h"
#include "logger.h"
#include "util.h"
namespace {
using ExtPublicKey = std::array<uint8_t, EXT_PUBLIC_KEY_SIZE>;
using ExtSecretKey = std::array<uint8_t, EXT_SECRET_KEY_SIZE>;
using ModerationHash = std::array<uint8_t, MOD_MODERATION_HASH_SIZE>;
TEST(ModList, PackedSizeOfEmptyModListIsZero)
{
Moderation mods{};
EXPECT_EQ(mod_list_packed_size(&mods), 0);
uint8_t byte = 1;
mod_list_pack(&mods, &byte);
EXPECT_EQ(byte, 1);
}
TEST(ModList, UnpackingZeroSizeArrayIsNoop)
{
Moderation mods{};
const uint8_t byte = 1;
EXPECT_EQ(mod_list_unpack(&mods, &byte, 0, 0), 0);
}
TEST(ModList, AddRemoveMultipleMods)
{
Moderation mods{};
uint8_t sig_pk1[32] = {1};
uint8_t sig_pk2[32] = {2};
EXPECT_TRUE(mod_list_add_entry(&mods, sig_pk1));
EXPECT_TRUE(mod_list_add_entry(&mods, sig_pk2));
EXPECT_TRUE(mod_list_remove_entry(&mods, sig_pk1));
EXPECT_TRUE(mod_list_remove_entry(&mods, sig_pk2));
}
TEST(ModList, PackingAndUnpackingList)
{
using ModListEntry = std::array<uint8_t, MOD_LIST_ENTRY_SIZE>;
Moderation mods{};
EXPECT_TRUE(mod_list_add_entry(&mods, ModListEntry{}.data()));
std::vector<uint8_t> packed(mod_list_packed_size(&mods));
mod_list_pack(&mods, packed.data());
EXPECT_TRUE(mod_list_remove_entry(&mods, ModListEntry{}.data()));
Moderation mods2{};
EXPECT_EQ(mod_list_unpack(&mods2, packed.data(), packed.size(), 1), packed.size());
EXPECT_TRUE(mod_list_remove_entry(&mods2, ModListEntry{}.data()));
}
TEST(ModList, UnpackingTooManyModsFails)
{
using ModListEntry = std::array<uint8_t, MOD_LIST_ENTRY_SIZE>;
Moderation mods{};
EXPECT_TRUE(mod_list_add_entry(&mods, ModListEntry{}.data()));
std::vector<uint8_t> packed(mod_list_packed_size(&mods));
mod_list_pack(&mods, packed.data());
Moderation mods2{};
EXPECT_EQ(mod_list_unpack(&mods2, packed.data(), packed.size(), 2), -1);
EXPECT_TRUE(mod_list_remove_entry(&mods, ModListEntry{}.data()));
}
TEST(ModList, UnpackingFromEmptyBufferFails)
{
std::vector<uint8_t> packed(1);
Moderation mods{};
EXPECT_EQ(mod_list_unpack(&mods, packed.end().base(), 0, 1), -1);
}
TEST(ModList, HashOfEmptyModListZeroesOutBuffer)
{
const Random *rng = system_random();
ASSERT_NE(rng, nullptr);
Moderation mods{};
// Fill with random data, check that it's zeroed.
ModerationHash hash;
random_bytes(rng, hash.data(), hash.size());
EXPECT_TRUE(mod_list_make_hash(&mods, hash.data()));
EXPECT_EQ(hash, ModerationHash{});
}
TEST(ModList, RemoveIndexFromEmptyModListFails)
{
Moderation mods{};
EXPECT_FALSE(mod_list_remove_index(&mods, 0));
EXPECT_FALSE(mod_list_remove_index(&mods, UINT16_MAX));
}
TEST(ModList, RemoveEntryFromEmptyModListFails)
{
Moderation mods{};
uint8_t sig_pk[32] = {0};
EXPECT_FALSE(mod_list_remove_entry(&mods, sig_pk));
}
TEST(ModList, ModListRemoveIndex)
{
Moderation mods{};
uint8_t sig_pk[32] = {1};
EXPECT_TRUE(mod_list_add_entry(&mods, sig_pk));
EXPECT_TRUE(mod_list_remove_index(&mods, 0));
}
TEST(ModList, CleanupOnEmptyModsIsNoop)
{
Moderation mods{};
mod_list_cleanup(&mods);
}
TEST(ModList, EmptyModListCannotVerifyAnySigPk)
{
Moderation mods{};
uint8_t sig_pk[32] = {1};
EXPECT_FALSE(mod_list_verify_sig_pk(&mods, sig_pk));
}
TEST(ModList, ModListAddVerifyRemoveSigPK)
{
Moderation mods{};
uint8_t sig_pk[32] = {1};
EXPECT_TRUE(mod_list_add_entry(&mods, sig_pk));
EXPECT_TRUE(mod_list_verify_sig_pk(&mods, sig_pk));
EXPECT_TRUE(mod_list_remove_entry(&mods, sig_pk));
EXPECT_FALSE(mod_list_verify_sig_pk(&mods, sig_pk));
}
TEST(ModList, ModListHashCheck)
{
Moderation mods1{};
uint8_t sig_pk1[32] = {1};
std::array<uint8_t, MOD_MODERATION_HASH_SIZE> hash1;
EXPECT_TRUE(mod_list_add_entry(&mods1, sig_pk1));
EXPECT_TRUE(mod_list_make_hash(&mods1, hash1.data()));
EXPECT_TRUE(mod_list_remove_entry(&mods1, sig_pk1));
}
TEST(SanctionsList, PackingIntoUndersizedBufferFails)
{
Mod_Sanction sanctions[1] = {};
std::array<uint8_t, 1> packed;
EXPECT_EQ(sanctions_list_pack(packed.data(), packed.size(), sanctions, 1, nullptr), -1);
uint16_t length = sanctions_list_packed_size(1) - 1;
std::vector<uint8_t> packed2(length);
EXPECT_EQ(sanctions_list_pack(packed2.data(), packed2.size(), sanctions, 1, nullptr), -1);
}
TEST(SanctionsList, PackUnpackSanctionsCreds)
{
Moderation mod{};
std::array<uint8_t, MOD_SANCTIONS_CREDS_SIZE> packed;
EXPECT_EQ(sanctions_creds_pack(&mod.sanctions_creds, packed.data()), MOD_SANCTIONS_CREDS_SIZE);
EXPECT_EQ(
sanctions_creds_unpack(&mod.sanctions_creds, packed.data()), MOD_SANCTIONS_CREDS_SIZE);
}
struct SanctionsListMod : ::testing::Test {
protected:
ExtPublicKey pk;
ExtSecretKey sk;
Logger *log = logger_new();
Moderation mod{};
Mod_Sanction sanctions[2] = {};
const uint8_t sanctioned_pk1[32] = {1};
const uint8_t sanctioned_pk2[32] = {2};
void SetUp() override
{
ASSERT_TRUE(create_extended_keypair(pk.data(), sk.data()));
mod.log = log;
memcpy(mod.self_public_sig_key, get_sig_pk(pk.data()), SIG_PUBLIC_KEY_SIZE);
memcpy(mod.self_secret_sig_key, get_sig_sk(sk.data()), SIG_SECRET_KEY_SIZE);
ASSERT_TRUE(mod_list_add_entry(&mod, get_sig_pk(pk.data())));
EXPECT_FALSE(sanctions_list_check_integrity(&mod, &mod.sanctions_creds, &sanctions[0], 0));
EXPECT_FALSE(sanctions_list_check_integrity(&mod, &mod.sanctions_creds, &sanctions[0], 1));
EXPECT_FALSE(
sanctions_list_check_integrity(&mod, &mod.sanctions_creds, &sanctions[0], UINT16_MAX));
EXPECT_TRUE(sanctions_list_make_entry(&mod, sanctioned_pk1, &sanctions[0], SA_OBSERVER));
EXPECT_TRUE(sanctions_list_check_integrity(
&mod, &mod.sanctions_creds, sanctions, mod.num_sanctions));
EXPECT_TRUE(sanctions_list_make_entry(&mod, sanctioned_pk2, &sanctions[1], SA_OBSERVER));
EXPECT_TRUE(sanctions_list_check_integrity(
&mod, &mod.sanctions_creds, sanctions, mod.num_sanctions));
}
~SanctionsListMod() override
{
EXPECT_TRUE(sanctions_list_remove_observer(&mod, sanctioned_pk1, nullptr));
EXPECT_TRUE(sanctions_list_remove_observer(&mod, sanctioned_pk2, nullptr));
EXPECT_FALSE(sanctions_list_entry_exists(&mod, &sanctions[0]));
EXPECT_FALSE(sanctions_list_entry_exists(&mod, &sanctions[1]));
EXPECT_TRUE(mod_list_remove_entry(&mod, get_sig_pk(pk.data())));
logger_kill(log);
}
};
// TODO(JFreegman): Split this up into smaller subtests
TEST_F(SanctionsListMod, PackUnpackSanction)
{
std::vector<uint8_t> packed(sanctions_list_packed_size(2));
EXPECT_EQ(
sanctions_list_pack(packed.data(), packed.size(), sanctions, 2, nullptr), packed.size());
Mod_Sanction unpacked_sanctions[2] = {};
uint16_t processed_data_len = 0;
EXPECT_EQ(sanctions_list_unpack(unpacked_sanctions, &mod.sanctions_creds, 2, packed.data(),
packed.size(), &processed_data_len),
2);
EXPECT_EQ(processed_data_len, packed.size());
EXPECT_TRUE(sanctions_list_check_integrity(
&mod, &mod.sanctions_creds, unpacked_sanctions, mod.num_sanctions));
EXPECT_TRUE(sanctions_list_entry_exists(&mod, &unpacked_sanctions[0]));
EXPECT_TRUE(sanctions_list_entry_exists(&mod, &unpacked_sanctions[1]));
}
TEST_F(SanctionsListMod, ReplaceSanctionSignatures)
{
EXPECT_EQ(sanctions_list_replace_sig(&mod, mod.self_public_sig_key), mod.num_sanctions);
EXPECT_TRUE(
sanctions_list_check_integrity(&mod, &mod.sanctions_creds, sanctions, mod.num_sanctions));
}
} // namespace

View File

@ -0,0 +1,115 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2020 The TokTok team.
* Copyright © 2015 Tox project.
*/
#include "group_onion_announce.h"
#include <assert.h>
#include <string.h>
#include "ccompat.h"
static_assert(GCA_ANNOUNCE_MAX_SIZE <= ONION_MAX_EXTRA_DATA_SIZE,
"GC_Announce does not fit into the onion packet extra data");
static pack_extra_data_cb pack_group_announces;
non_null()
static int pack_group_announces(void *object, const Logger *logger, const Mono_Time *mono_time,
uint8_t num_nodes, uint8_t *plain, uint16_t plain_size,
uint8_t *response, uint16_t response_size, uint16_t offset)
{
GC_Announces_List *gc_announces_list = (GC_Announces_List *)object;
GC_Public_Announce public_announce;
if (gca_unpack_public_announce(logger, plain, plain_size,
&public_announce) == -1) {
LOGGER_WARNING(logger, "Failed to unpack public group announce");
return -1;
}
const GC_Peer_Announce *new_announce = gca_add_announce(mono_time, gc_announces_list, &public_announce);
if (new_announce == nullptr) {
LOGGER_ERROR(logger, "Failed to add group announce");
return -1;
}
GC_Announce gc_announces[GCA_MAX_SENT_ANNOUNCES];
const int num_ann = gca_get_announces(gc_announces_list,
gc_announces,
GCA_MAX_SENT_ANNOUNCES,
public_announce.chat_public_key,
new_announce->base_announce.peer_public_key);
if (num_ann < 0) {
LOGGER_ERROR(logger, "failed to get group announce");
return -1;
}
assert(num_ann <= UINT8_MAX);
size_t announces_length = 0;
if (gca_pack_announces_list(logger, response + offset, response_size - offset, gc_announces, (uint8_t)num_ann,
&announces_length) != num_ann) {
LOGGER_WARNING(logger, "Failed to pack group announces list");
return -1;
}
return announces_length;
}
void gca_onion_init(GC_Announces_List *group_announce, Onion_Announce *onion_a)
{
onion_announce_extra_data_callback(onion_a, GCA_MAX_SENT_ANNOUNCES * sizeof(GC_Announce), pack_group_announces,
group_announce);
}
#ifndef VANILLA_NACL
int create_gca_announce_request(
const Random *rng, 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, uint16_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 + GCA_ANNOUNCE_MAX_SIZE];
uint8_t *position_in_plain = plain;
const size_t encrypted_size = sizeof(plain) - GCA_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, gc_data_length);
packet[0] = NET_PACKET_ANNOUNCE_REQUEST;
random_nonce(rng, packet + 1);
memcpy(packet + 1 + CRYPTO_NONCE_SIZE, public_key, CRYPTO_PUBLIC_KEY_SIZE);
const int len = encrypt_data(dest_client_id, secret_key, packet + 1, plain,
encrypted_size, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE);
const 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

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2020 The TokTok team.
* Copyright © 2015 Tox project.
*/
#ifndef C_TOXCORE_TOXCORE_GROUP_ONION_ANNOUNCE_H
#define C_TOXCORE_TOXCORE_GROUP_ONION_ANNOUNCE_H
#include "group_announce.h"
#include "onion_announce.h"
non_null()
void gca_onion_init(GC_Announces_List *group_announce, Onion_Announce *onion_a);
non_null()
int create_gca_announce_request(
const Random *rng, 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, uint16_t gc_data_length);
#endif // C_TOXCORE_TOXCORE_GROUP_ONION_ANNOUNCE_H

View File

@ -0,0 +1,423 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2020 The TokTok team.
* Copyright © 2015 Tox project.
*/
/**
* Packer and unpacker functions for saving and loading groups.
*/
#include "group_pack.h"
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "bin_pack.h"
#include "bin_unpack.h"
#include "ccompat.h"
#include "util.h"
non_null()
static bool load_unpack_state_values(GC_Chat *chat, Bin_Unpack *bu)
{
if (!bin_unpack_array_fixed(bu, 8)) {
LOGGER_ERROR(chat->log, "Group state values array malformed");
return false;
}
bool manually_disconnected = false;
uint8_t privacy_state = 0;
uint8_t voice_state = 0;
if (!(bin_unpack_bool(bu, &manually_disconnected)
&& bin_unpack_u16(bu, &chat->shared_state.group_name_len)
&& bin_unpack_u08(bu, &privacy_state)
&& bin_unpack_u16(bu, &chat->shared_state.maxpeers)
&& bin_unpack_u16(bu, &chat->shared_state.password_length)
&& bin_unpack_u32(bu, &chat->shared_state.version)
&& bin_unpack_u32(bu, &chat->shared_state.topic_lock)
&& bin_unpack_u08(bu, &voice_state))) {
LOGGER_ERROR(chat->log, "Failed to unpack state value");
return false;
}
chat->connection_state = manually_disconnected ? CS_DISCONNECTED : CS_CONNECTING;
chat->shared_state.privacy_state = (Group_Privacy_State)privacy_state;
chat->shared_state.voice_state = (Group_Voice_State)voice_state;
// we always load saved groups as private in case the group became private while we were offline.
// this will have no detrimental effect if the group is public, as the correct privacy
// state will be set via sync.
chat->join_type = HJ_PRIVATE;
return true;
}
non_null()
static bool load_unpack_state_bin(GC_Chat *chat, Bin_Unpack *bu)
{
if (!bin_unpack_array_fixed(bu, 5)) {
LOGGER_ERROR(chat->log, "Group state binary array malformed");
return false;
}
if (!(bin_unpack_bin_fixed(bu, chat->shared_state_sig, SIGNATURE_SIZE)
&& bin_unpack_bin_fixed(bu, chat->shared_state.founder_public_key, EXT_PUBLIC_KEY_SIZE)
&& bin_unpack_bin_fixed(bu, chat->shared_state.group_name, chat->shared_state.group_name_len)
&& bin_unpack_bin_fixed(bu, chat->shared_state.password, chat->shared_state.password_length)
&& bin_unpack_bin_fixed(bu, chat->shared_state.mod_list_hash, MOD_MODERATION_HASH_SIZE))) {
LOGGER_ERROR(chat->log, "Failed to unpack state binary data");
return false;
}
return true;
}
non_null()
static bool load_unpack_topic_info(GC_Chat *chat, Bin_Unpack *bu)
{
if (!bin_unpack_array_fixed(bu, 6)) {
LOGGER_ERROR(chat->log, "Group topic array malformed");
return false;
}
if (!(bin_unpack_u32(bu, &chat->topic_info.version)
&& bin_unpack_u16(bu, &chat->topic_info.length)
&& bin_unpack_u16(bu, &chat->topic_info.checksum)
&& bin_unpack_bin_fixed(bu, chat->topic_info.topic, chat->topic_info.length)
&& bin_unpack_bin_fixed(bu, chat->topic_info.public_sig_key, SIG_PUBLIC_KEY_SIZE)
&& bin_unpack_bin_fixed(bu, chat->topic_sig, SIGNATURE_SIZE))) {
LOGGER_ERROR(chat->log, "Failed to unpack topic info");
return false;
}
return true;
}
non_null()
static bool load_unpack_mod_list(GC_Chat *chat, Bin_Unpack *bu)
{
if (!bin_unpack_array_fixed(bu, 2)) {
LOGGER_ERROR(chat->log, "Group mod list array malformed");
return false;
}
if (!bin_unpack_u16(bu, &chat->moderation.num_mods)) {
LOGGER_ERROR(chat->log, "Failed to unpack mod list value");
return false;
}
if (chat->moderation.num_mods == 0) {
bin_unpack_nil(bu);
return true;
}
if (chat->moderation.num_mods > MOD_MAX_NUM_MODERATORS) {
LOGGER_ERROR(chat->log, "moderation count %u exceeds maximum %u", chat->moderation.num_mods, MOD_MAX_NUM_MODERATORS);
return false;
}
uint8_t *packed_mod_list = (uint8_t *)malloc(chat->moderation.num_mods * MOD_LIST_ENTRY_SIZE);
if (packed_mod_list == nullptr) {
LOGGER_ERROR(chat->log, "Failed to allocate memory for packed mod list");
return false;
}
const size_t packed_size = chat->moderation.num_mods * MOD_LIST_ENTRY_SIZE;
if (!bin_unpack_bin_fixed(bu, packed_mod_list, packed_size)) {
LOGGER_ERROR(chat->log, "Failed to unpack mod list binary data");
free(packed_mod_list);
return false;
}
if (mod_list_unpack(&chat->moderation, packed_mod_list, packed_size, chat->moderation.num_mods) == -1) {
LOGGER_ERROR(chat->log, "Failed to unpack mod list info");
free(packed_mod_list);
return false;
}
free(packed_mod_list);
return true;
}
non_null()
static bool load_unpack_keys(GC_Chat *chat, Bin_Unpack *bu)
{
if (!bin_unpack_array_fixed(bu, 4)) {
LOGGER_ERROR(chat->log, "Group keys array malformed");
return false;
}
if (!(bin_unpack_bin_fixed(bu, chat->chat_public_key, EXT_PUBLIC_KEY_SIZE)
&& bin_unpack_bin_fixed(bu, chat->chat_secret_key, EXT_SECRET_KEY_SIZE)
&& bin_unpack_bin_fixed(bu, chat->self_public_key, EXT_PUBLIC_KEY_SIZE)
&& bin_unpack_bin_fixed(bu, chat->self_secret_key, EXT_SECRET_KEY_SIZE))) {
LOGGER_ERROR(chat->log, "Failed to unpack keys");
return false;
}
return true;
}
non_null()
static bool load_unpack_self_info(GC_Chat *chat, Bin_Unpack *bu)
{
if (!bin_unpack_array_fixed(bu, 4)) {
LOGGER_ERROR(chat->log, "Group self info array malformed");
return false;
}
uint8_t self_nick[MAX_GC_NICK_SIZE];
uint16_t self_nick_len = 0;
uint8_t self_role = GR_USER;
uint8_t self_status = GS_NONE;
if (!(bin_unpack_u16(bu, &self_nick_len)
&& bin_unpack_u08(bu, &self_role)
&& bin_unpack_u08(bu, &self_status))) {
LOGGER_ERROR(chat->log, "Failed to unpack self values");
return false;
}
assert(self_nick_len <= MAX_GC_NICK_SIZE);
if (!bin_unpack_bin_fixed(bu, self_nick, self_nick_len)) {
LOGGER_ERROR(chat->log, "Failed to unpack self nick bytes");
return false;
}
// we have to add ourself before setting self info
if (peer_add(chat, nullptr, chat->self_public_key) != 0) {
LOGGER_ERROR(chat->log, "Failed to add self to peer list");
return false;
}
assert(chat->numpeers > 0);
GC_Peer *self = &chat->group[0];
memcpy(self->gconn.addr.public_key, chat->self_public_key, EXT_PUBLIC_KEY_SIZE);
memcpy(self->nick, self_nick, self_nick_len);
self->nick_length = self_nick_len;
self->role = (Group_Role)self_role;
self->status = (Group_Peer_Status)self_status;
self->gconn.confirmed = true;
return true;
}
non_null()
static bool load_unpack_saved_peers(GC_Chat *chat, Bin_Unpack *bu)
{
if (!bin_unpack_array_fixed(bu, 2)) {
LOGGER_ERROR(chat->log, "Group saved peers array malformed");
return false;
}
// Saved peers
uint16_t saved_peers_size = 0;
if (!bin_unpack_u16(bu, &saved_peers_size)) {
LOGGER_ERROR(chat->log, "Failed to unpack saved peers value");
return false;
}
if (saved_peers_size == 0) {
bin_unpack_nil(bu);
return true;
}
uint8_t *saved_peers = (uint8_t *)malloc(saved_peers_size * GC_SAVED_PEER_SIZE);
if (saved_peers == nullptr) {
LOGGER_ERROR(chat->log, "Failed to allocate memory for saved peer list");
return false;
}
if (!bin_unpack_bin_fixed(bu, saved_peers, saved_peers_size)) {
LOGGER_ERROR(chat->log, "Failed to unpack saved peers binary data");
free(saved_peers);
return false;
}
if (unpack_gc_saved_peers(chat, saved_peers, saved_peers_size) == -1) {
LOGGER_ERROR(chat->log, "Failed to unpack saved peers"); // recoverable error
}
free(saved_peers);
return true;
}
bool gc_load_unpack_group(GC_Chat *chat, Bin_Unpack *bu)
{
if (!bin_unpack_array_fixed(bu, 7)) {
LOGGER_ERROR(chat->log, "Group info array malformed");
return false;
}
return load_unpack_state_values(chat, bu)
&& load_unpack_state_bin(chat, bu)
&& load_unpack_topic_info(chat, bu)
&& load_unpack_mod_list(chat, bu)
&& load_unpack_keys(chat, bu)
&& load_unpack_self_info(chat, bu)
&& load_unpack_saved_peers(chat, bu);
}
non_null()
static void save_pack_state_values(const GC_Chat *chat, Bin_Pack *bp)
{
bin_pack_array(bp, 8);
bin_pack_bool(bp, chat->connection_state == CS_DISCONNECTED); // 1
bin_pack_u16(bp, chat->shared_state.group_name_len); // 2
bin_pack_u08(bp, chat->shared_state.privacy_state); // 3
bin_pack_u16(bp, chat->shared_state.maxpeers); // 4
bin_pack_u16(bp, chat->shared_state.password_length); // 5
bin_pack_u32(bp, chat->shared_state.version); // 6
bin_pack_u32(bp, chat->shared_state.topic_lock); // 7
bin_pack_u08(bp, chat->shared_state.voice_state); // 8
}
non_null()
static void save_pack_state_bin(const GC_Chat *chat, Bin_Pack *bp)
{
bin_pack_array(bp, 5);
bin_pack_bin(bp, chat->shared_state_sig, SIGNATURE_SIZE); // 1
bin_pack_bin(bp, chat->shared_state.founder_public_key, EXT_PUBLIC_KEY_SIZE); // 2
bin_pack_bin(bp, chat->shared_state.group_name, chat->shared_state.group_name_len); // 3
bin_pack_bin(bp, chat->shared_state.password, chat->shared_state.password_length); // 4
bin_pack_bin(bp, chat->shared_state.mod_list_hash, MOD_MODERATION_HASH_SIZE); // 5
}
non_null()
static void save_pack_topic_info(const GC_Chat *chat, Bin_Pack *bp)
{
bin_pack_array(bp, 6);
bin_pack_u32(bp, chat->topic_info.version); // 1
bin_pack_u16(bp, chat->topic_info.length); // 2
bin_pack_u16(bp, chat->topic_info.checksum); // 3
bin_pack_bin(bp, chat->topic_info.topic, chat->topic_info.length); // 4
bin_pack_bin(bp, chat->topic_info.public_sig_key, SIG_PUBLIC_KEY_SIZE); // 5
bin_pack_bin(bp, chat->topic_sig, SIGNATURE_SIZE); // 6
}
non_null()
static void save_pack_mod_list(const GC_Chat *chat, Bin_Pack *bp)
{
bin_pack_array(bp, 2);
const uint16_t num_mods = min_u16(chat->moderation.num_mods, MOD_MAX_NUM_MODERATORS);
if (num_mods == 0) {
bin_pack_u16(bp, num_mods); // 1
bin_pack_nil(bp); // 2
return;
}
uint8_t *packed_mod_list = (uint8_t *)malloc(num_mods * MOD_LIST_ENTRY_SIZE);
// we can still recover without the mod list
if (packed_mod_list == nullptr) {
bin_pack_u16(bp, 0); // 1
bin_pack_nil(bp); // 2
LOGGER_ERROR(chat->log, "Failed to allocate memory for moderation list");
return;
}
bin_pack_u16(bp, num_mods); // 1
mod_list_pack(&chat->moderation, packed_mod_list);
const size_t packed_size = num_mods * MOD_LIST_ENTRY_SIZE;
bin_pack_bin(bp, packed_mod_list, packed_size); // 2
free(packed_mod_list);
}
non_null()
static void save_pack_keys(const GC_Chat *chat, Bin_Pack *bp)
{
bin_pack_array(bp, 4);
bin_pack_bin(bp, chat->chat_public_key, EXT_PUBLIC_KEY_SIZE); // 1
bin_pack_bin(bp, chat->chat_secret_key, EXT_SECRET_KEY_SIZE); // 2
bin_pack_bin(bp, chat->self_public_key, EXT_PUBLIC_KEY_SIZE); // 3
bin_pack_bin(bp, chat->self_secret_key, EXT_SECRET_KEY_SIZE); // 4
}
non_null()
static void save_pack_self_info(const GC_Chat *chat, Bin_Pack *bp)
{
bin_pack_array(bp, 4);
const GC_Peer *self = &chat->group[0];
assert(self->nick_length <= MAX_GC_NICK_SIZE);
bin_pack_u16(bp, self->nick_length); // 1
bin_pack_u08(bp, (uint8_t)self->role); // 2
bin_pack_u08(bp, (uint8_t)self->status); // 3
bin_pack_bin(bp, self->nick, self->nick_length); // 4
}
non_null()
static void save_pack_saved_peers(const GC_Chat *chat, Bin_Pack *bp)
{
bin_pack_array(bp, 2);
uint8_t *saved_peers = (uint8_t *)malloc(GC_MAX_SAVED_PEERS * GC_SAVED_PEER_SIZE);
// we can still recover without the saved peers list
if (saved_peers == nullptr) {
bin_pack_u16(bp, 0); // 1
bin_pack_nil(bp); // 2
LOGGER_ERROR(chat->log, "Failed to allocate memory for saved peers list");
return;
}
uint16_t packed_size = 0;
const int count = pack_gc_saved_peers(chat, saved_peers, GC_MAX_SAVED_PEERS * GC_SAVED_PEER_SIZE, &packed_size);
if (count < 0) {
LOGGER_ERROR(chat->log, "Failed to pack saved peers");
}
bin_pack_u16(bp, packed_size); // 1
if (packed_size == 0) {
bin_pack_nil(bp); // 2
free(saved_peers);
return;
}
bin_pack_bin(bp, saved_peers, packed_size); // 2
free(saved_peers);
}
void gc_save_pack_group(const GC_Chat *chat, Bin_Pack *bp)
{
if (chat->numpeers == 0) {
LOGGER_ERROR(chat->log, "Failed to pack group: numpeers is 0");
return;
}
bin_pack_array(bp, 7);
save_pack_state_values(chat, bp); // 1
save_pack_state_bin(chat, bp); // 2
save_pack_topic_info(chat, bp); // 3
save_pack_mod_list(chat, bp); // 4
save_pack_keys(chat, bp); // 5
save_pack_self_info(chat, bp); // 6
save_pack_saved_peers(chat, bp); // 7
}

View File

@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2020 The TokTok team.
* Copyright © 2015 Tox project.
*/
/**
* Packer and unpacker functions for saving and loading groups.
*/
#ifndef GROUP_PACK_H
#define GROUP_PACK_H
#include <stdbool.h>
#include "bin_pack.h"
#include "bin_unpack.h"
#include "group_common.h"
/**
* Packs group data from `chat` into `mp` in binary format. Parallel to the
* `gc_load_unpack_group` function.
*/
non_null()
void gc_save_pack_group(const GC_Chat *chat, Bin_Pack *bp);
/**
* Unpacks binary group data from `obj` into `chat`. Parallel to the `gc_save_pack_group`
* function.
*
* Return true if unpacking is successful.
*/
non_null()
bool gc_load_unpack_group(GC_Chat *chat, Bin_Unpack *bu);
#endif // GROUP_PACK_H

Some files were not shown because too many files have changed in this diff Show More