summaryrefslogtreecommitdiff
path: root/protocols/Tox/libtox/src
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2022-07-24 16:44:24 +0300
committerGeorge Hazan <ghazan@miranda.im>2022-07-24 16:44:24 +0300
commite55d071e5485a937efd427d159b76c208cccdcce (patch)
tree48d09dc5cf1df2581fd6471b5ccf1560015fa3a4 /protocols/Tox/libtox/src
parentf36629f67153bc500c828cf51de31988122a1024 (diff)
fixes #3118 (Update toxcore to 0.2.18)
Diffstat (limited to 'protocols/Tox/libtox/src')
-rw-r--r--protocols/Tox/libtox/src/third_party/cmp/LICENSE22
-rw-r--r--protocols/Tox/libtox/src/third_party/cmp/README.md223
-rw-r--r--protocols/Tox/libtox/src/third_party/cmp/cmp.c3561
-rw-r--r--protocols/Tox/libtox/src/third_party/cmp/cmp.h572
-rw-r--r--protocols/Tox/libtox/src/toxcore/DHT.c920
-rw-r--r--protocols/Tox/libtox/src/toxcore/DHT.h381
-rw-r--r--protocols/Tox/libtox/src/toxcore/LAN_discovery.c222
-rw-r--r--protocols/Tox/libtox/src/toxcore/LAN_discovery.h19
-rw-r--r--protocols/Tox/libtox/src/toxcore/Messenger.c1552
-rw-r--r--protocols/Tox/libtox/src/toxcore/Messenger.h548
-rw-r--r--protocols/Tox/libtox/src/toxcore/TCP_client.c508
-rw-r--r--protocols/Tox/libtox/src/toxcore/TCP_client.h78
-rw-r--r--protocols/Tox/libtox/src/toxcore/TCP_common.c94
-rw-r--r--protocols/Tox/libtox/src/toxcore/TCP_common.h58
-rw-r--r--protocols/Tox/libtox/src/toxcore/TCP_connection.c590
-rw-r--r--protocols/Tox/libtox/src/toxcore/TCP_connection.h143
-rw-r--r--protocols/Tox/libtox/src/toxcore/TCP_server.c420
-rw-r--r--protocols/Tox/libtox/src/toxcore/TCP_server.h20
-rw-r--r--protocols/Tox/libtox/src/toxcore/announce.c689
-rw-r--r--protocols/Tox/libtox/src/toxcore/announce.h67
-rw-r--r--protocols/Tox/libtox/src/toxcore/attributes.h31
-rw-r--r--protocols/Tox/libtox/src/toxcore/bin_pack.c161
-rw-r--r--protocols/Tox/libtox/src/toxcore/bin_pack.h122
-rw-r--r--protocols/Tox/libtox/src/toxcore/bin_unpack.c185
-rw-r--r--protocols/Tox/libtox/src/toxcore/bin_unpack.h100
-rw-r--r--protocols/Tox/libtox/src/toxcore/ccompat.c4
-rw-r--r--protocols/Tox/libtox/src/toxcore/ccompat.h36
-rw-r--r--protocols/Tox/libtox/src/toxcore/crypto_core.c294
-rw-r--r--protocols/Tox/libtox/src/toxcore/crypto_core.h236
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/conference_connected.c190
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/conference_invite.c249
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/conference_message.c266
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/conference_peer_list_changed.c195
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/conference_peer_name.c250
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/conference_title.c248
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/events_alloc.c64
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/events_alloc.h216
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/file_chunk_request.c243
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/file_recv.c282
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/file_recv_chunk.c265
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/file_recv_control.c228
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/friend_connection_status.c215
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/friend_lossless_packet.c233
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/friend_lossy_packet.c232
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/friend_message.c248
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/friend_name.c231
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/friend_read_receipt.c210
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/friend_request.c232
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/friend_status.c211
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/friend_status_message.c234
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/friend_typing.c208
-rw-r--r--protocols/Tox/libtox/src/toxcore/events/self_connection_status.c190
-rw-r--r--protocols/Tox/libtox/src/toxcore/forwarding.c395
-rw-r--r--protocols/Tox/libtox/src/toxcore/forwarding.h125
-rw-r--r--protocols/Tox/libtox/src/toxcore/friend_connection.c277
-rw-r--r--protocols/Tox/libtox/src/toxcore/friend_connection.h107
-rw-r--r--protocols/Tox/libtox/src/toxcore/friend_requests.c36
-rw-r--r--protocols/Tox/libtox/src/toxcore/friend_requests.h23
-rw-r--r--protocols/Tox/libtox/src/toxcore/group.c1226
-rw-r--r--protocols/Tox/libtox/src/toxcore/group.h444
-rw-r--r--protocols/Tox/libtox/src/toxcore/group_announce.c462
-rw-r--r--protocols/Tox/libtox/src/toxcore/group_announce.h218
-rw-r--r--protocols/Tox/libtox/src/toxcore/group_moderation.c864
-rw-r--r--protocols/Tox/libtox/src/toxcore/group_moderation.h288
-rw-r--r--protocols/Tox/libtox/src/toxcore/list.c66
-rw-r--r--protocols/Tox/libtox/src/toxcore/list.h44
-rw-r--r--protocols/Tox/libtox/src/toxcore/logger.c21
-rw-r--r--protocols/Tox/libtox/src/toxcore/logger.h66
-rw-r--r--protocols/Tox/libtox/src/toxcore/mono_time.c114
-rw-r--r--protocols/Tox/libtox/src/toxcore/mono_time.h20
-rw-r--r--protocols/Tox/libtox/src/toxcore/net_crypto.c784
-rw-r--r--protocols/Tox/libtox/src/toxcore/net_crypto.h168
-rw-r--r--protocols/Tox/libtox/src/toxcore/network.c1041
-rw-r--r--protocols/Tox/libtox/src/toxcore/network.h337
-rw-r--r--protocols/Tox/libtox/src/toxcore/onion.c122
-rw-r--r--protocols/Tox/libtox/src/toxcore/onion.h48
-rw-r--r--protocols/Tox/libtox/src/toxcore/onion_announce.c368
-rw-r--r--protocols/Tox/libtox/src/toxcore/onion_announce.h62
-rw-r--r--protocols/Tox/libtox/src/toxcore/onion_client.c738
-rw-r--r--protocols/Tox/libtox/src/toxcore/onion_client.h103
-rw-r--r--protocols/Tox/libtox/src/toxcore/ping.c57
-rw-r--r--protocols/Tox/libtox/src/toxcore/ping.h16
-rw-r--r--protocols/Tox/libtox/src/toxcore/ping_array.c20
-rw-r--r--protocols/Tox/libtox/src/toxcore/ping_array.h14
-rw-r--r--protocols/Tox/libtox/src/toxcore/state.c2
-rw-r--r--protocols/Tox/libtox/src/toxcore/state.h8
-rw-r--r--protocols/Tox/libtox/src/toxcore/timed_auth.c44
-rw-r--r--protocols/Tox/libtox/src/toxcore/timed_auth.h35
-rw-r--r--protocols/Tox/libtox/src/toxcore/tox.c885
-rw-r--r--protocols/Tox/libtox/src/toxcore/tox.h148
-rw-r--r--protocols/Tox/libtox/src/toxcore/tox_api.c117
-rw-r--r--protocols/Tox/libtox/src/toxcore/tox_dispatch.c484
-rw-r--r--protocols/Tox/libtox/src/toxcore/tox_dispatch.h144
-rw-r--r--protocols/Tox/libtox/src/toxcore/tox_events.c290
-rw-r--r--protocols/Tox/libtox/src/toxcore/tox_events.h354
-rw-r--r--protocols/Tox/libtox/src/toxcore/tox_private.c149
-rw-r--r--protocols/Tox/libtox/src/toxcore/tox_private.h102
-rw-r--r--protocols/Tox/libtox/src/toxcore/tox_struct.h55
-rw-r--r--protocols/Tox/libtox/src/toxcore/tox_unpack.c70
-rw-r--r--protocols/Tox/libtox/src/toxcore/tox_unpack.h18
-rw-r--r--protocols/Tox/libtox/src/toxcore/util.c55
-rw-r--r--protocols/Tox/libtox/src/toxcore/util.h37
-rw-r--r--protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.c245
-rw-r--r--protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.h20
104 files changed, 23456 insertions, 5476 deletions
diff --git a/protocols/Tox/libtox/src/third_party/cmp/LICENSE b/protocols/Tox/libtox/src/third_party/cmp/LICENSE
new file mode 100644
index 0000000000..8adaa77c83
--- /dev/null
+++ b/protocols/Tox/libtox/src/third_party/cmp/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2020 Charles Gunyon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/protocols/Tox/libtox/src/third_party/cmp/README.md b/protocols/Tox/libtox/src/third_party/cmp/README.md
new file mode 100644
index 0000000000..a726ceb2a8
--- /dev/null
+++ b/protocols/Tox/libtox/src/third_party/cmp/README.md
@@ -0,0 +1,223 @@
+# CMP
+
+[![Build Status](https://travis-ci.org/camgunz/cmp.svg?branch=master)](https://travis-ci.org/camgunz/cmp) [![Coverage Status](https://coveralls.io/repos/github/camgunz/cmp/badge.svg?branch=develop)](https://coveralls.io/github/camgunz/cmp?branch=develop)
+
+CMP is a C implementation of the MessagePack serialization format. It
+currently implements version 5 of the [MessagePack
+Spec](http://github.com/msgpack/msgpack/blob/master/spec.md).
+
+CMP's goal is to be lightweight and straightforward, forcing nothing on the
+programmer.
+
+## License
+
+While I'm a big believer in the GPL, I license CMP under the MIT license.
+
+## Example Usage
+
+The following examples use a file as the backend, and are modeled after the
+examples included with the msgpack-c project.
+
+```C
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cmp.h"
+
+static bool read_bytes(void *data, size_t sz, FILE *fh) {
+ return fread(data, sizeof(uint8_t), sz, fh) == (sz * sizeof(uint8_t));
+}
+
+static bool file_reader(cmp_ctx_t *ctx, void *data, size_t limit) {
+ return read_bytes(data, limit, (FILE *)ctx->buf);
+}
+
+static bool file_skipper(cmp_ctx_t *ctx, size_t count) {
+ return fseek((FILE *)ctx->buf, count, SEEK_CUR);
+}
+
+static size_t file_writer(cmp_ctx_t *ctx, const void *data, size_t count) {
+ return fwrite(data, sizeof(uint8_t), count, (FILE *)ctx->buf);
+}
+
+static void error_and_exit(const char *msg) {
+ fprintf(stderr, "%s\n\n", msg);
+ exit(EXIT_FAILURE);
+}
+
+int main(void) {
+ FILE *fh = NULL;
+ cmp_ctx_t cmp = {0};
+ uint32_t array_size = 0;
+ uint32_t str_size = 0;
+ char hello[6] = {0};
+ char message_pack[12] = {0};
+
+ fh = fopen("cmp_data.dat", "w+b");
+
+ if (fh == NULL) {
+ error_and_exit("Error opening data.dat");
+ }
+
+ cmp_init(&cmp, fh, file_reader, file_skipper, file_writer);
+
+ if (!cmp_write_array(&cmp, 2)) {
+ error_and_exit(cmp_strerror(&cmp));
+ }
+
+ if (!cmp_write_str(&cmp, "Hello", 5)) {
+ error_and_exit(cmp_strerror(&cmp));
+ }
+
+ if (!cmp_write_str(&cmp, "MessagePack", 11)) {
+ error_and_exit(cmp_strerror(&cmp));
+ }
+
+ rewind(fh);
+
+ if (!cmp_read_array(&cmp, &array_size)) {
+ error_and_exit(cmp_strerror(&cmp));
+ }
+
+ /* You can read the str byte size and then read str bytes... */
+
+ if (!cmp_read_str_size(&cmp, &str_size)) {
+ error_and_exit(cmp_strerror(&cmp));
+ }
+
+ if (str_size > (sizeof(hello) - 1)) {
+ error_and_exit("Packed 'hello' length too long\n");
+ }
+
+ if (!read_bytes(hello, str_size, fh)) {
+ error_and_exit(cmp_strerror(&cmp));
+ }
+
+ /*
+ * ...or you can set the maximum number of bytes to read and do it all in
+ * one call
+ */
+
+ str_size = sizeof(message_pack);
+ if (!cmp_read_str(&cmp, message_pack, &str_size)) {
+ error_and_exit(cmp_strerror(&cmp));
+ }
+
+ printf("Array Length: %u.\n", array_size);
+ printf("[\"%s\", \"%s\"]\n", hello, message_pack);
+
+ fclose(fh);
+
+ return EXIT_SUCCESS;
+}
+```
+
+## Advanced Usage
+
+See the `examples` folder.
+
+## Fast, Lightweight, Flexible, and Robust
+
+CMP uses no internal buffers; conversions, encoding and decoding are done on
+the fly.
+
+CMP's source and header file together are ~4k LOC.
+
+CMP makes no heap allocations.
+
+CMP uses standardized types rather than declaring its own, and it depends only
+on `stdbool.h`, `stdint.h` and `string.h`.
+
+CMP is written using C89 (ANSI C), aside, of course, from its use of
+fixed-width integer types and `bool`.
+
+On the other hand, CMP's test suite requires C99.
+
+CMP only requires the programmer supply a read function, a write function, and
+an optional skip function. In this way, the programmer can use CMP on memory,
+files, sockets, etc.
+
+CMP is portable. It uses fixed-width integer types, and checks the endianness
+of the machine at runtime before swapping bytes (MessagePack is big-endian).
+
+CMP provides a fairly comprehensive error reporting mechanism modeled after
+`errno` and `strerror`.
+
+CMP is thread aware; while contexts cannot be shared between threads, each
+thread may use its own context freely.
+
+CMP is tested using the MessagePack test suite as well as a large set of custom
+test cases. Its small test program is compiled with clang using `-Wall -Werror
+-Wextra ...` along with several other flags, and generates no compilation
+errors in either clang or GCC.
+
+CMP's source is written as readably as possible, using explicit, descriptive
+variable names and a consistent, clear style.
+
+CMP's source is written to be as secure as possible. Its testing suite checks
+for invalid values, and data is always treated as suspect before it passes
+validation.
+
+CMP's API is designed to be clear, convenient and unsurprising. Strings are
+null-terminated, binary data is not, error codes are clear, and so on.
+
+CMP provides optional backwards compatibility for use with other MessagePack
+implementations that only implement version 4 of the spec.
+
+## Building
+
+There is no build system for CMP. The programmer can drop `cmp.c` and `cmp.h`
+in their source tree and modify as necessary. No special compiler settings are
+required to build it, and it generates no compilation errors in either clang or
+gcc.
+
+## Versioning
+
+CMP's versions are single integers. I don't use semantic versioning because
+I don't guarantee that any version is completely compatible with any other. In
+general, semantic versioning provides a false sense of security. You should be
+evaluating compatibility yourself, not relying on some stranger's versioning
+convention.
+
+## Stability
+
+I only guarantee stability for versions released on
+[the releases page](../../releases). While rare, both `master` and `develop`
+branches may have errors or mismatched versions.
+
+## Backwards Compatibility
+
+Version 4 of the MessagePack spec has no `BIN` type, and provides no `STR8`
+marker. In order to remain backwards compatible with version 4 of MessagePack,
+do the following:
+
+Avoid these functions:
+
+ - `cmp_write_bin`
+ - `cmp_write_bin_marker`
+ - `cmp_write_str8_marker`
+ - `cmp_write_str8`
+ - `cmp_write_bin8_marker`
+ - `cmp_write_bin8`
+ - `cmp_write_bin16_marker`
+ - `cmp_write_bin16`
+ - `cmp_write_bin32_marker`
+ - `cmp_write_bin32`
+
+Use these functions in lieu of their v5 counterparts:
+
+ - `cmp_write_str_marker_v4` instead of `cmp_write_str_marker`
+ - `cmp_write_str_v4` instead of `cmp_write_str`
+ - `cmp_write_object_v4` instead of `cmp_write_object`
+
+## Disabling Floating Point Operations
+
+Thanks to [tdragon](https://github.com/tdragon) it's possible to disable
+floating point operations in CMP by defining `CMP_NO_FLOAT`. No floating point
+functionality will be included. Fair warning: this changes the ABI.
+
+## Setting Endianness at Compile Time
+
+CMP will honor `WORDS_BIGENDIAN`. If defined to `0` it will convert data
+to/from little-endian format when writing/reading. If defined to `1` it won't.
+If not defined, CMP will check at runtime.
diff --git a/protocols/Tox/libtox/src/third_party/cmp/cmp.c b/protocols/Tox/libtox/src/third_party/cmp/cmp.c
new file mode 100644
index 0000000000..405b5780b2
--- /dev/null
+++ b/protocols/Tox/libtox/src/third_party/cmp/cmp.c
@@ -0,0 +1,3561 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2020 Charles Gunyon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "cmp.h"
+
+static const uint32_t version = 20;
+static const uint32_t mp_version = 5;
+
+enum {
+ POSITIVE_FIXNUM_MARKER = 0x00,
+ FIXMAP_MARKER = 0x80,
+ FIXARRAY_MARKER = 0x90,
+ FIXSTR_MARKER = 0xA0,
+ NIL_MARKER = 0xC0,
+ FALSE_MARKER = 0xC2,
+ TRUE_MARKER = 0xC3,
+ BIN8_MARKER = 0xC4,
+ BIN16_MARKER = 0xC5,
+ BIN32_MARKER = 0xC6,
+ EXT8_MARKER = 0xC7,
+ EXT16_MARKER = 0xC8,
+ EXT32_MARKER = 0xC9,
+ FLOAT_MARKER = 0xCA,
+ DOUBLE_MARKER = 0xCB,
+ U8_MARKER = 0xCC,
+ U16_MARKER = 0xCD,
+ U32_MARKER = 0xCE,
+ U64_MARKER = 0xCF,
+ S8_MARKER = 0xD0,
+ S16_MARKER = 0xD1,
+ S32_MARKER = 0xD2,
+ S64_MARKER = 0xD3,
+ FIXEXT1_MARKER = 0xD4,
+ FIXEXT2_MARKER = 0xD5,
+ FIXEXT4_MARKER = 0xD6,
+ FIXEXT8_MARKER = 0xD7,
+ FIXEXT16_MARKER = 0xD8,
+ STR8_MARKER = 0xD9,
+ STR16_MARKER = 0xDA,
+ STR32_MARKER = 0xDB,
+ ARRAY16_MARKER = 0xDC,
+ ARRAY32_MARKER = 0xDD,
+ MAP16_MARKER = 0xDE,
+ MAP32_MARKER = 0xDF,
+ NEGATIVE_FIXNUM_MARKER = 0xE0
+};
+
+enum {
+ FIXARRAY_SIZE = 0xF,
+ FIXMAP_SIZE = 0xF,
+ FIXSTR_SIZE = 0x1F
+};
+
+enum {
+ ERROR_NONE,
+ STR_DATA_LENGTH_TOO_LONG_ERROR,
+ BIN_DATA_LENGTH_TOO_LONG_ERROR,
+ ARRAY_LENGTH_TOO_LONG_ERROR,
+ MAP_LENGTH_TOO_LONG_ERROR,
+ INPUT_VALUE_TOO_LARGE_ERROR,
+ FIXED_VALUE_WRITING_ERROR,
+ TYPE_MARKER_READING_ERROR,
+ TYPE_MARKER_WRITING_ERROR,
+ DATA_READING_ERROR,
+ DATA_WRITING_ERROR,
+ EXT_TYPE_READING_ERROR,
+ EXT_TYPE_WRITING_ERROR,
+ INVALID_TYPE_ERROR,
+ LENGTH_READING_ERROR,
+ LENGTH_WRITING_ERROR,
+ SKIP_DEPTH_LIMIT_EXCEEDED_ERROR,
+ INTERNAL_ERROR,
+ DISABLED_FLOATING_POINT_ERROR,
+ ERROR_MAX
+};
+
+static const char * const cmp_error_messages[ERROR_MAX + 1] = {
+ "No Error",
+ "Specified string data length is too long (> 0xFFFFFFFF)",
+ "Specified binary data length is too long (> 0xFFFFFFFF)",
+ "Specified array length is too long (> 0xFFFFFFFF)",
+ "Specified map length is too long (> 0xFFFFFFFF)",
+ "Input value is too large",
+ "Error writing fixed value",
+ "Error reading type marker",
+ "Error writing type marker",
+ "Error reading packed data",
+ "Error writing packed data",
+ "Error reading ext type",
+ "Error writing ext type",
+ "Invalid type",
+ "Error reading size",
+ "Error writing size",
+ "Depth limit exceeded while skipping",
+ "Internal error",
+ "Floating point operations disabled",
+ "Max Error"
+};
+
+#ifdef WORDS_BIGENDIAN
+#define is_bigendian() (WORDS_BIGENDIAN)
+#else
+static const int32_t _i = 1;
+#define is_bigendian() ((*(const char *)&_i) == 0)
+#endif
+
+static uint16_t be16(uint16_t x) {
+ char *b = (char *)&x;
+
+ if (!is_bigendian()) {
+ char swap = b[0];
+ b[0] = b[1];
+ b[1] = swap;
+ }
+
+ return x;
+}
+
+static int16_t sbe16(int16_t x) {
+ return (int16_t)be16((uint16_t)x);
+}
+
+static uint32_t be32(uint32_t x) {
+ char *b = (char *)&x;
+
+ if (!is_bigendian()) {
+ char swap = b[0];
+ b[0] = b[3];
+ b[3] = swap;
+
+ swap = b[1];
+ b[1] = b[2];
+ b[2] = swap;
+ }
+
+ return x;
+}
+
+static int32_t sbe32(int32_t x) {
+ return (int32_t)be32((uint32_t)x);
+}
+
+static uint64_t be64(uint64_t x) {
+ char *b = (char *)&x;
+
+ if (!is_bigendian()) {
+ char swap;
+
+ swap = b[0];
+ b[0] = b[7];
+ b[7] = swap;
+
+ swap = b[1];
+ b[1] = b[6];
+ b[6] = swap;
+
+ swap = b[2];
+ b[2] = b[5];
+ b[5] = swap;
+
+ swap = b[3];
+ b[3] = b[4];
+ b[4] = swap;
+ }
+
+ return x;
+}
+
+static int64_t sbe64(int64_t x) {
+ return (int64_t)be64((uint64_t)x);
+}
+
+#ifndef CMP_NO_FLOAT
+static float decode_befloat(const char *b) {
+ float f = 0.;
+ char *fb = (char *)&f;
+
+ if (!is_bigendian()) {
+ fb[0] = b[3];
+ fb[1] = b[2];
+ fb[2] = b[1];
+ fb[3] = b[0];
+ }
+ else {
+ fb[0] = b[0];
+ fb[1] = b[1];
+ fb[2] = b[2];
+ fb[3] = b[3];
+ }
+
+ return f;
+}
+
+static double decode_bedouble(const char *b) {
+ double d = 0.;
+ char *db = (char *)&d;
+
+ if (!is_bigendian()) {
+ db[0] = b[7];
+ db[1] = b[6];
+ db[2] = b[5];
+ db[3] = b[4];
+ db[4] = b[3];
+ db[5] = b[2];
+ db[6] = b[1];
+ db[7] = b[0];
+ }
+ else {
+ db[0] = b[0];
+ db[1] = b[1];
+ db[2] = b[2];
+ db[3] = b[3];
+ db[4] = b[4];
+ db[5] = b[5];
+ db[6] = b[6];
+ db[7] = b[7];
+ }
+
+ return d;
+}
+#endif /* CMP_NO_FLOAT */
+
+static bool read_byte(cmp_ctx_t *ctx, uint8_t *x) {
+ return ctx->read(ctx, x, sizeof(uint8_t));
+}
+
+static bool write_byte(cmp_ctx_t *ctx, uint8_t x) {
+ return (ctx->write(ctx, &x, sizeof(uint8_t)) == (sizeof(uint8_t)));
+}
+
+static bool skip_bytes(cmp_ctx_t *ctx, size_t count) {
+ if (ctx->skip) {
+ return ctx->skip(ctx, count);
+ }
+ else {
+ uint8_t floor;
+ size_t i;
+
+ for (i = 0; i < count; i++) {
+ if (!ctx->read(ctx, &floor, sizeof(uint8_t))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
+static bool read_type_marker(cmp_ctx_t *ctx, uint8_t *marker) {
+ if (read_byte(ctx, marker)) {
+ return true;
+ }
+
+ ctx->error = TYPE_MARKER_READING_ERROR;
+ return false;
+}
+
+static bool write_type_marker(cmp_ctx_t *ctx, uint8_t marker) {
+ if (write_byte(ctx, marker))
+ return true;
+
+ ctx->error = TYPE_MARKER_WRITING_ERROR;
+ return false;
+}
+
+static bool write_fixed_value(cmp_ctx_t *ctx, uint8_t value) {
+ if (write_byte(ctx, value))
+ return true;
+
+ ctx->error = FIXED_VALUE_WRITING_ERROR;
+ return false;
+}
+
+static bool type_marker_to_cmp_type(uint8_t type_marker, uint8_t *cmp_type) {
+ if (type_marker <= 0x7F) {
+ *cmp_type = CMP_TYPE_POSITIVE_FIXNUM;
+ return true;
+ }
+
+ if (type_marker <= 0x8F) {
+ *cmp_type = CMP_TYPE_FIXMAP;
+ return true;
+ }
+
+ if (type_marker <= 0x9F) {
+ *cmp_type = CMP_TYPE_FIXARRAY;
+ return true;
+ }
+
+ if (type_marker <= 0xBF) {
+ *cmp_type = CMP_TYPE_FIXSTR;
+ return true;
+ }
+
+ if (type_marker >= 0xE0) {
+ *cmp_type = CMP_TYPE_NEGATIVE_FIXNUM;
+ return true;
+ }
+
+ switch (type_marker) {
+ case NIL_MARKER:
+ *cmp_type = CMP_TYPE_NIL;
+ return true;
+ case FALSE_MARKER:
+ *cmp_type = CMP_TYPE_BOOLEAN;
+ return true;
+ case TRUE_MARKER:
+ *cmp_type = CMP_TYPE_BOOLEAN;
+ return true;
+ case BIN8_MARKER:
+ *cmp_type = CMP_TYPE_BIN8;
+ return true;
+ case BIN16_MARKER:
+ *cmp_type = CMP_TYPE_BIN16;
+ return true;
+ case BIN32_MARKER:
+ *cmp_type = CMP_TYPE_BIN32;
+ return true;
+ case EXT8_MARKER:
+ *cmp_type = CMP_TYPE_EXT8;
+ return true;
+ case EXT16_MARKER:
+ *cmp_type = CMP_TYPE_EXT16;
+ return true;
+ case EXT32_MARKER:
+ *cmp_type = CMP_TYPE_EXT32;
+ return true;
+ case FLOAT_MARKER:
+ *cmp_type = CMP_TYPE_FLOAT;
+ return true;
+ case DOUBLE_MARKER:
+ *cmp_type = CMP_TYPE_DOUBLE;
+ return true;
+ case U8_MARKER:
+ *cmp_type = CMP_TYPE_UINT8;
+ return true;
+ case U16_MARKER:
+ *cmp_type = CMP_TYPE_UINT16;
+ return true;
+ case U32_MARKER:
+ *cmp_type = CMP_TYPE_UINT32;
+ return true;
+ case U64_MARKER:
+ *cmp_type = CMP_TYPE_UINT64;
+ return true;
+ case S8_MARKER:
+ *cmp_type = CMP_TYPE_SINT8;
+ return true;
+ case S16_MARKER:
+ *cmp_type = CMP_TYPE_SINT16;
+ return true;
+ case S32_MARKER:
+ *cmp_type = CMP_TYPE_SINT32;
+ return true;
+ case S64_MARKER:
+ *cmp_type = CMP_TYPE_SINT64;
+ return true;
+ case FIXEXT1_MARKER:
+ *cmp_type = CMP_TYPE_FIXEXT1;
+ return true;
+ case FIXEXT2_MARKER:
+ *cmp_type = CMP_TYPE_FIXEXT2;
+ return true;
+ case FIXEXT4_MARKER:
+ *cmp_type = CMP_TYPE_FIXEXT4;
+ return true;
+ case FIXEXT8_MARKER:
+ *cmp_type = CMP_TYPE_FIXEXT8;
+ return true;
+ case FIXEXT16_MARKER:
+ *cmp_type = CMP_TYPE_FIXEXT16;
+ return true;
+ case STR8_MARKER:
+ *cmp_type = CMP_TYPE_STR8;
+ return true;
+ case STR16_MARKER:
+ *cmp_type = CMP_TYPE_STR16;
+ return true;
+ case STR32_MARKER:
+ *cmp_type = CMP_TYPE_STR32;
+ return true;
+ case ARRAY16_MARKER:
+ *cmp_type = CMP_TYPE_ARRAY16;
+ return true;
+ case ARRAY32_MARKER:
+ *cmp_type = CMP_TYPE_ARRAY32;
+ return true;
+ case MAP16_MARKER:
+ *cmp_type = CMP_TYPE_MAP16;
+ return true;
+ case MAP32_MARKER:
+ *cmp_type = CMP_TYPE_MAP32;
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool read_type_size(cmp_ctx_t *ctx, uint8_t type_marker,
+ uint8_t cmp_type,
+ uint32_t *size) {
+ uint8_t u8temp = 0;
+ uint16_t u16temp = 0;
+ uint32_t u32temp = 0;
+
+ switch (cmp_type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ *size = 0;
+ return true;
+ case CMP_TYPE_FIXMAP:
+ *size = (type_marker & FIXMAP_SIZE);
+ return true;
+ case CMP_TYPE_FIXARRAY:
+ *size = (type_marker & FIXARRAY_SIZE);
+ return true;
+ case CMP_TYPE_FIXSTR:
+ *size = (type_marker & FIXSTR_SIZE);
+ return true;
+ case CMP_TYPE_NIL:
+ *size = 0;
+ return true;
+ case CMP_TYPE_BOOLEAN:
+ *size = 0;
+ return true;
+ case CMP_TYPE_BIN8:
+ if (!ctx->read(ctx, &u8temp, sizeof(uint8_t))) {
+ ctx->error = LENGTH_READING_ERROR;
+ return false;
+ }
+ *size = u8temp;
+ return true;
+ case CMP_TYPE_BIN16:
+ if (!ctx->read(ctx, &u16temp, sizeof(uint16_t))) {
+ ctx->error = LENGTH_READING_ERROR;
+ return false;
+ }
+ *size = be16(u16temp);
+ return true;
+ case CMP_TYPE_BIN32:
+ if (!ctx->read(ctx, &u32temp, sizeof(uint32_t))) {
+ ctx->error = LENGTH_READING_ERROR;
+ return false;
+ }
+ *size = be32(u32temp);
+ return true;
+ case CMP_TYPE_EXT8:
+ if (!ctx->read(ctx, &u8temp, sizeof(uint8_t))) {
+ ctx->error = LENGTH_READING_ERROR;
+ return false;
+ }
+ *size = u8temp;
+ return true;
+ case CMP_TYPE_EXT16:
+ if (!ctx->read(ctx, &u16temp, sizeof(uint16_t))) {
+ ctx->error = LENGTH_READING_ERROR;
+ return false;
+ }
+ *size = be16(u16temp);
+ return true;
+ case CMP_TYPE_EXT32:
+ if (!ctx->read(ctx, &u32temp, sizeof(uint32_t))) {
+ ctx->error = LENGTH_READING_ERROR;
+ return false;
+ }
+ *size = be32(u32temp);
+ return true;
+ case CMP_TYPE_FLOAT:
+ *size = 4;
+ return true;
+ case CMP_TYPE_DOUBLE:
+ *size = 8;
+ return true;
+ case CMP_TYPE_UINT8:
+ *size = 1;
+ return true;
+ case CMP_TYPE_UINT16:
+ *size = 2;
+ return true;
+ case CMP_TYPE_UINT32:
+ *size = 4;
+ return true;
+ case CMP_TYPE_UINT64:
+ *size = 8;
+ return true;
+ case CMP_TYPE_SINT8:
+ *size = 1;
+ return true;
+ case CMP_TYPE_SINT16:
+ *size = 2;
+ return true;
+ case CMP_TYPE_SINT32:
+ *size = 4;
+ return true;
+ case CMP_TYPE_SINT64:
+ *size = 8;
+ return true;
+ case CMP_TYPE_FIXEXT1:
+ *size = 1;
+ return true;
+ case CMP_TYPE_FIXEXT2:
+ *size = 2;
+ return true;
+ case CMP_TYPE_FIXEXT4:
+ *size = 4;
+ return true;
+ case CMP_TYPE_FIXEXT8:
+ *size = 8;
+ return true;
+ case CMP_TYPE_FIXEXT16:
+ *size = 16;
+ return true;
+ case CMP_TYPE_STR8:
+ if (!ctx->read(ctx, &u8temp, sizeof(uint8_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ *size = u8temp;
+ return true;
+ case CMP_TYPE_STR16:
+ if (!ctx->read(ctx, &u16temp, sizeof(uint16_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ *size = be16(u16temp);
+ return true;
+ case CMP_TYPE_STR32:
+ if (!ctx->read(ctx, &u32temp, sizeof(uint32_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ *size = be32(u32temp);
+ return true;
+ case CMP_TYPE_ARRAY16:
+ if (!ctx->read(ctx, &u16temp, sizeof(uint16_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ *size = be16(u16temp);
+ return true;
+ case CMP_TYPE_ARRAY32:
+ if (!ctx->read(ctx, &u32temp, sizeof(uint32_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ *size = be32(u32temp);
+ return true;
+ case CMP_TYPE_MAP16:
+ if (!ctx->read(ctx, &u16temp, sizeof(uint16_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ *size = be16(u16temp);
+ return true;
+ case CMP_TYPE_MAP32:
+ if (!ctx->read(ctx, &u32temp, sizeof(uint32_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ *size = be32(u32temp);
+ return true;
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ *size = 0;
+ return true;
+ default:
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+}
+
+static bool read_obj_data(cmp_ctx_t *ctx, uint8_t type_marker,
+ cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ obj->as.u8 = type_marker;
+ return true;
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ obj->as.s8 = (int8_t)type_marker;
+ return true;
+ case CMP_TYPE_NIL:
+ obj->as.u8 = 0;
+ return true;
+ case CMP_TYPE_BOOLEAN:
+ switch (type_marker) {
+ case TRUE_MARKER:
+ obj->as.boolean = true;
+ return true;
+ case FALSE_MARKER:
+ obj->as.boolean = false;
+ return true;
+ default:
+ break;
+ }
+ ctx->error = INTERNAL_ERROR;
+ return false;
+ case CMP_TYPE_UINT8:
+ if (!ctx->read(ctx, &obj->as.u8, sizeof(uint8_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ return true;
+ case CMP_TYPE_UINT16:
+ if (!ctx->read(ctx, &obj->as.u16, sizeof(uint16_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ obj->as.u16 = be16(obj->as.u16);
+ return true;
+ case CMP_TYPE_UINT32:
+ if (!ctx->read(ctx, &obj->as.u32, sizeof(uint32_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ obj->as.u32 = be32(obj->as.u32);
+ return true;
+ case CMP_TYPE_UINT64:
+ if (!ctx->read(ctx, &obj->as.u64, sizeof(uint64_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ obj->as.u64 = be64(obj->as.u64);
+ return true;
+ case CMP_TYPE_SINT8:
+ if (!ctx->read(ctx, &obj->as.s8, sizeof(int8_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ return true;
+ case CMP_TYPE_SINT16:
+ if (!ctx->read(ctx, &obj->as.s16, sizeof(int16_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ obj->as.s16 = sbe16(obj->as.s16);
+ return true;
+ case CMP_TYPE_SINT32:
+ if (!ctx->read(ctx, &obj->as.s32, sizeof(int32_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ obj->as.s32 = sbe32(obj->as.s32);
+ return true;
+ case CMP_TYPE_SINT64:
+ if (!ctx->read(ctx, &obj->as.s64, sizeof(int64_t))) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ obj->as.s64 = sbe64(obj->as.s64);
+ return true;
+ case CMP_TYPE_FLOAT:
+ {
+#ifndef CMP_NO_FLOAT
+ char bytes[4];
+
+ if (!ctx->read(ctx, bytes, 4)) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ obj->as.flt = decode_befloat(bytes);
+ return true;
+#else /* CMP_NO_FLOAT */
+ ctx->error = DISABLED_FLOATING_POINT_ERROR;
+ return false;
+#endif /* CMP_NO_FLOAT */
+ }
+ case CMP_TYPE_DOUBLE:
+ {
+#ifndef CMP_NO_FLOAT
+ char bytes[8];
+
+ if (!ctx->read(ctx, bytes, 8)) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ obj->as.dbl = decode_bedouble(bytes);
+ return true;
+#else /* CMP_NO_FLOAT */
+ ctx->error = DISABLED_FLOATING_POINT_ERROR;
+ return false;
+#endif /* CMP_NO_FLOAT */
+ }
+ case CMP_TYPE_BIN8:
+ case CMP_TYPE_BIN16:
+ case CMP_TYPE_BIN32:
+ return read_type_size(ctx, type_marker, obj->type, &obj->as.bin_size);
+ case CMP_TYPE_FIXSTR:
+ case CMP_TYPE_STR8:
+ case CMP_TYPE_STR16:
+ case CMP_TYPE_STR32:
+ return read_type_size(ctx, type_marker, obj->type, &obj->as.str_size);
+ case CMP_TYPE_FIXARRAY:
+ case CMP_TYPE_ARRAY16:
+ case CMP_TYPE_ARRAY32:
+ return read_type_size(ctx, type_marker, obj->type, &obj->as.array_size);
+ case CMP_TYPE_FIXMAP:
+ case CMP_TYPE_MAP16:
+ case CMP_TYPE_MAP32:
+ return read_type_size(ctx, type_marker, obj->type, &obj->as.map_size);
+ case CMP_TYPE_FIXEXT1:
+ if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) {
+ ctx->error = EXT_TYPE_READING_ERROR;
+ return false;
+ }
+ obj->as.ext.size = 1;
+ return true;
+ case CMP_TYPE_FIXEXT2:
+ if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) {
+ ctx->error = EXT_TYPE_READING_ERROR;
+ return false;
+ }
+ obj->as.ext.size = 2;
+ return true;
+ case CMP_TYPE_FIXEXT4:
+ if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) {
+ ctx->error = EXT_TYPE_READING_ERROR;
+ return false;
+ }
+ obj->as.ext.size = 4;
+ return true;
+ case CMP_TYPE_FIXEXT8:
+ if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) {
+ ctx->error = EXT_TYPE_READING_ERROR;
+ return false;
+ }
+ obj->as.ext.size = 8;
+ return true;
+ case CMP_TYPE_FIXEXT16:
+ if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) {
+ ctx->error = EXT_TYPE_READING_ERROR;
+ return false;
+ }
+ obj->as.ext.size = 16;
+ return true;
+ case CMP_TYPE_EXT8:
+ if (!read_type_size(ctx, type_marker, obj->type, &obj->as.ext.size)) {
+ return false;
+ }
+ if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) {
+ ctx->error = EXT_TYPE_READING_ERROR;
+ return false;
+ }
+ return true;
+ case CMP_TYPE_EXT16:
+ if (!read_type_size(ctx, type_marker, obj->type, &obj->as.ext.size)) {
+ return false;
+ }
+ if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) {
+ ctx->error = EXT_TYPE_READING_ERROR;
+ return false;
+ }
+ return true;
+ case CMP_TYPE_EXT32:
+ if (!read_type_size(ctx, type_marker, obj->type, &obj->as.ext.size)) {
+ return false;
+ }
+ if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) {
+ ctx->error = EXT_TYPE_READING_ERROR;
+ return false;
+ }
+ return true;
+ default:
+ break;
+ }
+
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+}
+
+void cmp_init(cmp_ctx_t *ctx, void *buf, cmp_reader read,
+ cmp_skipper skip,
+ cmp_writer write) {
+ ctx->error = ERROR_NONE;
+ ctx->buf = buf;
+ ctx->read = read;
+ ctx->skip = skip;
+ ctx->write = write;
+}
+
+uint32_t cmp_version(void) {
+ return version;
+}
+
+uint32_t cmp_mp_version(void) {
+ return mp_version;
+}
+
+const char* cmp_strerror(cmp_ctx_t *ctx) {
+ if (ctx->error > ERROR_NONE && ctx->error < ERROR_MAX)
+ return cmp_error_messages[ctx->error];
+
+ return "";
+}
+
+bool cmp_write_pfix(cmp_ctx_t *ctx, uint8_t c) {
+ if (c <= 0x7F)
+ return write_fixed_value(ctx, c);
+
+ ctx->error = INPUT_VALUE_TOO_LARGE_ERROR;
+ return false;
+}
+
+bool cmp_write_nfix(cmp_ctx_t *ctx, int8_t c) {
+ if (c >= -32 && c <= -1)
+ return write_fixed_value(ctx, (uint8_t)c);
+
+ ctx->error = INPUT_VALUE_TOO_LARGE_ERROR;
+ return false;
+}
+
+bool cmp_write_sfix(cmp_ctx_t *ctx, int8_t c) {
+ if (c >= 0)
+ return cmp_write_pfix(ctx, (uint8_t)c);
+ if (c >= -32 && c <= -1)
+ return cmp_write_nfix(ctx, c);
+
+ ctx->error = INPUT_VALUE_TOO_LARGE_ERROR;
+ return false;
+}
+
+bool cmp_write_s8(cmp_ctx_t *ctx, int8_t c) {
+ if (!write_type_marker(ctx, S8_MARKER))
+ return false;
+
+ return ctx->write(ctx, &c, sizeof(int8_t));
+}
+
+bool cmp_write_s16(cmp_ctx_t *ctx, int16_t s) {
+ if (!write_type_marker(ctx, S16_MARKER))
+ return false;
+
+ s = sbe16(s);
+
+ return ctx->write(ctx, &s, sizeof(int16_t));
+}
+
+bool cmp_write_s32(cmp_ctx_t *ctx, int32_t i) {
+ if (!write_type_marker(ctx, S32_MARKER))
+ return false;
+
+ i = sbe32(i);
+
+ return ctx->write(ctx, &i, sizeof(int32_t));
+}
+
+bool cmp_write_s64(cmp_ctx_t *ctx, int64_t l) {
+ if (!write_type_marker(ctx, S64_MARKER))
+ return false;
+
+ l = sbe64(l);
+
+ return ctx->write(ctx, &l, sizeof(int64_t));
+}
+
+bool cmp_write_integer(cmp_ctx_t *ctx, int64_t d) {
+ if (d >= 0)
+ return cmp_write_uinteger(ctx, (uint64_t)d);
+ if (d >= -32)
+ return cmp_write_nfix(ctx, (int8_t)d);
+ if (d >= -128)
+ return cmp_write_s8(ctx, (int8_t)d);
+ if (d >= -32768)
+ return cmp_write_s16(ctx, (int16_t)d);
+ if (d >= (-2147483647 - 1))
+ return cmp_write_s32(ctx, (int32_t)d);
+
+ return cmp_write_s64(ctx, d);
+}
+
+bool cmp_write_ufix(cmp_ctx_t *ctx, uint8_t c) {
+ return cmp_write_pfix(ctx, c);
+}
+
+bool cmp_write_u8(cmp_ctx_t *ctx, uint8_t c) {
+ if (!write_type_marker(ctx, U8_MARKER))
+ return false;
+
+ return ctx->write(ctx, &c, sizeof(uint8_t));
+}
+
+bool cmp_write_u16(cmp_ctx_t *ctx, uint16_t s) {
+ if (!write_type_marker(ctx, U16_MARKER))
+ return false;
+
+ s = be16(s);
+
+ return ctx->write(ctx, &s, sizeof(uint16_t));
+}
+
+bool cmp_write_u32(cmp_ctx_t *ctx, uint32_t i) {
+ if (!write_type_marker(ctx, U32_MARKER))
+ return false;
+
+ i = be32(i);
+
+ return ctx->write(ctx, &i, sizeof(uint32_t));
+}
+
+bool cmp_write_u64(cmp_ctx_t *ctx, uint64_t l) {
+ if (!write_type_marker(ctx, U64_MARKER))
+ return false;
+
+ l = be64(l);
+
+ return ctx->write(ctx, &l, sizeof(uint64_t));
+}
+
+bool cmp_write_uinteger(cmp_ctx_t *ctx, uint64_t u) {
+ if (u <= 0x7F)
+ return cmp_write_pfix(ctx, (uint8_t)u);
+ if (u <= 0xFF)
+ return cmp_write_u8(ctx, (uint8_t)u);
+ if (u <= 0xFFFF)
+ return cmp_write_u16(ctx, (uint16_t)u);
+ if (u <= 0xFFFFFFFF)
+ return cmp_write_u32(ctx, (uint32_t)u);
+
+ return cmp_write_u64(ctx, u);
+}
+
+#ifndef CMP_NO_FLOAT
+bool cmp_write_float(cmp_ctx_t *ctx, float f) {
+ if (!write_type_marker(ctx, FLOAT_MARKER))
+ return false;
+
+ /*
+ * We may need to swap the float's bytes, but we can't just swap them inside
+ * the float because the swapped bytes may not constitute a valid float.
+ * Therefore, we have to create a buffer and swap the bytes there.
+ */
+ if (!is_bigendian()) {
+ char swapped[sizeof(float)];
+ char *fbuf = (char *)&f;
+ size_t i;
+
+ for (i = 0; i < sizeof(float); i++)
+ swapped[i] = fbuf[sizeof(float) - i - 1];
+
+ return ctx->write(ctx, swapped, sizeof(float));
+ }
+
+ return ctx->write(ctx, &f, sizeof(float));
+}
+
+bool cmp_write_double(cmp_ctx_t *ctx, double d) {
+ if (!write_type_marker(ctx, DOUBLE_MARKER))
+ return false;
+
+ /* Same deal for doubles */
+ if (!is_bigendian()) {
+ char swapped[sizeof(double)];
+ char *dbuf = (char *)&d;
+ size_t i;
+
+ for (i = 0; i < sizeof(double); i++)
+ swapped[i] = dbuf[sizeof(double) - i - 1];
+
+ return ctx->write(ctx, swapped, sizeof(double));
+ }
+
+ return ctx->write(ctx, &d, sizeof(double));
+}
+
+bool cmp_write_decimal(cmp_ctx_t *ctx, double d) {
+ float f = (float)d;
+ double df = (double)f;
+
+ if (df == d)
+ return cmp_write_float(ctx, f);
+ else
+ return cmp_write_double(ctx, d);
+}
+#endif /* CMP_NO_FLOAT */
+
+bool cmp_write_nil(cmp_ctx_t *ctx) {
+ return write_type_marker(ctx, NIL_MARKER);
+}
+
+bool cmp_write_true(cmp_ctx_t *ctx) {
+ return write_type_marker(ctx, TRUE_MARKER);
+}
+
+bool cmp_write_false(cmp_ctx_t *ctx) {
+ return write_type_marker(ctx, FALSE_MARKER);
+}
+
+bool cmp_write_bool(cmp_ctx_t *ctx, bool b) {
+ if (b)
+ return cmp_write_true(ctx);
+
+ return cmp_write_false(ctx);
+}
+
+bool cmp_write_u8_as_bool(cmp_ctx_t *ctx, uint8_t b) {
+ if (b)
+ return cmp_write_true(ctx);
+
+ return cmp_write_false(ctx);
+}
+
+bool cmp_write_fixstr_marker(cmp_ctx_t *ctx, uint8_t size) {
+ if (size <= FIXSTR_SIZE)
+ return write_fixed_value(ctx, FIXSTR_MARKER | size);
+
+ ctx->error = INPUT_VALUE_TOO_LARGE_ERROR;
+ return false;
+}
+
+bool cmp_write_fixstr(cmp_ctx_t *ctx, const char *data, uint8_t size) {
+ if (!cmp_write_fixstr_marker(ctx, size))
+ return false;
+
+ if (size == 0)
+ return true;
+
+ if (ctx->write(ctx, data, size))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_str8_marker(cmp_ctx_t *ctx, uint8_t size) {
+ if (!write_type_marker(ctx, STR8_MARKER))
+ return false;
+
+ if (ctx->write(ctx, &size, sizeof(uint8_t)))
+ return true;
+
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_str8(cmp_ctx_t *ctx, const char *data, uint8_t size) {
+ if (!cmp_write_str8_marker(ctx, size))
+ return false;
+
+ if (size == 0)
+ return true;
+
+ if (ctx->write(ctx, data, size))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_str16_marker(cmp_ctx_t *ctx, uint16_t size) {
+ if (!write_type_marker(ctx, STR16_MARKER))
+ return false;
+
+ size = be16(size);
+
+ if (ctx->write(ctx, &size, sizeof(uint16_t)))
+ return true;
+
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_str16(cmp_ctx_t *ctx, const char *data, uint16_t size) {
+ if (!cmp_write_str16_marker(ctx, size))
+ return false;
+
+ if (size == 0)
+ return true;
+
+ if (ctx->write(ctx, data, size))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_str32_marker(cmp_ctx_t *ctx, uint32_t size) {
+ if (!write_type_marker(ctx, STR32_MARKER))
+ return false;
+
+ size = be32(size);
+
+ if (ctx->write(ctx, &size, sizeof(uint32_t)))
+ return true;
+
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_str32(cmp_ctx_t *ctx, const char *data, uint32_t size) {
+ if (!cmp_write_str32_marker(ctx, size))
+ return false;
+
+ if (size == 0)
+ return true;
+
+ if (ctx->write(ctx, data, size))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_str_marker(cmp_ctx_t *ctx, uint32_t size) {
+ if (size <= FIXSTR_SIZE)
+ return cmp_write_fixstr_marker(ctx, (uint8_t)size);
+ if (size <= 0xFF)
+ return cmp_write_str8_marker(ctx, (uint8_t)size);
+ if (size <= 0xFFFF)
+ return cmp_write_str16_marker(ctx, (uint16_t)size);
+
+ return cmp_write_str32_marker(ctx, size);
+}
+
+bool cmp_write_str_marker_v4(cmp_ctx_t *ctx, uint32_t size) {
+ if (size <= FIXSTR_SIZE)
+ return cmp_write_fixstr_marker(ctx, (uint8_t)size);
+ if (size <= 0xFFFF)
+ return cmp_write_str16_marker(ctx, (uint16_t)size);
+
+ return cmp_write_str32_marker(ctx, size);
+}
+
+bool cmp_write_str(cmp_ctx_t *ctx, const char *data, uint32_t size) {
+ if (size <= FIXSTR_SIZE)
+ return cmp_write_fixstr(ctx, data, (uint8_t)size);
+ if (size <= 0xFF)
+ return cmp_write_str8(ctx, data, (uint8_t)size);
+ if (size <= 0xFFFF)
+ return cmp_write_str16(ctx, data, (uint16_t)size);
+
+ return cmp_write_str32(ctx, data, size);
+}
+
+bool cmp_write_str_v4(cmp_ctx_t *ctx, const char *data, uint32_t size) {
+ if (size <= FIXSTR_SIZE)
+ return cmp_write_fixstr(ctx, data, (uint8_t)size);
+ if (size <= 0xFFFF)
+ return cmp_write_str16(ctx, data, (uint16_t)size);
+
+ return cmp_write_str32(ctx, data, size);
+}
+
+bool cmp_write_bin8_marker(cmp_ctx_t *ctx, uint8_t size) {
+ if (!write_type_marker(ctx, BIN8_MARKER))
+ return false;
+
+ if (ctx->write(ctx, &size, sizeof(uint8_t)))
+ return true;
+
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_bin8(cmp_ctx_t *ctx, const void *data, uint8_t size) {
+ if (!cmp_write_bin8_marker(ctx, size))
+ return false;
+
+ if (size == 0)
+ return true;
+
+ if (ctx->write(ctx, data, size))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_bin16_marker(cmp_ctx_t *ctx, uint16_t size) {
+ if (!write_type_marker(ctx, BIN16_MARKER))
+ return false;
+
+ size = be16(size);
+
+ if (ctx->write(ctx, &size, sizeof(uint16_t)))
+ return true;
+
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_bin16(cmp_ctx_t *ctx, const void *data, uint16_t size) {
+ if (!cmp_write_bin16_marker(ctx, size))
+ return false;
+
+ if (size == 0)
+ return true;
+
+ if (ctx->write(ctx, data, size))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_bin32_marker(cmp_ctx_t *ctx, uint32_t size) {
+ if (!write_type_marker(ctx, BIN32_MARKER))
+ return false;
+
+ size = be32(size);
+
+ if (ctx->write(ctx, &size, sizeof(uint32_t)))
+ return true;
+
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_bin32(cmp_ctx_t *ctx, const void *data, uint32_t size) {
+ if (!cmp_write_bin32_marker(ctx, size))
+ return false;
+
+ if (size == 0)
+ return true;
+
+ if (ctx->write(ctx, data, size))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_bin_marker(cmp_ctx_t *ctx, uint32_t size) {
+ if (size <= 0xFF)
+ return cmp_write_bin8_marker(ctx, (uint8_t)size);
+ if (size <= 0xFFFF)
+ return cmp_write_bin16_marker(ctx, (uint16_t)size);
+
+ return cmp_write_bin32_marker(ctx, size);
+}
+
+bool cmp_write_bin(cmp_ctx_t *ctx, const void *data, uint32_t size) {
+ if (size <= 0xFF)
+ return cmp_write_bin8(ctx, data, (uint8_t)size);
+ if (size <= 0xFFFF)
+ return cmp_write_bin16(ctx, data, (uint16_t)size);
+
+ return cmp_write_bin32(ctx, data, size);
+}
+
+bool cmp_write_fixarray(cmp_ctx_t *ctx, uint8_t size) {
+ if (size <= FIXARRAY_SIZE)
+ return write_fixed_value(ctx, FIXARRAY_MARKER | size);
+
+ ctx->error = INPUT_VALUE_TOO_LARGE_ERROR;
+ return false;
+}
+
+bool cmp_write_array16(cmp_ctx_t *ctx, uint16_t size) {
+ if (!write_type_marker(ctx, ARRAY16_MARKER))
+ return false;
+
+ size = be16(size);
+
+ if (ctx->write(ctx, &size, sizeof(uint16_t)))
+ return true;
+
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_array32(cmp_ctx_t *ctx, uint32_t size) {
+ if (!write_type_marker(ctx, ARRAY32_MARKER))
+ return false;
+
+ size = be32(size);
+
+ if (ctx->write(ctx, &size, sizeof(uint32_t)))
+ return true;
+
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_array(cmp_ctx_t *ctx, uint32_t size) {
+ if (size <= FIXARRAY_SIZE)
+ return cmp_write_fixarray(ctx, (uint8_t)size);
+ if (size <= 0xFFFF)
+ return cmp_write_array16(ctx, (uint16_t)size);
+
+ return cmp_write_array32(ctx, size);
+}
+
+bool cmp_write_fixmap(cmp_ctx_t *ctx, uint8_t size) {
+ if (size <= FIXMAP_SIZE)
+ return write_fixed_value(ctx, FIXMAP_MARKER | size);
+
+ ctx->error = INPUT_VALUE_TOO_LARGE_ERROR;
+ return false;
+}
+
+bool cmp_write_map16(cmp_ctx_t *ctx, uint16_t size) {
+ if (!write_type_marker(ctx, MAP16_MARKER))
+ return false;
+
+ size = be16(size);
+
+ if (ctx->write(ctx, &size, sizeof(uint16_t)))
+ return true;
+
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_map32(cmp_ctx_t *ctx, uint32_t size) {
+ if (!write_type_marker(ctx, MAP32_MARKER))
+ return false;
+
+ size = be32(size);
+
+ if (ctx->write(ctx, &size, sizeof(uint32_t)))
+ return true;
+
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_map(cmp_ctx_t *ctx, uint32_t size) {
+ if (size <= FIXMAP_SIZE)
+ return cmp_write_fixmap(ctx, (uint8_t)size);
+ if (size <= 0xFFFF)
+ return cmp_write_map16(ctx, (uint16_t)size);
+
+ return cmp_write_map32(ctx, size);
+}
+
+bool cmp_write_fixext1_marker(cmp_ctx_t *ctx, int8_t type) {
+ if (!write_type_marker(ctx, FIXEXT1_MARKER))
+ return false;
+
+ if (ctx->write(ctx, &type, sizeof(int8_t)))
+ return true;
+
+ ctx->error = EXT_TYPE_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_fixext1(cmp_ctx_t *ctx, int8_t type, const void *data) {
+ if (!cmp_write_fixext1_marker(ctx, type))
+ return false;
+
+ if (ctx->write(ctx, data, 1))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_fixext2_marker(cmp_ctx_t *ctx, int8_t type) {
+ if (!write_type_marker(ctx, FIXEXT2_MARKER))
+ return false;
+
+ if (ctx->write(ctx, &type, sizeof(int8_t)))
+ return true;
+
+ ctx->error = EXT_TYPE_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_fixext2(cmp_ctx_t *ctx, int8_t type, const void *data) {
+ if (!cmp_write_fixext2_marker(ctx, type))
+ return false;
+
+ if (ctx->write(ctx, data, 2))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_fixext4_marker(cmp_ctx_t *ctx, int8_t type) {
+ if (!write_type_marker(ctx, FIXEXT4_MARKER))
+ return false;
+
+ if (ctx->write(ctx, &type, sizeof(int8_t)))
+ return true;
+
+ ctx->error = EXT_TYPE_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_fixext4(cmp_ctx_t *ctx, int8_t type, const void *data) {
+ if (!cmp_write_fixext4_marker(ctx, type))
+ return false;
+
+ if (ctx->write(ctx, data, 4))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_fixext8_marker(cmp_ctx_t *ctx, int8_t type) {
+ if (!write_type_marker(ctx, FIXEXT8_MARKER))
+ return false;
+
+ if (ctx->write(ctx, &type, sizeof(int8_t)))
+ return true;
+
+ ctx->error = EXT_TYPE_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_fixext8(cmp_ctx_t *ctx, int8_t type, const void *data) {
+ if (!cmp_write_fixext8_marker(ctx, type))
+ return false;
+
+ if (ctx->write(ctx, data, 8))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_fixext16_marker(cmp_ctx_t *ctx, int8_t type) {
+ if (!write_type_marker(ctx, FIXEXT16_MARKER))
+ return false;
+
+ if (ctx->write(ctx, &type, sizeof(int8_t)))
+ return true;
+
+ ctx->error = EXT_TYPE_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_fixext16(cmp_ctx_t *ctx, int8_t type, const void *data) {
+ if (!cmp_write_fixext16_marker(ctx, type))
+ return false;
+
+ if (ctx->write(ctx, data, 16))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_ext8_marker(cmp_ctx_t *ctx, int8_t type, uint8_t size) {
+ if (!write_type_marker(ctx, EXT8_MARKER))
+ return false;
+
+ if (!ctx->write(ctx, &size, sizeof(uint8_t))) {
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+ }
+
+ if (ctx->write(ctx, &type, sizeof(int8_t)))
+ return true;
+
+ ctx->error = EXT_TYPE_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_ext8(cmp_ctx_t *ctx, int8_t type, uint8_t size, const void *data) {
+ if (!cmp_write_ext8_marker(ctx, type, size))
+ return false;
+
+ if (ctx->write(ctx, data, size))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_ext16_marker(cmp_ctx_t *ctx, int8_t type, uint16_t size) {
+ if (!write_type_marker(ctx, EXT16_MARKER))
+ return false;
+
+ size = be16(size);
+
+ if (!ctx->write(ctx, &size, sizeof(uint16_t))) {
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+ }
+
+ if (ctx->write(ctx, &type, sizeof(int8_t)))
+ return true;
+
+ ctx->error = EXT_TYPE_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_ext16(cmp_ctx_t *ctx, int8_t type, uint16_t size, const void *data) {
+ if (!cmp_write_ext16_marker(ctx, type, size))
+ return false;
+
+ if (ctx->write(ctx, data, size))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_ext32_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size) {
+ if (!write_type_marker(ctx, EXT32_MARKER))
+ return false;
+
+ size = be32(size);
+
+ if (!ctx->write(ctx, &size, sizeof(uint32_t))) {
+ ctx->error = LENGTH_WRITING_ERROR;
+ return false;
+ }
+
+ if (ctx->write(ctx, &type, sizeof(int8_t)))
+ return true;
+
+ ctx->error = EXT_TYPE_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_ext32(cmp_ctx_t *ctx, int8_t type, uint32_t size, const void *data) {
+ if (!cmp_write_ext32_marker(ctx, type, size))
+ return false;
+
+ if (ctx->write(ctx, data, size))
+ return true;
+
+ ctx->error = DATA_WRITING_ERROR;
+ return false;
+}
+
+bool cmp_write_ext_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size) {
+ if (size == 1)
+ return cmp_write_fixext1_marker(ctx, type);
+ if (size == 2)
+ return cmp_write_fixext2_marker(ctx, type);
+ if (size == 4)
+ return cmp_write_fixext4_marker(ctx, type);
+ if (size == 8)
+ return cmp_write_fixext8_marker(ctx, type);
+ if (size == 16)
+ return cmp_write_fixext16_marker(ctx, type);
+ if (size <= 0xFF)
+ return cmp_write_ext8_marker(ctx, type, (uint8_t)size);
+ if (size <= 0xFFFF)
+ return cmp_write_ext16_marker(ctx, type, (uint16_t)size);
+
+ return cmp_write_ext32_marker(ctx, type, size);
+}
+
+bool cmp_write_ext(cmp_ctx_t *ctx, int8_t type, uint32_t size, const void *data) {
+ if (size == 1)
+ return cmp_write_fixext1(ctx, type, data);
+ if (size == 2)
+ return cmp_write_fixext2(ctx, type, data);
+ if (size == 4)
+ return cmp_write_fixext4(ctx, type, data);
+ if (size == 8)
+ return cmp_write_fixext8(ctx, type, data);
+ if (size == 16)
+ return cmp_write_fixext16(ctx, type, data);
+ if (size <= 0xFF)
+ return cmp_write_ext8(ctx, type, (uint8_t)size, data);
+ if (size <= 0xFFFF)
+ return cmp_write_ext16(ctx, type, (uint16_t)size, data);
+
+ return cmp_write_ext32(ctx, type, size, data);
+}
+
+bool cmp_write_object(cmp_ctx_t *ctx, const cmp_object_t *obj) {
+ switch(obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ return cmp_write_pfix(ctx, obj->as.u8);
+ case CMP_TYPE_FIXMAP:
+ return cmp_write_fixmap(ctx, (uint8_t)obj->as.map_size);
+ case CMP_TYPE_FIXARRAY:
+ return cmp_write_fixarray(ctx, (uint8_t)obj->as.array_size);
+ case CMP_TYPE_FIXSTR:
+ return cmp_write_fixstr_marker(ctx, (uint8_t)obj->as.str_size);
+ case CMP_TYPE_NIL:
+ return cmp_write_nil(ctx);
+ case CMP_TYPE_BOOLEAN:
+ if (obj->as.boolean)
+ return cmp_write_true(ctx);
+ return cmp_write_false(ctx);
+ case CMP_TYPE_BIN8:
+ return cmp_write_bin8_marker(ctx, (uint8_t)obj->as.bin_size);
+ case CMP_TYPE_BIN16:
+ return cmp_write_bin16_marker(ctx, (uint16_t)obj->as.bin_size);
+ case CMP_TYPE_BIN32:
+ return cmp_write_bin32_marker(ctx, obj->as.bin_size);
+ case CMP_TYPE_EXT8:
+ return cmp_write_ext8_marker(
+ ctx, obj->as.ext.type, (uint8_t)obj->as.ext.size
+ );
+ case CMP_TYPE_EXT16:
+ return cmp_write_ext16_marker(
+ ctx, obj->as.ext.type, (uint16_t)obj->as.ext.size
+ );
+ case CMP_TYPE_EXT32:
+ return cmp_write_ext32_marker(ctx, obj->as.ext.type, obj->as.ext.size);
+ case CMP_TYPE_FLOAT:
+#ifndef CMP_NO_FLOAT
+ return cmp_write_float(ctx, obj->as.flt);
+#else /* CMP_NO_FLOAT */
+ ctx->error = DISABLED_FLOATING_POINT_ERROR;
+ return false;
+#endif /* CMP_NO_FLOAT */
+ case CMP_TYPE_DOUBLE:
+#ifndef CMP_NO_FLOAT
+ return cmp_write_double(ctx, obj->as.dbl);
+#else /* CMP_NO_FLOAT */
+ ctx->error = DISABLED_FLOATING_POINT_ERROR;
+ return false;
+#endif
+ case CMP_TYPE_UINT8:
+ return cmp_write_u8(ctx, obj->as.u8);
+ case CMP_TYPE_UINT16:
+ return cmp_write_u16(ctx, obj->as.u16);
+ case CMP_TYPE_UINT32:
+ return cmp_write_u32(ctx, obj->as.u32);
+ case CMP_TYPE_UINT64:
+ return cmp_write_u64(ctx, obj->as.u64);
+ case CMP_TYPE_SINT8:
+ return cmp_write_s8(ctx, obj->as.s8);
+ case CMP_TYPE_SINT16:
+ return cmp_write_s16(ctx, obj->as.s16);
+ case CMP_TYPE_SINT32:
+ return cmp_write_s32(ctx, obj->as.s32);
+ case CMP_TYPE_SINT64:
+ return cmp_write_s64(ctx, obj->as.s64);
+ case CMP_TYPE_FIXEXT1:
+ return cmp_write_fixext1_marker(ctx, obj->as.ext.type);
+ case CMP_TYPE_FIXEXT2:
+ return cmp_write_fixext2_marker(ctx, obj->as.ext.type);
+ case CMP_TYPE_FIXEXT4:
+ return cmp_write_fixext4_marker(ctx, obj->as.ext.type);
+ case CMP_TYPE_FIXEXT8:
+ return cmp_write_fixext8_marker(ctx, obj->as.ext.type);
+ case CMP_TYPE_FIXEXT16:
+ return cmp_write_fixext16_marker(ctx, obj->as.ext.type);
+ case CMP_TYPE_STR8:
+ return cmp_write_str8_marker(ctx, (uint8_t)obj->as.str_size);
+ case CMP_TYPE_STR16:
+ return cmp_write_str16_marker(ctx, (uint16_t)obj->as.str_size);
+ case CMP_TYPE_STR32:
+ return cmp_write_str32_marker(ctx, obj->as.str_size);
+ case CMP_TYPE_ARRAY16:
+ return cmp_write_array16(ctx, (uint16_t)obj->as.array_size);
+ case CMP_TYPE_ARRAY32:
+ return cmp_write_array32(ctx, obj->as.array_size);
+ case CMP_TYPE_MAP16:
+ return cmp_write_map16(ctx, (uint16_t)obj->as.map_size);
+ case CMP_TYPE_MAP32:
+ return cmp_write_map32(ctx, obj->as.map_size);
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ return cmp_write_nfix(ctx, obj->as.s8);
+ default:
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+}
+
+bool cmp_write_object_v4(cmp_ctx_t *ctx, const cmp_object_t *obj) {
+ switch(obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ return cmp_write_pfix(ctx, obj->as.u8);
+ case CMP_TYPE_FIXMAP:
+ return cmp_write_fixmap(ctx, (uint8_t)obj->as.map_size);
+ case CMP_TYPE_FIXARRAY:
+ return cmp_write_fixarray(ctx, (uint8_t)obj->as.array_size);
+ case CMP_TYPE_FIXSTR:
+ return cmp_write_fixstr_marker(ctx, (uint8_t)obj->as.str_size);
+ case CMP_TYPE_NIL:
+ return cmp_write_nil(ctx);
+ case CMP_TYPE_BOOLEAN:
+ if (obj->as.boolean)
+ return cmp_write_true(ctx);
+ return cmp_write_false(ctx);
+ case CMP_TYPE_EXT8:
+ return cmp_write_ext8_marker(ctx, obj->as.ext.type, (uint8_t)obj->as.ext.size);
+ case CMP_TYPE_EXT16:
+ return cmp_write_ext16_marker(
+ ctx, obj->as.ext.type, (uint16_t)obj->as.ext.size
+ );
+ case CMP_TYPE_EXT32:
+ return cmp_write_ext32_marker(ctx, obj->as.ext.type, obj->as.ext.size);
+ case CMP_TYPE_FLOAT:
+#ifndef CMP_NO_FLOAT
+ return cmp_write_float(ctx, obj->as.flt);
+#else /* CMP_NO_FLOAT */
+ ctx->error = DISABLED_FLOATING_POINT_ERROR;
+ return false;
+#endif
+ case CMP_TYPE_DOUBLE:
+#ifndef CMP_NO_FLOAT
+ return cmp_write_double(ctx, obj->as.dbl);
+#else
+ ctx->error = DISABLED_FLOATING_POINT_ERROR;
+ return false;
+#endif
+ case CMP_TYPE_UINT8:
+ return cmp_write_u8(ctx, obj->as.u8);
+ case CMP_TYPE_UINT16:
+ return cmp_write_u16(ctx, obj->as.u16);
+ case CMP_TYPE_UINT32:
+ return cmp_write_u32(ctx, obj->as.u32);
+ case CMP_TYPE_UINT64:
+ return cmp_write_u64(ctx, obj->as.u64);
+ case CMP_TYPE_SINT8:
+ return cmp_write_s8(ctx, obj->as.s8);
+ case CMP_TYPE_SINT16:
+ return cmp_write_s16(ctx, obj->as.s16);
+ case CMP_TYPE_SINT32:
+ return cmp_write_s32(ctx, obj->as.s32);
+ case CMP_TYPE_SINT64:
+ return cmp_write_s64(ctx, obj->as.s64);
+ case CMP_TYPE_FIXEXT1:
+ return cmp_write_fixext1_marker(ctx, obj->as.ext.type);
+ case CMP_TYPE_FIXEXT2:
+ return cmp_write_fixext2_marker(ctx, obj->as.ext.type);
+ case CMP_TYPE_FIXEXT4:
+ return cmp_write_fixext4_marker(ctx, obj->as.ext.type);
+ case CMP_TYPE_FIXEXT8:
+ return cmp_write_fixext8_marker(ctx, obj->as.ext.type);
+ case CMP_TYPE_FIXEXT16:
+ return cmp_write_fixext16_marker(ctx, obj->as.ext.type);
+ case CMP_TYPE_STR16:
+ return cmp_write_str16_marker(ctx, (uint16_t)obj->as.str_size);
+ case CMP_TYPE_STR32:
+ return cmp_write_str32_marker(ctx, obj->as.str_size);
+ case CMP_TYPE_ARRAY16:
+ return cmp_write_array16(ctx, (uint16_t)obj->as.array_size);
+ case CMP_TYPE_ARRAY32:
+ return cmp_write_array32(ctx, obj->as.array_size);
+ case CMP_TYPE_MAP16:
+ return cmp_write_map16(ctx, (uint16_t)obj->as.map_size);
+ case CMP_TYPE_MAP32:
+ return cmp_write_map32(ctx, obj->as.map_size);
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ return cmp_write_nfix(ctx, obj->as.s8);
+ default:
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+}
+
+bool cmp_read_pfix(cmp_ctx_t *ctx, uint8_t *c) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_POSITIVE_FIXNUM) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *c = obj.as.u8;
+ return true;
+}
+
+bool cmp_read_nfix(cmp_ctx_t *ctx, int8_t *c) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_NEGATIVE_FIXNUM) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *c = obj.as.s8;
+ return true;
+}
+
+bool cmp_read_sfix(cmp_ctx_t *ctx, int8_t *c) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ *c = obj.as.s8;
+ return true;
+ default:
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+}
+
+bool cmp_read_s8(cmp_ctx_t *ctx, int8_t *c) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_SINT8) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *c = obj.as.s8;
+ return true;
+}
+
+bool cmp_read_s16(cmp_ctx_t *ctx, int16_t *s) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_SINT16) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *s = obj.as.s16;
+ return true;
+}
+
+bool cmp_read_s32(cmp_ctx_t *ctx, int32_t *i) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_SINT32) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *i = obj.as.s32;
+ return true;
+}
+
+bool cmp_read_s64(cmp_ctx_t *ctx, int64_t *l) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_SINT64) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *l = obj.as.s64;
+ return true;
+}
+
+bool cmp_read_char(cmp_ctx_t *ctx, int8_t *c) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ *c = obj.as.s8;
+ return true;
+ case CMP_TYPE_UINT8:
+ if (obj.as.u8 <= 127) {
+ *c = (int8_t)obj.as.u8;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+}
+
+bool cmp_read_short(cmp_ctx_t *ctx, int16_t *s) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ *s = obj.as.s8;
+ return true;
+ case CMP_TYPE_UINT8:
+ *s = obj.as.u8;
+ return true;
+ case CMP_TYPE_SINT16:
+ *s = obj.as.s16;
+ return true;
+ case CMP_TYPE_UINT16:
+ if (obj.as.u16 <= 32767) {
+ *s = (int16_t)obj.as.u16;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+}
+
+bool cmp_read_int(cmp_ctx_t *ctx, int32_t *i) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ *i = obj.as.s8;
+ return true;
+ case CMP_TYPE_UINT8:
+ *i = obj.as.u8;
+ return true;
+ case CMP_TYPE_SINT16:
+ *i = obj.as.s16;
+ return true;
+ case CMP_TYPE_UINT16:
+ *i = obj.as.u16;
+ return true;
+ case CMP_TYPE_SINT32:
+ *i = obj.as.s32;
+ return true;
+ case CMP_TYPE_UINT32:
+ if (obj.as.u32 <= 2147483647) {
+ *i = (int32_t)obj.as.u32;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+}
+
+bool cmp_read_long(cmp_ctx_t *ctx, int64_t *d) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ *d = obj.as.s8;
+ return true;
+ case CMP_TYPE_UINT8:
+ *d = obj.as.u8;
+ return true;
+ case CMP_TYPE_SINT16:
+ *d = obj.as.s16;
+ return true;
+ case CMP_TYPE_UINT16:
+ *d = obj.as.u16;
+ return true;
+ case CMP_TYPE_SINT32:
+ *d = obj.as.s32;
+ return true;
+ case CMP_TYPE_UINT32:
+ *d = obj.as.u32;
+ return true;
+ case CMP_TYPE_SINT64:
+ *d = obj.as.s64;
+ return true;
+ case CMP_TYPE_UINT64:
+ if (obj.as.u64 <= 9223372036854775807) {
+ *d = (int64_t)obj.as.u64;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+}
+
+bool cmp_read_integer(cmp_ctx_t *ctx, int64_t *d) {
+ return cmp_read_long(ctx, d);
+}
+
+bool cmp_read_ufix(cmp_ctx_t *ctx, uint8_t *c) {
+ return cmp_read_pfix(ctx, c);
+}
+
+bool cmp_read_u8(cmp_ctx_t *ctx, uint8_t *c) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_UINT8) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *c = obj.as.u8;
+ return true;
+}
+
+bool cmp_read_u16(cmp_ctx_t *ctx, uint16_t *s) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_UINT16) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *s = obj.as.u16;
+ return true;
+}
+
+bool cmp_read_u32(cmp_ctx_t *ctx, uint32_t *i) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_UINT32) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *i = obj.as.u32;
+ return true;
+}
+
+bool cmp_read_u64(cmp_ctx_t *ctx, uint64_t *l) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_UINT64) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *l = obj.as.u64;
+ return true;
+}
+
+bool cmp_read_uchar(cmp_ctx_t *ctx, uint8_t *c) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_UINT8:
+ *c = obj.as.u8;
+ return true;
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ if (obj.as.s8 >= 0) {
+ *c = (uint8_t)obj.as.s8;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+}
+
+bool cmp_read_ushort(cmp_ctx_t *ctx, uint16_t *s) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_UINT8:
+ *s = obj.as.u8;
+ return true;
+ case CMP_TYPE_UINT16:
+ *s = obj.as.u16;
+ return true;
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ if (obj.as.s8 >= 0) {
+ *s = (uint8_t)obj.as.s8;
+ return true;
+ }
+ break;
+ case CMP_TYPE_SINT16:
+ if (obj.as.s16 >= 0) {
+ *s = (uint16_t)obj.as.s16;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+}
+
+bool cmp_read_uint(cmp_ctx_t *ctx, uint32_t *i) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_UINT8:
+ *i = obj.as.u8;
+ return true;
+ case CMP_TYPE_UINT16:
+ *i = obj.as.u16;
+ return true;
+ case CMP_TYPE_UINT32:
+ *i = obj.as.u32;
+ return true;
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ if (obj.as.s8 >= 0) {
+ *i = (uint8_t)obj.as.s8;
+ return true;
+ }
+ break;
+ case CMP_TYPE_SINT16:
+ if (obj.as.s16 >= 0) {
+ *i = (uint16_t)obj.as.s16;
+ return true;
+ }
+ break;
+ case CMP_TYPE_SINT32:
+ if (obj.as.s32 >= 0) {
+ *i = (uint32_t)obj.as.s32;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+}
+
+bool cmp_read_ulong(cmp_ctx_t *ctx, uint64_t *u) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_UINT8:
+ *u = obj.as.u8;
+ return true;
+ case CMP_TYPE_UINT16:
+ *u = obj.as.u16;
+ return true;
+ case CMP_TYPE_UINT32:
+ *u = obj.as.u32;
+ return true;
+ case CMP_TYPE_UINT64:
+ *u = obj.as.u64;
+ return true;
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ if (obj.as.s8 >= 0) {
+ *u = (uint8_t)obj.as.s8;
+ return true;
+ }
+ break;
+ case CMP_TYPE_SINT16:
+ if (obj.as.s16 >= 0) {
+ *u = (uint16_t)obj.as.s16;
+ return true;
+ }
+ break;
+ case CMP_TYPE_SINT32:
+ if (obj.as.s32 >= 0) {
+ *u = (uint32_t)obj.as.s32;
+ return true;
+ }
+ break;
+ case CMP_TYPE_SINT64:
+ if (obj.as.s64 >= 0) {
+ *u = (uint64_t)obj.as.s64;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+}
+
+bool cmp_read_uinteger(cmp_ctx_t *ctx, uint64_t *u) {
+ return cmp_read_ulong(ctx, u);
+}
+
+#ifndef CMP_NO_FLOAT
+bool cmp_read_float(cmp_ctx_t *ctx, float *f) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_FLOAT) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *f = obj.as.flt;
+
+ return true;
+}
+
+bool cmp_read_double(cmp_ctx_t *ctx, double *d) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_DOUBLE) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *d = obj.as.dbl;
+
+ return true;
+}
+
+bool cmp_read_decimal(cmp_ctx_t *ctx, double *d) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_FLOAT:
+ *d = (double)obj.as.flt;
+ return true;
+ case CMP_TYPE_DOUBLE:
+ *d = obj.as.dbl;
+ return true;
+ default:
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+}
+#endif /* CMP_NO_FLOAT */
+
+bool cmp_read_nil(cmp_ctx_t *ctx) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type == CMP_TYPE_NIL)
+ return true;
+
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+}
+
+bool cmp_read_bool(cmp_ctx_t *ctx, bool *b) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_BOOLEAN) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ if (obj.as.boolean)
+ *b = true;
+ else
+ *b = false;
+
+ return true;
+}
+
+bool cmp_read_bool_as_u8(cmp_ctx_t *ctx, uint8_t *b) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_BOOLEAN) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ if (obj.as.boolean)
+ *b = 1;
+ else
+ *b = 0;
+
+ return true;
+}
+
+bool cmp_read_str_size(cmp_ctx_t *ctx, uint32_t *size) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_FIXSTR:
+ case CMP_TYPE_STR8:
+ case CMP_TYPE_STR16:
+ case CMP_TYPE_STR32:
+ *size = obj.as.str_size;
+ return true;
+ default:
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+}
+
+bool cmp_read_str(cmp_ctx_t *ctx, char *data, uint32_t *size) {
+ uint32_t str_size = 0;
+
+ if (!cmp_read_str_size(ctx, &str_size))
+ return false;
+
+ if (str_size >= *size) {
+ *size = str_size;
+ ctx->error = STR_DATA_LENGTH_TOO_LONG_ERROR;
+ return false;
+ }
+
+ if (!ctx->read(ctx, data, str_size)) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+
+ data[str_size] = 0;
+
+ *size = str_size;
+ return true;
+}
+
+bool cmp_read_bin_size(cmp_ctx_t *ctx, uint32_t *size) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_BIN8:
+ case CMP_TYPE_BIN16:
+ case CMP_TYPE_BIN32:
+ *size = obj.as.bin_size;
+ return true;
+ default:
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+}
+
+bool cmp_read_bin(cmp_ctx_t *ctx, void *data, uint32_t *size) {
+ uint32_t bin_size = 0;
+
+ if (!cmp_read_bin_size(ctx, &bin_size))
+ return false;
+
+ if (bin_size > *size) {
+ ctx->error = BIN_DATA_LENGTH_TOO_LONG_ERROR;
+ return false;
+ }
+
+ if (!ctx->read(ctx, data, bin_size)) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+
+ *size = bin_size;
+ return true;
+}
+
+bool cmp_read_array(cmp_ctx_t *ctx, uint32_t *size) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_FIXARRAY:
+ case CMP_TYPE_ARRAY16:
+ case CMP_TYPE_ARRAY32:
+ *size = obj.as.array_size;
+ return true;
+ default:
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+}
+
+bool cmp_read_map(cmp_ctx_t *ctx, uint32_t *size) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_FIXMAP:
+ case CMP_TYPE_MAP16:
+ case CMP_TYPE_MAP32:
+ *size = obj.as.map_size;
+ return true;
+ default:
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+}
+
+bool cmp_read_fixext1_marker(cmp_ctx_t *ctx, int8_t *type) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_FIXEXT1) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *type = obj.as.ext.type;
+ return true;
+}
+
+bool cmp_read_fixext1(cmp_ctx_t *ctx, int8_t *type, void *data) {
+ if (!cmp_read_fixext1_marker(ctx, type))
+ return false;
+
+ if (ctx->read(ctx, data, 1))
+ return true;
+
+ ctx->error = DATA_READING_ERROR;
+ return false;
+}
+
+bool cmp_read_fixext2_marker(cmp_ctx_t *ctx, int8_t *type) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_FIXEXT2) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *type = obj.as.ext.type;
+ return true;
+}
+
+bool cmp_read_fixext2(cmp_ctx_t *ctx, int8_t *type, void *data) {
+ if (!cmp_read_fixext2_marker(ctx, type))
+ return false;
+
+ if (ctx->read(ctx, data, 2))
+ return true;
+
+ ctx->error = DATA_READING_ERROR;
+ return false;
+}
+
+bool cmp_read_fixext4_marker(cmp_ctx_t *ctx, int8_t *type) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_FIXEXT4) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *type = obj.as.ext.type;
+ return true;
+}
+
+bool cmp_read_fixext4(cmp_ctx_t *ctx, int8_t *type, void *data) {
+ if (!cmp_read_fixext4_marker(ctx, type))
+ return false;
+
+ if (ctx->read(ctx, data, 4))
+ return true;
+
+ ctx->error = DATA_READING_ERROR;
+ return false;
+}
+
+bool cmp_read_fixext8_marker(cmp_ctx_t *ctx, int8_t *type) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_FIXEXT8) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *type = obj.as.ext.type;
+ return true;
+}
+
+bool cmp_read_fixext8(cmp_ctx_t *ctx, int8_t *type, void *data) {
+ if (!cmp_read_fixext8_marker(ctx, type))
+ return false;
+
+ if (ctx->read(ctx, data, 8))
+ return true;
+
+ ctx->error = DATA_READING_ERROR;
+ return false;
+}
+
+bool cmp_read_fixext16_marker(cmp_ctx_t *ctx, int8_t *type) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_FIXEXT16) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *type = obj.as.ext.type;
+ return true;
+}
+
+bool cmp_read_fixext16(cmp_ctx_t *ctx, int8_t *type, void *data) {
+ if (!cmp_read_fixext16_marker(ctx, type))
+ return false;
+
+ if (ctx->read(ctx, data, 16))
+ return true;
+
+ ctx->error = DATA_READING_ERROR;
+ return false;
+}
+
+bool cmp_read_ext8_marker(cmp_ctx_t *ctx, int8_t *type, uint8_t *size) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_EXT8) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *type = obj.as.ext.type;
+ *size = (uint8_t)obj.as.ext.size;
+
+ return true;
+}
+
+bool cmp_read_ext8(cmp_ctx_t *ctx, int8_t *type, uint8_t *size, void *data) {
+ if (!cmp_read_ext8_marker(ctx, type, size))
+ return false;
+
+ if (ctx->read(ctx, data, *size))
+ return true;
+
+ ctx->error = DATA_READING_ERROR;
+ return false;
+}
+
+bool cmp_read_ext16_marker(cmp_ctx_t *ctx, int8_t *type, uint16_t *size) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_EXT16) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *type = obj.as.ext.type;
+ *size = (uint16_t)obj.as.ext.size;
+
+ return true;
+}
+
+bool cmp_read_ext16(cmp_ctx_t *ctx, int8_t *type, uint16_t *size, void *data) {
+ if (!cmp_read_ext16_marker(ctx, type, size))
+ return false;
+
+ if (ctx->read(ctx, data, *size))
+ return true;
+
+ ctx->error = DATA_READING_ERROR;
+ return false;
+}
+
+bool cmp_read_ext32_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ if (obj.type != CMP_TYPE_EXT32) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ *type = obj.as.ext.type;
+ *size = obj.as.ext.size;
+
+ return true;
+}
+
+bool cmp_read_ext32(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data) {
+ if (!cmp_read_ext32_marker(ctx, type, size))
+ return false;
+
+ if (ctx->read(ctx, data, *size))
+ return true;
+
+ ctx->error = DATA_READING_ERROR;
+ return false;
+}
+
+bool cmp_read_ext_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size) {
+ cmp_object_t obj;
+
+ if (!cmp_read_object(ctx, &obj))
+ return false;
+
+ switch (obj.type) {
+ case CMP_TYPE_FIXEXT1:
+ case CMP_TYPE_FIXEXT2:
+ case CMP_TYPE_FIXEXT4:
+ case CMP_TYPE_FIXEXT8:
+ case CMP_TYPE_FIXEXT16:
+ case CMP_TYPE_EXT8:
+ case CMP_TYPE_EXT16:
+ case CMP_TYPE_EXT32:
+ *type = obj.as.ext.type;
+ *size = obj.as.ext.size;
+ return true;
+ default:
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+}
+
+bool cmp_read_ext(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data) {
+ if (!cmp_read_ext_marker(ctx, type, size))
+ return false;
+
+ if (ctx->read(ctx, data, *size))
+ return true;
+
+ ctx->error = DATA_READING_ERROR;
+ return false;
+}
+
+bool cmp_read_object(cmp_ctx_t *ctx, cmp_object_t *obj) {
+ uint8_t type_marker = 0;
+
+ if (!read_type_marker(ctx, &type_marker))
+ return false;
+
+ if (!type_marker_to_cmp_type(type_marker, &obj->type)) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ return read_obj_data(ctx, type_marker, obj);
+}
+
+bool cmp_skip_object(cmp_ctx_t *ctx, cmp_object_t *obj) {
+ uint8_t type_marker = 0;
+ uint8_t cmp_type;
+ uint32_t size = 0;
+
+ if (!read_type_marker(ctx, &type_marker)) {
+ return false;
+ }
+
+ if (!type_marker_to_cmp_type(type_marker, &cmp_type)) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ switch (cmp_type) {
+ case CMP_TYPE_FIXARRAY:
+ case CMP_TYPE_ARRAY16:
+ case CMP_TYPE_ARRAY32:
+ case CMP_TYPE_FIXMAP:
+ case CMP_TYPE_MAP16:
+ case CMP_TYPE_MAP32:
+ obj->type = cmp_type;
+
+ if (!read_obj_data(ctx, type_marker, obj)) {
+ return false;
+ }
+
+ ctx->error = SKIP_DEPTH_LIMIT_EXCEEDED_ERROR;
+
+ return false;
+ default:
+ if (!read_type_size(ctx, type_marker, cmp_type, &size)) {
+ return false;
+ }
+
+ if (size) {
+ switch (cmp_type) {
+ case CMP_TYPE_FIXEXT1:
+ case CMP_TYPE_FIXEXT2:
+ case CMP_TYPE_FIXEXT4:
+ case CMP_TYPE_FIXEXT8:
+ case CMP_TYPE_FIXEXT16:
+ case CMP_TYPE_EXT8:
+ case CMP_TYPE_EXT16:
+ case CMP_TYPE_EXT32:
+ size++;
+ break;
+ default:
+ break;
+ }
+
+ skip_bytes(ctx, size);
+ }
+ }
+
+ return true;
+}
+
+bool cmp_skip_object_flat(cmp_ctx_t *ctx, cmp_object_t *obj) {
+ size_t element_count = 1;
+ bool in_container = false;
+
+ while (element_count) {
+ uint8_t type_marker = 0;
+ uint8_t cmp_type;
+ uint32_t size = 0;
+
+ if (!read_type_marker(ctx, &type_marker)) {
+ return false;
+ }
+
+ if (!type_marker_to_cmp_type(type_marker, &cmp_type)) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ switch (cmp_type) {
+ case CMP_TYPE_FIXARRAY:
+ case CMP_TYPE_ARRAY16:
+ case CMP_TYPE_ARRAY32:
+ case CMP_TYPE_FIXMAP:
+ case CMP_TYPE_MAP16:
+ case CMP_TYPE_MAP32:
+ if (in_container) {
+ obj->type = cmp_type;
+
+ if (!read_obj_data(ctx, type_marker, obj)) {
+ return false;
+ }
+
+ ctx->error = SKIP_DEPTH_LIMIT_EXCEEDED_ERROR;
+ return false;
+ }
+
+ in_container = true;
+
+ break;
+ default:
+ if (!read_type_size(ctx, type_marker, cmp_type, &size)) {
+ return false;
+ }
+
+ if (size) {
+ switch (cmp_type) {
+ case CMP_TYPE_FIXEXT1:
+ case CMP_TYPE_FIXEXT2:
+ case CMP_TYPE_FIXEXT4:
+ case CMP_TYPE_FIXEXT8:
+ case CMP_TYPE_FIXEXT16:
+ case CMP_TYPE_EXT8:
+ case CMP_TYPE_EXT16:
+ case CMP_TYPE_EXT32:
+ size++;
+ break;
+ default:
+ break;
+ }
+
+ skip_bytes(ctx, size);
+ }
+ }
+
+ element_count--;
+
+ switch (cmp_type) {
+ case CMP_TYPE_FIXARRAY:
+ case CMP_TYPE_ARRAY16:
+ case CMP_TYPE_ARRAY32:
+ if (!read_type_size(ctx, type_marker, cmp_type, &size)) {
+ return false;
+ }
+ element_count += size;
+ break;
+ case CMP_TYPE_FIXMAP:
+ case CMP_TYPE_MAP16:
+ case CMP_TYPE_MAP32:
+ if (!read_type_size(ctx, type_marker, cmp_type, &size)) {
+ return false;
+ }
+ element_count += ((size_t)size) * 2;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool cmp_skip_object_no_limit(cmp_ctx_t *ctx) {
+ size_t element_count = 1;
+
+ while (element_count) {
+ uint8_t type_marker = 0;
+ uint8_t cmp_type = 0;
+ uint32_t size = 0;
+
+ if (!read_type_marker(ctx, &type_marker)) {
+ return false;
+ }
+
+ if (!type_marker_to_cmp_type(type_marker, &cmp_type)) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ switch (cmp_type) {
+ case CMP_TYPE_FIXARRAY:
+ case CMP_TYPE_ARRAY16:
+ case CMP_TYPE_ARRAY32:
+ case CMP_TYPE_FIXMAP:
+ case CMP_TYPE_MAP16:
+ case CMP_TYPE_MAP32:
+ break;
+ default:
+ if (!read_type_size(ctx, type_marker, cmp_type, &size)) {
+ return false;
+ }
+
+ if (size) {
+ switch (cmp_type) {
+ case CMP_TYPE_FIXEXT1:
+ case CMP_TYPE_FIXEXT2:
+ case CMP_TYPE_FIXEXT4:
+ case CMP_TYPE_FIXEXT8:
+ case CMP_TYPE_FIXEXT16:
+ case CMP_TYPE_EXT8:
+ case CMP_TYPE_EXT16:
+ case CMP_TYPE_EXT32:
+ size++;
+ break;
+ default:
+ break;
+ }
+
+ skip_bytes(ctx, size);
+ }
+ }
+
+ element_count--;
+
+ switch (cmp_type) {
+ case CMP_TYPE_FIXARRAY:
+ case CMP_TYPE_ARRAY16:
+ case CMP_TYPE_ARRAY32:
+ if (!read_type_size(ctx, type_marker, cmp_type, &size)) {
+ return false;
+ }
+ element_count += size;
+ break;
+ case CMP_TYPE_FIXMAP:
+ case CMP_TYPE_MAP16:
+ case CMP_TYPE_MAP32:
+ if (!read_type_size(ctx, type_marker, cmp_type, &size)) {
+ return false;
+ }
+ element_count += ((size_t)size) * 2;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool cmp_skip_object_limit(cmp_ctx_t *ctx, cmp_object_t *obj, uint32_t limit) {
+ size_t element_count = 1;
+ uint32_t depth = 0;
+
+ while (element_count) {
+ uint8_t type_marker = 0;
+ uint8_t cmp_type;
+ uint32_t size = 0;
+
+ if (!read_type_marker(ctx, &type_marker)) {
+ return false;
+ }
+
+ if (!type_marker_to_cmp_type(type_marker, &cmp_type)) {
+ ctx->error = INVALID_TYPE_ERROR;
+ return false;
+ }
+
+ switch (cmp_type) {
+ case CMP_TYPE_FIXARRAY:
+ case CMP_TYPE_ARRAY16:
+ case CMP_TYPE_ARRAY32:
+ case CMP_TYPE_FIXMAP:
+ case CMP_TYPE_MAP16:
+ case CMP_TYPE_MAP32:
+ depth++;
+
+ if (depth > limit) {
+ obj->type = cmp_type;
+
+ if (!read_obj_data(ctx, type_marker, obj)) {
+ return false;
+ }
+
+ ctx->error = SKIP_DEPTH_LIMIT_EXCEEDED_ERROR;
+
+ return false;
+ }
+
+ break;
+ default:
+ if (!read_type_size(ctx, type_marker, cmp_type, &size)) {
+ return false;
+ }
+
+ if (size) {
+ switch (cmp_type) {
+ case CMP_TYPE_FIXEXT1:
+ case CMP_TYPE_FIXEXT2:
+ case CMP_TYPE_FIXEXT4:
+ case CMP_TYPE_FIXEXT8:
+ case CMP_TYPE_FIXEXT16:
+ case CMP_TYPE_EXT8:
+ case CMP_TYPE_EXT16:
+ case CMP_TYPE_EXT32:
+ size++;
+ break;
+ default:
+ break;
+ }
+
+ skip_bytes(ctx, size);
+ }
+ }
+
+ element_count--;
+
+ switch (cmp_type) {
+ case CMP_TYPE_FIXARRAY:
+ case CMP_TYPE_ARRAY16:
+ case CMP_TYPE_ARRAY32:
+ if (!read_type_size(ctx, type_marker, cmp_type, &size)) {
+ return false;
+ }
+ element_count += size;
+ break;
+ case CMP_TYPE_FIXMAP:
+ case CMP_TYPE_MAP16:
+ case CMP_TYPE_MAP32:
+ if (!read_type_size(ctx, type_marker, cmp_type, &size)) {
+ return false;
+ }
+ element_count += ((size_t)size) * 2;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool cmp_object_is_char(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_is_short(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ case CMP_TYPE_SINT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_is_int(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ case CMP_TYPE_SINT16:
+ case CMP_TYPE_SINT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_is_long(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ case CMP_TYPE_SINT16:
+ case CMP_TYPE_SINT32:
+ case CMP_TYPE_SINT64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_is_sinteger(const cmp_object_t *obj) {
+ return cmp_object_is_long(obj);
+}
+
+bool cmp_object_is_uchar(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_UINT8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_is_ushort(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_UINT8:
+ return true;
+ case CMP_TYPE_UINT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_is_uint(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_UINT8:
+ case CMP_TYPE_UINT16:
+ case CMP_TYPE_UINT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_is_ulong(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_UINT8:
+ case CMP_TYPE_UINT16:
+ case CMP_TYPE_UINT32:
+ case CMP_TYPE_UINT64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_is_uinteger(const cmp_object_t *obj) {
+ return cmp_object_is_ulong(obj);
+}
+
+bool cmp_object_is_float(const cmp_object_t *obj) {
+ if (obj->type == CMP_TYPE_FLOAT)
+ return true;
+
+ return false;
+}
+
+bool cmp_object_is_double(const cmp_object_t *obj) {
+ if (obj->type == CMP_TYPE_DOUBLE)
+ return true;
+
+ return false;
+}
+
+bool cmp_object_is_nil(const cmp_object_t *obj) {
+ if (obj->type == CMP_TYPE_NIL)
+ return true;
+
+ return false;
+}
+
+bool cmp_object_is_bool(const cmp_object_t *obj) {
+ if (obj->type == CMP_TYPE_BOOLEAN)
+ return true;
+
+ return false;
+}
+
+bool cmp_object_is_str(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_FIXSTR:
+ case CMP_TYPE_STR8:
+ case CMP_TYPE_STR16:
+ case CMP_TYPE_STR32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_is_bin(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_BIN8:
+ case CMP_TYPE_BIN16:
+ case CMP_TYPE_BIN32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_is_array(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_FIXARRAY:
+ case CMP_TYPE_ARRAY16:
+ case CMP_TYPE_ARRAY32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_is_map(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_FIXMAP:
+ case CMP_TYPE_MAP16:
+ case CMP_TYPE_MAP32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_is_ext(const cmp_object_t *obj) {
+ switch (obj->type) {
+ case CMP_TYPE_FIXEXT1:
+ case CMP_TYPE_FIXEXT2:
+ case CMP_TYPE_FIXEXT4:
+ case CMP_TYPE_FIXEXT8:
+ case CMP_TYPE_FIXEXT16:
+ case CMP_TYPE_EXT8:
+ case CMP_TYPE_EXT16:
+ case CMP_TYPE_EXT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_char(const cmp_object_t *obj, int8_t *c) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ *c = obj->as.s8;
+ return true;
+ case CMP_TYPE_UINT8:
+ if (obj->as.u8 <= 127) {
+ *c = obj->as.s8;
+ return true;
+ }
+ else {
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_short(const cmp_object_t *obj, int16_t *s) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ *s = obj->as.s8;
+ return true;
+ case CMP_TYPE_UINT8:
+ *s = obj->as.u8;
+ return true;
+ case CMP_TYPE_SINT16:
+ *s = obj->as.s16;
+ return true;
+ case CMP_TYPE_UINT16:
+ if (obj->as.u16 <= 32767) {
+ *s = (int16_t)obj->as.u16;
+ return true;
+ }
+ else {
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_int(const cmp_object_t *obj, int32_t *i) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ *i = obj->as.s8;
+ return true;
+ case CMP_TYPE_UINT8:
+ *i = obj->as.u8;
+ return true;
+ case CMP_TYPE_SINT16:
+ *i = obj->as.s16;
+ return true;
+ case CMP_TYPE_UINT16:
+ *i = obj->as.u16;
+ return true;
+ case CMP_TYPE_SINT32:
+ *i = obj->as.s32;
+ return true;
+ case CMP_TYPE_UINT32:
+ if (obj->as.u32 <= 2147483647) {
+ *i = (int32_t)obj->as.u32;
+ return true;
+ }
+ else {
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_long(const cmp_object_t *obj, int64_t *d) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_NEGATIVE_FIXNUM:
+ case CMP_TYPE_SINT8:
+ *d = obj->as.s8;
+ return true;
+ case CMP_TYPE_UINT8:
+ *d = obj->as.u8;
+ return true;
+ case CMP_TYPE_SINT16:
+ *d = obj->as.s16;
+ return true;
+ case CMP_TYPE_UINT16:
+ *d = obj->as.u16;
+ return true;
+ case CMP_TYPE_SINT32:
+ *d = obj->as.s32;
+ return true;
+ case CMP_TYPE_UINT32:
+ *d = obj->as.u32;
+ return true;
+ case CMP_TYPE_SINT64:
+ *d = obj->as.s64;
+ return true;
+ case CMP_TYPE_UINT64:
+ if (obj->as.u64 <= 9223372036854775807) {
+ *d = (int64_t)obj->as.u64;
+ return true;
+ }
+ else {
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_sinteger(const cmp_object_t *obj, int64_t *d) {
+ return cmp_object_as_long(obj, d);
+}
+
+bool cmp_object_as_uchar(const cmp_object_t *obj, uint8_t *c) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_UINT8:
+ *c = obj->as.u8;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_ushort(const cmp_object_t *obj, uint16_t *s) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_UINT8:
+ *s = obj->as.u8;
+ return true;
+ case CMP_TYPE_UINT16:
+ *s = obj->as.u16;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_uint(const cmp_object_t *obj, uint32_t *i) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_UINT8:
+ *i = obj->as.u8;
+ return true;
+ case CMP_TYPE_UINT16:
+ *i = obj->as.u16;
+ return true;
+ case CMP_TYPE_UINT32:
+ *i = obj->as.u32;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_ulong(const cmp_object_t *obj, uint64_t *u) {
+ switch (obj->type) {
+ case CMP_TYPE_POSITIVE_FIXNUM:
+ case CMP_TYPE_UINT8:
+ *u = obj->as.u8;
+ return true;
+ case CMP_TYPE_UINT16:
+ *u = obj->as.u16;
+ return true;
+ case CMP_TYPE_UINT32:
+ *u = obj->as.u32;
+ return true;
+ case CMP_TYPE_UINT64:
+ *u = obj->as.u64;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_uinteger(const cmp_object_t *obj, uint64_t *u) {
+ return cmp_object_as_ulong(obj, u);
+}
+
+#ifndef CMP_NO_FLOAT
+bool cmp_object_as_float(const cmp_object_t *obj, float *f) {
+ if (obj->type == CMP_TYPE_FLOAT) {
+ *f = obj->as.flt;
+ return true;
+ }
+
+ return false;
+}
+
+bool cmp_object_as_double(const cmp_object_t *obj, double *d) {
+ if (obj->type == CMP_TYPE_DOUBLE) {
+ *d = obj->as.dbl;
+ return true;
+ }
+
+ return false;
+}
+#endif /* CMP_NO_FLOAT */
+
+bool cmp_object_as_bool(const cmp_object_t *obj, bool *b) {
+ if (obj->type == CMP_TYPE_BOOLEAN) {
+ if (obj->as.boolean)
+ *b = true;
+ else
+ *b = false;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool cmp_object_as_str(const cmp_object_t *obj, uint32_t *size) {
+ switch (obj->type) {
+ case CMP_TYPE_FIXSTR:
+ case CMP_TYPE_STR8:
+ case CMP_TYPE_STR16:
+ case CMP_TYPE_STR32:
+ *size = obj->as.str_size;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_bin(const cmp_object_t *obj, uint32_t *size) {
+ switch (obj->type) {
+ case CMP_TYPE_BIN8:
+ case CMP_TYPE_BIN16:
+ case CMP_TYPE_BIN32:
+ *size = obj->as.bin_size;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_array(const cmp_object_t *obj, uint32_t *size) {
+ switch (obj->type) {
+ case CMP_TYPE_FIXARRAY:
+ case CMP_TYPE_ARRAY16:
+ case CMP_TYPE_ARRAY32:
+ *size = obj->as.array_size;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_map(const cmp_object_t *obj, uint32_t *size) {
+ switch (obj->type) {
+ case CMP_TYPE_FIXMAP:
+ case CMP_TYPE_MAP16:
+ case CMP_TYPE_MAP32:
+ *size = obj->as.map_size;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_as_ext(const cmp_object_t *obj, int8_t *type, uint32_t *size) {
+ switch (obj->type) {
+ case CMP_TYPE_FIXEXT1:
+ case CMP_TYPE_FIXEXT2:
+ case CMP_TYPE_FIXEXT4:
+ case CMP_TYPE_FIXEXT8:
+ case CMP_TYPE_FIXEXT16:
+ case CMP_TYPE_EXT8:
+ case CMP_TYPE_EXT16:
+ case CMP_TYPE_EXT32:
+ *type = obj->as.ext.type;
+ *size = obj->as.ext.size;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_to_str(cmp_ctx_t *ctx, const cmp_object_t *obj, char *data,
+ uint32_t buf_size) {
+ uint32_t str_size = 0;
+
+ switch (obj->type) {
+ case CMP_TYPE_FIXSTR:
+ case CMP_TYPE_STR8:
+ case CMP_TYPE_STR16:
+ case CMP_TYPE_STR32:
+ str_size = obj->as.str_size;
+ if (str_size >= buf_size) {
+ ctx->error = STR_DATA_LENGTH_TOO_LONG_ERROR;
+ return false;
+ }
+
+ if (!ctx->read(ctx, data, str_size)) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+
+ data[str_size] = 0;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool cmp_object_to_bin(cmp_ctx_t *ctx, const cmp_object_t *obj, void *data,
+ uint32_t buf_size) {
+ uint32_t bin_size = 0;
+
+ switch (obj->type) {
+ case CMP_TYPE_BIN8:
+ case CMP_TYPE_BIN16:
+ case CMP_TYPE_BIN32:
+ bin_size = obj->as.bin_size;
+ if (bin_size > buf_size) {
+ ctx->error = BIN_DATA_LENGTH_TOO_LONG_ERROR;
+ return false;
+ }
+
+ if (!ctx->read(ctx, data, bin_size)) {
+ ctx->error = DATA_READING_ERROR;
+ return false;
+ }
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* vi: set et ts=2 sw=2: */
+
diff --git a/protocols/Tox/libtox/src/third_party/cmp/cmp.h b/protocols/Tox/libtox/src/third_party/cmp/cmp.h
new file mode 100644
index 0000000000..3fde68dbf0
--- /dev/null
+++ b/protocols/Tox/libtox/src/third_party/cmp/cmp.h
@@ -0,0 +1,572 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2020 Charles Gunyon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef CMP_H_INCLUDED
+#define CMP_H_INCLUDED
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+struct cmp_ctx_s;
+
+typedef bool (*cmp_reader)(struct cmp_ctx_s *ctx, void *data, size_t limit);
+typedef bool (*cmp_skipper)(struct cmp_ctx_s *ctx, size_t count);
+typedef size_t (*cmp_writer)(struct cmp_ctx_s *ctx, const void *data,
+ size_t count);
+
+enum {
+ CMP_TYPE_POSITIVE_FIXNUM, /* 0 */
+ CMP_TYPE_FIXMAP, /* 1 */
+ CMP_TYPE_FIXARRAY, /* 2 */
+ CMP_TYPE_FIXSTR, /* 3 */
+ CMP_TYPE_NIL, /* 4 */
+ CMP_TYPE_BOOLEAN, /* 5 */
+ CMP_TYPE_BIN8, /* 6 */
+ CMP_TYPE_BIN16, /* 7 */
+ CMP_TYPE_BIN32, /* 8 */
+ CMP_TYPE_EXT8, /* 9 */
+ CMP_TYPE_EXT16, /* 10 */
+ CMP_TYPE_EXT32, /* 11 */
+ CMP_TYPE_FLOAT, /* 12 */
+ CMP_TYPE_DOUBLE, /* 13 */
+ CMP_TYPE_UINT8, /* 14 */
+ CMP_TYPE_UINT16, /* 15 */
+ CMP_TYPE_UINT32, /* 16 */
+ CMP_TYPE_UINT64, /* 17 */
+ CMP_TYPE_SINT8, /* 18 */
+ CMP_TYPE_SINT16, /* 19 */
+ CMP_TYPE_SINT32, /* 20 */
+ CMP_TYPE_SINT64, /* 21 */
+ CMP_TYPE_FIXEXT1, /* 22 */
+ CMP_TYPE_FIXEXT2, /* 23 */
+ CMP_TYPE_FIXEXT4, /* 24 */
+ CMP_TYPE_FIXEXT8, /* 25 */
+ CMP_TYPE_FIXEXT16, /* 26 */
+ CMP_TYPE_STR8, /* 27 */
+ CMP_TYPE_STR16, /* 28 */
+ CMP_TYPE_STR32, /* 29 */
+ CMP_TYPE_ARRAY16, /* 30 */
+ CMP_TYPE_ARRAY32, /* 31 */
+ CMP_TYPE_MAP16, /* 32 */
+ CMP_TYPE_MAP32, /* 33 */
+ CMP_TYPE_NEGATIVE_FIXNUM /* 34 */
+};
+
+typedef struct cmp_ext_s {
+ int8_t type;
+ uint32_t size;
+} cmp_ext_t;
+
+union cmp_object_data_u {
+ bool boolean;
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ int8_t s8;
+ int16_t s16;
+ int32_t s32;
+ int64_t s64;
+#ifndef CMP_NO_FLOAT
+ float flt;
+ double dbl;
+#endif /* CMP_NO_FLOAT */
+ uint32_t array_size;
+ uint32_t map_size;
+ uint32_t str_size;
+ uint32_t bin_size;
+ cmp_ext_t ext;
+};
+
+typedef struct cmp_ctx_s {
+ uint8_t error;
+ void *buf;
+ cmp_reader read;
+ cmp_skipper skip;
+ cmp_writer write;
+} cmp_ctx_t;
+
+typedef struct cmp_object_s {
+ uint8_t type;
+ union cmp_object_data_u as;
+} cmp_object_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * ============================================================================
+ * === Main API
+ * ============================================================================
+ */
+
+/*
+ * Initializes a CMP context
+ *
+ * If you don't intend to read, `read` may be NULL, but calling `*read*`
+ * functions will crash; there is no check.
+ *
+ * `skip` may be NULL, in which case skipping functions will use `read`.
+ *
+ * If you don't intend to write, `write` may be NULL, but calling `*write*`
+ * functions will crash; there is no check.
+ */
+void cmp_init(cmp_ctx_t *ctx, void *buf, cmp_reader read,
+ cmp_skipper skip,
+ cmp_writer write);
+
+/* Returns CMP's version */
+uint32_t cmp_version(void);
+
+/* Returns the MessagePack version employed by CMP */
+uint32_t cmp_mp_version(void);
+
+/* Returns a string description of a CMP context's error */
+const char* cmp_strerror(cmp_ctx_t *ctx);
+
+/* Writes a signed integer to the backend */
+bool cmp_write_integer(cmp_ctx_t *ctx, int64_t d);
+
+/* Writes an unsigned integer to the backend */
+bool cmp_write_uinteger(cmp_ctx_t *ctx, uint64_t u);
+
+/*
+ * Writes a floating-point value (either single or double-precision) to the
+ * backend
+ */
+#ifndef CMP_NO_FLOAT
+bool cmp_write_decimal(cmp_ctx_t *ctx, double d);
+#endif /* CMP_NO_FLOAT */
+
+/* Writes NULL to the backend */
+bool cmp_write_nil(cmp_ctx_t *ctx);
+
+/* Writes true to the backend */
+bool cmp_write_true(cmp_ctx_t *ctx);
+
+/* Writes false to the backend */
+bool cmp_write_false(cmp_ctx_t *ctx);
+
+/* Writes a boolean value to the backend */
+bool cmp_write_bool(cmp_ctx_t *ctx, bool b);
+
+/*
+ * Writes an unsigned char's value to the backend as a boolean. This is useful
+ * if you are using a different boolean type in your application.
+ */
+bool cmp_write_u8_as_bool(cmp_ctx_t *ctx, uint8_t b);
+
+/*
+ * Writes a string to the backend; according to the MessagePack spec, this must
+ * be encoded using UTF-8, but CMP leaves that job up to the programmer.
+ */
+bool cmp_write_str(cmp_ctx_t *ctx, const char *data, uint32_t size);
+
+/*
+ * Writes a string to the backend. This avoids using the STR8 marker, which
+ * is unsupported by MessagePack v4, the version implemented by many other
+ * MessagePack libraries. No encoding is assumed in this case, not that it
+ * matters.
+ */
+bool cmp_write_str_v4(cmp_ctx_t *ctx, const char *data, uint32_t size);
+
+/*
+ * Writes the string marker to the backend. This is useful if you are writing
+ * data in chunks instead of a single shot.
+ */
+bool cmp_write_str_marker(cmp_ctx_t *ctx, uint32_t size);
+
+/*
+ * Writes the string marker to the backend. This is useful if you are writing
+ * data in chunks instead of a single shot. This avoids using the STR8
+ * marker, which is unsupported by MessagePack v4, the version implemented by
+ * many other MessagePack libraries. No encoding is assumed in this case, not
+ * that it matters.
+ */
+bool cmp_write_str_marker_v4(cmp_ctx_t *ctx, uint32_t size);
+
+/* Writes binary data to the backend */
+bool cmp_write_bin(cmp_ctx_t *ctx, const void *data, uint32_t size);
+
+/*
+ * Writes the binary data marker to the backend. This is useful if you are
+ * writing data in chunks instead of a single shot.
+ */
+bool cmp_write_bin_marker(cmp_ctx_t *ctx, uint32_t size);
+
+/* Writes an array to the backend. */
+bool cmp_write_array(cmp_ctx_t *ctx, uint32_t size);
+
+/* Writes a map to the backend. */
+bool cmp_write_map(cmp_ctx_t *ctx, uint32_t size);
+
+/* Writes an extended type to the backend */
+bool cmp_write_ext(cmp_ctx_t *ctx, int8_t type, uint32_t size,
+ const void *data);
+
+/*
+ * Writes the extended type marker to the backend. This is useful if you want
+ * to write the type's data in chunks instead of a single shot.
+ */
+bool cmp_write_ext_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size);
+
+/* Writes an object to the backend */
+bool cmp_write_object(cmp_ctx_t *ctx, const cmp_object_t *obj);
+
+/*
+ * Writes an object to the backend. This avoids using the STR8 marker, which
+ * is unsupported by MessagePack v4, the version implemented by many other
+ * MessagePack libraries.
+ */
+bool cmp_write_object_v4(cmp_ctx_t *ctx, const cmp_object_t *obj);
+
+/* Reads a signed integer that fits inside a signed char */
+bool cmp_read_char(cmp_ctx_t *ctx, int8_t *c);
+
+/* Reads a signed integer that fits inside a signed short */
+bool cmp_read_short(cmp_ctx_t *ctx, int16_t *s);
+
+/* Reads a signed integer that fits inside a signed int */
+bool cmp_read_int(cmp_ctx_t *ctx, int32_t *i);
+
+/* Reads a signed integer that fits inside a signed long */
+bool cmp_read_long(cmp_ctx_t *ctx, int64_t *d);
+
+/* Reads a signed integer */
+bool cmp_read_integer(cmp_ctx_t *ctx, int64_t *d);
+
+/* Reads an unsigned integer that fits inside an unsigned char */
+bool cmp_read_uchar(cmp_ctx_t *ctx, uint8_t *c);
+
+/* Reads an unsigned integer that fits inside an unsigned short */
+bool cmp_read_ushort(cmp_ctx_t *ctx, uint16_t *s);
+
+/* Reads an unsigned integer that fits inside an unsigned int */
+bool cmp_read_uint(cmp_ctx_t *ctx, uint32_t *i);
+
+/* Reads an unsigned integer that fits inside an unsigned long */
+bool cmp_read_ulong(cmp_ctx_t *ctx, uint64_t *u);
+
+/* Reads an unsigned integer */
+bool cmp_read_uinteger(cmp_ctx_t *ctx, uint64_t *u);
+
+/*
+ * Reads a floating point value (either single or double-precision) from the
+ * backend
+ */
+#ifndef CMP_NO_FLOAT
+bool cmp_read_decimal(cmp_ctx_t *ctx, double *d);
+#endif /* CMP_NO_FLOAT */
+
+/* "Reads" (more like "skips") a NULL value from the backend */
+bool cmp_read_nil(cmp_ctx_t *ctx);
+
+/* Reads a boolean from the backend */
+bool cmp_read_bool(cmp_ctx_t *ctx, bool *b);
+
+/*
+ * Reads a boolean as an unsigned char from the backend; this is useful if your
+ * application uses a different boolean type.
+ */
+bool cmp_read_bool_as_u8(cmp_ctx_t *ctx, uint8_t *b);
+
+/* Reads a string's size from the backend */
+bool cmp_read_str_size(cmp_ctx_t *ctx, uint32_t *size);
+
+/*
+ * Reads a string from the backend; according to the spec, the string's data
+ * ought to be encoded using UTF-8,
+ */
+bool cmp_read_str(cmp_ctx_t *ctx, char *data, uint32_t *size);
+
+/* Reads the size of packed binary data from the backend */
+bool cmp_read_bin_size(cmp_ctx_t *ctx, uint32_t *size);
+
+/* Reads packed binary data from the backend */
+bool cmp_read_bin(cmp_ctx_t *ctx, void *data, uint32_t *size);
+
+/* Reads an array from the backend */
+bool cmp_read_array(cmp_ctx_t *ctx, uint32_t *size);
+
+/* Reads a map from the backend */
+bool cmp_read_map(cmp_ctx_t *ctx, uint32_t *size);
+
+/* Reads the extended type's marker from the backend */
+bool cmp_read_ext_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size);
+
+/* Reads an extended type from the backend */
+bool cmp_read_ext(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data);
+
+/* Reads an object from the backend */
+bool cmp_read_object(cmp_ctx_t *ctx, cmp_object_t *obj);
+
+/*
+ * Skips the next object from the backend. If that object is an array or map,
+ * this function will:
+ * - If `obj` is not `NULL`, fill in `obj` with that object
+ * - Set `ctx->error` to `SKIP_DEPTH_LIMIT_EXCEEDED_ERROR`
+ * - Return `false`
+ * Otherwise:
+ * - (Don't touch `obj`)
+ * - Return `true`
+ */
+bool cmp_skip_object(cmp_ctx_t *ctx, cmp_object_t *obj);
+
+/*
+ * This is similar to `cmp_skip_object`, except it tolerates flat arrays and
+ * maps. If when skipping such an array or map this function encounters
+ * another array/map, it will:
+ * - If `obj` is not `NULL`, fill in `obj` with that (nested) object
+ * - Set `ctx->error` to `SKIP_DEPTH_LIMIT_EXCEEDED_ERROR`
+ * - Return `false`
+ * Otherwise:
+ * - (Don't touch `obj`)
+ * - Return `true`
+ *
+ * WARNING: This can cause your application to spend an unbounded amount of
+ * time reading nested data structures. Unless you completely trust
+ * the data source, you should use `cmp_skip_object`.
+ */
+bool cmp_skip_object_flat(cmp_ctx_t *ctx, cmp_object_t *obj);
+
+/*
+ * This is similar to `cmp_skip_object`, except it will continually skip
+ * nested data structures.
+ *
+ * WARNING: This can cause your application to spend an unbounded amount of
+ * time reading nested data structures. Unless you completely trust
+ * the data source, you should use `cmp_skip_object`.
+ */
+bool cmp_skip_object_no_limit(cmp_ctx_t *ctx);
+
+/*
+ * WARNING: THIS FUNCTION IS DEPRECATED AND WILL BE REMOVED IN A FUTURE RELEASE
+ *
+ * There is no way to track depths across elements without allocation. For
+ * example, an array constructed as: `[ [] [] [] [] [] [] [] [] [] [] ]`
+ * should be able to be skipped with `cmp_skip_object_limit(&cmp, &obj, 2)`.
+ * However, because we cannot track depth across the elements, there's no way
+ * to reset it after descending down into each element.
+ *
+ * This is similar to `cmp_skip_object`, except it tolerates up to `limit`
+ * levels of nesting. For example, in order to skip an array that contains a
+ * map, call `cmp_skip_object_limit(ctx, &obj, 2)`. Or in other words,
+ * `cmp_skip_object(ctx, &obj)` acts similarly to `cmp_skip_object_limit(ctx,
+ * &obj, 0)`
+ *
+ * Specifically, `limit` refers to depth, not breadth. So in order to skip an
+ * array that contains two arrays that each contain 3 strings, you would call
+ * `cmp_skip_object_limit(ctx, &obj, 2). In order to skip an array that
+ * contains 4 arrays that each contain 1 string, you would still call
+ * `cmp_skip_object_limit(ctx, &obj, 2).
+ */
+bool cmp_skip_object_limit(cmp_ctx_t *ctx, cmp_object_t *obj, uint32_t limit)
+#ifdef __GNUC__
+ __attribute__((deprecated))
+#endif
+;
+
+#ifdef _MSC_VER
+#pragma deprecated(cmp_skip_object_limit)
+#endif
+
+/*
+ * ============================================================================
+ * === Specific API
+ * ============================================================================
+ */
+
+bool cmp_write_pfix(cmp_ctx_t *ctx, uint8_t c);
+bool cmp_write_nfix(cmp_ctx_t *ctx, int8_t c);
+
+bool cmp_write_sfix(cmp_ctx_t *ctx, int8_t c);
+bool cmp_write_s8(cmp_ctx_t *ctx, int8_t c);
+bool cmp_write_s16(cmp_ctx_t *ctx, int16_t s);
+bool cmp_write_s32(cmp_ctx_t *ctx, int32_t i);
+bool cmp_write_s64(cmp_ctx_t *ctx, int64_t l);
+
+bool cmp_write_ufix(cmp_ctx_t *ctx, uint8_t c);
+bool cmp_write_u8(cmp_ctx_t *ctx, uint8_t c);
+bool cmp_write_u16(cmp_ctx_t *ctx, uint16_t s);
+bool cmp_write_u32(cmp_ctx_t *ctx, uint32_t i);
+bool cmp_write_u64(cmp_ctx_t *ctx, uint64_t l);
+
+#ifndef CMP_NO_FLOAT
+bool cmp_write_float(cmp_ctx_t *ctx, float f);
+bool cmp_write_double(cmp_ctx_t *ctx, double d);
+#endif /* CMP_NO_FLOAT */
+
+bool cmp_write_fixstr_marker(cmp_ctx_t *ctx, uint8_t size);
+bool cmp_write_fixstr(cmp_ctx_t *ctx, const char *data, uint8_t size);
+bool cmp_write_str8_marker(cmp_ctx_t *ctx, uint8_t size);
+bool cmp_write_str8(cmp_ctx_t *ctx, const char *data, uint8_t size);
+bool cmp_write_str16_marker(cmp_ctx_t *ctx, uint16_t size);
+bool cmp_write_str16(cmp_ctx_t *ctx, const char *data, uint16_t size);
+bool cmp_write_str32_marker(cmp_ctx_t *ctx, uint32_t size);
+bool cmp_write_str32(cmp_ctx_t *ctx, const char *data, uint32_t size);
+
+bool cmp_write_bin8_marker(cmp_ctx_t *ctx, uint8_t size);
+bool cmp_write_bin8(cmp_ctx_t *ctx, const void *data, uint8_t size);
+bool cmp_write_bin16_marker(cmp_ctx_t *ctx, uint16_t size);
+bool cmp_write_bin16(cmp_ctx_t *ctx, const void *data, uint16_t size);
+bool cmp_write_bin32_marker(cmp_ctx_t *ctx, uint32_t size);
+bool cmp_write_bin32(cmp_ctx_t *ctx, const void *data, uint32_t size);
+
+bool cmp_write_fixarray(cmp_ctx_t *ctx, uint8_t size);
+bool cmp_write_array16(cmp_ctx_t *ctx, uint16_t size);
+bool cmp_write_array32(cmp_ctx_t *ctx, uint32_t size);
+
+bool cmp_write_fixmap(cmp_ctx_t *ctx, uint8_t size);
+bool cmp_write_map16(cmp_ctx_t *ctx, uint16_t size);
+bool cmp_write_map32(cmp_ctx_t *ctx, uint32_t size);
+
+bool cmp_write_fixext1_marker(cmp_ctx_t *ctx, int8_t type);
+bool cmp_write_fixext1(cmp_ctx_t *ctx, int8_t type, const void *data);
+bool cmp_write_fixext2_marker(cmp_ctx_t *ctx, int8_t type);
+bool cmp_write_fixext2(cmp_ctx_t *ctx, int8_t type, const void *data);
+bool cmp_write_fixext4_marker(cmp_ctx_t *ctx, int8_t type);
+bool cmp_write_fixext4(cmp_ctx_t *ctx, int8_t type, const void *data);
+bool cmp_write_fixext8_marker(cmp_ctx_t *ctx, int8_t type);
+bool cmp_write_fixext8(cmp_ctx_t *ctx, int8_t type, const void *data);
+bool cmp_write_fixext16_marker(cmp_ctx_t *ctx, int8_t type);
+bool cmp_write_fixext16(cmp_ctx_t *ctx, int8_t type, const void *data);
+
+bool cmp_write_ext8_marker(cmp_ctx_t *ctx, int8_t type, uint8_t size);
+bool cmp_write_ext8(cmp_ctx_t *ctx, int8_t type, uint8_t size,
+ const void *data);
+bool cmp_write_ext16_marker(cmp_ctx_t *ctx, int8_t type, uint16_t size);
+bool cmp_write_ext16(cmp_ctx_t *ctx, int8_t type, uint16_t size,
+ const void *data);
+bool cmp_write_ext32_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size);
+bool cmp_write_ext32(cmp_ctx_t *ctx, int8_t type, uint32_t size,
+ const void *data);
+
+bool cmp_read_pfix(cmp_ctx_t *ctx, uint8_t *c);
+bool cmp_read_nfix(cmp_ctx_t *ctx, int8_t *c);
+
+bool cmp_read_sfix(cmp_ctx_t *ctx, int8_t *c);
+bool cmp_read_s8(cmp_ctx_t *ctx, int8_t *c);
+bool cmp_read_s16(cmp_ctx_t *ctx, int16_t *s);
+bool cmp_read_s32(cmp_ctx_t *ctx, int32_t *i);
+bool cmp_read_s64(cmp_ctx_t *ctx, int64_t *l);
+
+bool cmp_read_ufix(cmp_ctx_t *ctx, uint8_t *c);
+bool cmp_read_u8(cmp_ctx_t *ctx, uint8_t *c);
+bool cmp_read_u16(cmp_ctx_t *ctx, uint16_t *s);
+bool cmp_read_u32(cmp_ctx_t *ctx, uint32_t *i);
+bool cmp_read_u64(cmp_ctx_t *ctx, uint64_t *l);
+
+#ifndef CMP_NO_FLOAT
+bool cmp_read_float(cmp_ctx_t *ctx, float *f);
+bool cmp_read_double(cmp_ctx_t *ctx, double *d);
+#endif /* CMP_NO_FLOAT */
+
+bool cmp_read_fixext1_marker(cmp_ctx_t *ctx, int8_t *type);
+bool cmp_read_fixext1(cmp_ctx_t *ctx, int8_t *type, void *data);
+bool cmp_read_fixext2_marker(cmp_ctx_t *ctx, int8_t *type);
+bool cmp_read_fixext2(cmp_ctx_t *ctx, int8_t *type, void *data);
+bool cmp_read_fixext4_marker(cmp_ctx_t *ctx, int8_t *type);
+bool cmp_read_fixext4(cmp_ctx_t *ctx, int8_t *type, void *data);
+bool cmp_read_fixext8_marker(cmp_ctx_t *ctx, int8_t *type);
+bool cmp_read_fixext8(cmp_ctx_t *ctx, int8_t *type, void *data);
+bool cmp_read_fixext16_marker(cmp_ctx_t *ctx, int8_t *type);
+bool cmp_read_fixext16(cmp_ctx_t *ctx, int8_t *type, void *data);
+
+bool cmp_read_ext8_marker(cmp_ctx_t *ctx, int8_t *type, uint8_t *size);
+bool cmp_read_ext8(cmp_ctx_t *ctx, int8_t *type, uint8_t *size, void *data);
+bool cmp_read_ext16_marker(cmp_ctx_t *ctx, int8_t *type, uint16_t *size);
+bool cmp_read_ext16(cmp_ctx_t *ctx, int8_t *type, uint16_t *size, void *data);
+bool cmp_read_ext32_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size);
+bool cmp_read_ext32(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data);
+
+/*
+ * ============================================================================
+ * === Object API
+ * ============================================================================
+ */
+
+bool cmp_object_is_char(const cmp_object_t *obj);
+bool cmp_object_is_short(const cmp_object_t *obj);
+bool cmp_object_is_int(const cmp_object_t *obj);
+bool cmp_object_is_long(const cmp_object_t *obj);
+bool cmp_object_is_sinteger(const cmp_object_t *obj);
+bool cmp_object_is_uchar(const cmp_object_t *obj);
+bool cmp_object_is_ushort(const cmp_object_t *obj);
+bool cmp_object_is_uint(const cmp_object_t *obj);
+bool cmp_object_is_ulong(const cmp_object_t *obj);
+bool cmp_object_is_uinteger(const cmp_object_t *obj);
+bool cmp_object_is_float(const cmp_object_t *obj);
+bool cmp_object_is_double(const cmp_object_t *obj);
+bool cmp_object_is_nil(const cmp_object_t *obj);
+bool cmp_object_is_bool(const cmp_object_t *obj);
+bool cmp_object_is_str(const cmp_object_t *obj);
+bool cmp_object_is_bin(const cmp_object_t *obj);
+bool cmp_object_is_array(const cmp_object_t *obj);
+bool cmp_object_is_map(const cmp_object_t *obj);
+bool cmp_object_is_ext(const cmp_object_t *obj);
+
+bool cmp_object_as_char(const cmp_object_t *obj, int8_t *c);
+bool cmp_object_as_short(const cmp_object_t *obj, int16_t *s);
+bool cmp_object_as_int(const cmp_object_t *obj, int32_t *i);
+bool cmp_object_as_long(const cmp_object_t *obj, int64_t *d);
+bool cmp_object_as_sinteger(const cmp_object_t *obj, int64_t *d);
+bool cmp_object_as_uchar(const cmp_object_t *obj, uint8_t *c);
+bool cmp_object_as_ushort(const cmp_object_t *obj, uint16_t *s);
+bool cmp_object_as_uint(const cmp_object_t *obj, uint32_t *i);
+bool cmp_object_as_ulong(const cmp_object_t *obj, uint64_t *u);
+bool cmp_object_as_uinteger(const cmp_object_t *obj, uint64_t *u);
+bool cmp_object_as_float(const cmp_object_t *obj, float *f);
+bool cmp_object_as_double(const cmp_object_t *obj, double *d);
+bool cmp_object_as_bool(const cmp_object_t *obj, bool *b);
+bool cmp_object_as_str(const cmp_object_t *obj, uint32_t *size);
+bool cmp_object_as_bin(const cmp_object_t *obj, uint32_t *size);
+bool cmp_object_as_array(const cmp_object_t *obj, uint32_t *size);
+bool cmp_object_as_map(const cmp_object_t *obj, uint32_t *size);
+bool cmp_object_as_ext(const cmp_object_t *obj, int8_t *type, uint32_t *size);
+
+bool cmp_object_to_str(cmp_ctx_t *ctx, const cmp_object_t *obj, char *data, uint32_t buf_size);
+bool cmp_object_to_bin(cmp_ctx_t *ctx, const cmp_object_t *obj, void *data, uint32_t buf_size);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+/*
+ * ============================================================================
+ * === Backwards compatibility defines
+ * ============================================================================
+ */
+
+#define cmp_write_int cmp_write_integer
+#define cmp_write_sint cmp_write_integer
+#define cmp_write_sinteger cmp_write_integer
+#define cmp_write_uint cmp_write_uinteger
+#define cmp_read_sinteger cmp_read_integer
+
+#endif /* CMP_H_INCLUDED */
+
+/* vi: set et ts=2 sw=2: */
+
diff --git a/protocols/Tox/libtox/src/toxcore/DHT.c b/protocols/Tox/libtox/src/toxcore/DHT.c
index 4cc8d2002f..8aa161102b 100644
--- a/protocols/Tox/libtox/src/toxcore/DHT.c
+++ b/protocols/Tox/libtox/src/toxcore/DHT.c
@@ -13,6 +13,7 @@
#include <string.h>
#include "LAN_discovery.h"
+#include "ccompat.h"
#include "logger.h"
#include "mono_time.h"
#include "network.h"
@@ -67,6 +68,9 @@ struct DHT_Friend {
unsigned int num_to_bootstrap;
};
+static const DHT_Friend empty_dht_friend = {{0}};
+const Node_format empty_node_format = {{0}};
+
typedef struct Cryptopacket_Handler {
cryptopacket_handler_cb *function;
void *object;
@@ -74,10 +78,13 @@ typedef struct Cryptopacket_Handler {
struct DHT {
const Logger *log;
+ const Network *ns;
Mono_Time *mono_time;
+ const Random *rng;
Networking_Core *net;
bool hole_punching_enabled;
+ bool lan_discovery_enabled;
Client_data close_clientlist[LCLIENT_LIST];
uint64_t close_lastgetnodes;
@@ -105,6 +112,8 @@ struct DHT {
Node_format to_bootstrap[MAX_CLOSE_TO_BOOTSTRAP_NODES];
unsigned int num_to_bootstrap;
+
+ dht_get_nodes_response_cb *get_nodes_response;
};
const uint8_t *dht_friend_public_key(const DHT_Friend *dht_friend)
@@ -168,32 +177,34 @@ const uint8_t *dht_get_friend_public_key(const DHT *dht, uint32_t friend_num)
return dht->friends_list[friend_num].public_key;
}
+non_null()
static bool assoc_timeout(uint64_t cur_time, const IPPTsPng *assoc)
{
return (assoc->timestamp + BAD_NODE_TIMEOUT) <= cur_time;
}
-/** Converts an IPv4-in-IPv6 to IPv4 and returns the new IP_Port.
+/** @brief Converts an IPv4-in-IPv6 to IPv4 and returns the new IP_Port.
*
* If the ip_port is already IPv4 this function returns a copy of the original ip_port.
*/
+non_null()
static IP_Port ip_port_normalize(const IP_Port *ip_port)
{
IP_Port res = *ip_port;
if (net_family_is_ipv6(res.ip.family) && ipv6_ipv4_in_v6(&res.ip.ip.v6)) {
- res.ip.family = net_family_ipv4;
+ res.ip.family = net_family_ipv4();
res.ip.ip.v4.uint32 = res.ip.ip.v6.uint32[3];
}
return res;
}
-/** Compares pk1 and pk2 with pk.
+/** @brief Compares pk1 and pk2 with pk.
*
- * return 0 if both are same distance.
- * return 1 if pk1 is closer.
- * return 2 if pk2 is closer.
+ * @retval 0 if both are same distance.
+ * @retval 1 if pk1 is closer.
+ * @retval 2 if pk2 is closer.
*/
int id_closest(const uint8_t *pk, const uint8_t *pk1, const uint8_t *pk2)
{
@@ -213,9 +224,8 @@ int id_closest(const uint8_t *pk, const uint8_t *pk1, const uint8_t *pk2)
return 0;
}
-/** Return index of first unequal bit number.
- */
-static unsigned int bit_by_bit_cmp(const uint8_t *pk1, const uint8_t *pk2)
+/** Return index of first unequal bit number between public keys pk1 and pk2. */
+unsigned int bit_by_bit_cmp(const uint8_t *pk1, const uint8_t *pk2)
{
unsigned int i;
unsigned int j = 0;
@@ -239,7 +249,8 @@ static unsigned int bit_by_bit_cmp(const uint8_t *pk1, const uint8_t *pk2)
return i * 8 + j;
}
-/** Shared key generations are costly, it is therefore smart to store commonly used
+/**
+ * Shared key generations are costly, it is therefore smart to store commonly used
* ones so that they can be re-used later without being computed again.
*
* If a shared key is already in shared_keys, copy it to shared_key.
@@ -256,7 +267,7 @@ void get_shared_key(const Mono_Time *mono_time, Shared_Keys *shared_keys, uint8_
Shared_Key *const key = &shared_keys->keys[index];
if (key->stored) {
- if (id_equal(public_key, key->public_key)) {
+ if (pk_equal(public_key, key->public_key)) {
memcpy(shared_key, key->shared_key, CRYPTO_SHARED_KEY_SIZE);
++key->times_requested;
key->time_last_requested = mono_time_get(mono_time);
@@ -290,7 +301,8 @@ void get_shared_key(const Mono_Time *mono_time, Shared_Keys *shared_keys, uint8_
}
}
-/** Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key
+/**
+ * Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key
* for packets that we receive.
*/
void dht_get_shared_key_recv(DHT *dht, uint8_t *shared_key, const uint8_t *public_key)
@@ -298,7 +310,8 @@ void dht_get_shared_key_recv(DHT *dht, uint8_t *shared_key, const uint8_t *publi
get_shared_key(dht->mono_time, &dht->shared_keys_recv, shared_key, dht->self_secret_key, public_key);
}
-/** Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key
+/**
+ * Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key
* for packets that we send.
*/
void dht_get_shared_key_sent(DHT *dht, uint8_t *shared_key, const uint8_t *public_key)
@@ -308,34 +321,46 @@ void dht_get_shared_key_sent(DHT *dht, uint8_t *shared_key, const uint8_t *publi
#define CRYPTO_SIZE (1 + CRYPTO_PUBLIC_KEY_SIZE * 2 + CRYPTO_NONCE_SIZE)
-/** Create a request to peer.
- * send_public_key and send_secret_key are the pub/secret keys of the sender.
- * recv_public_key is public key of receiver.
- * packet must be an array of MAX_CRYPTO_REQUEST_SIZE big.
- * Data represents the data we send with the request with length being the length of the data.
- * request_id is the id of the request (32 = friend request, 254 = ping request).
+/**
+ * @brief Create a request to peer.
*
- * return -1 on failure.
- * return the length of the created packet on success.
+ * 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.
*/
-int create_request(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 length, uint8_t request_id)
+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)
{
- if (!send_public_key || !packet || !recv_public_key || !data) {
+ if (send_public_key == nullptr || packet == nullptr || recv_public_key == nullptr || data == nullptr) {
return -1;
}
- if (MAX_CRYPTO_REQUEST_SIZE < length + CRYPTO_SIZE + 1 + CRYPTO_MAC_SIZE) {
+ if (MAX_CRYPTO_REQUEST_SIZE < data_length + CRYPTO_SIZE + 1 + CRYPTO_MAC_SIZE) {
return -1;
}
uint8_t *const nonce = packet + 1 + CRYPTO_PUBLIC_KEY_SIZE * 2;
- random_nonce(nonce);
- uint8_t temp[MAX_CRYPTO_REQUEST_SIZE];
- memcpy(temp + 1, data, length);
+ random_nonce(rng, nonce);
+ uint8_t temp[MAX_CRYPTO_REQUEST_SIZE] = {0};
temp[0] = request_id;
- const int len = encrypt_data(recv_public_key, send_secret_key, nonce, temp, length + 1,
- CRYPTO_SIZE + packet);
+ memcpy(temp + 1, data, data_length);
+ const int len = encrypt_data(recv_public_key, send_secret_key, nonce, temp, data_length + 1,
+ packet + CRYPTO_SIZE);
if (len == -1) {
crypto_memzero(temp, MAX_CRYPTO_REQUEST_SIZE);
@@ -350,38 +375,59 @@ int create_request(const uint8_t *send_public_key, const uint8_t *send_secret_ke
return len + CRYPTO_SIZE;
}
-/** Puts the senders public key in the request in public_key, the data from the request
- * in data if a friend or ping request was sent to us and returns the length of the data.
- * packet is the request packet and length is its length.
+/**
+ * @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.
*
- * return -1 if not valid request.
+ * @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.
*/
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 length)
+ uint8_t *request_id, const uint8_t *packet, uint16_t packet_length)
{
- if (!self_public_key || !public_key || !data || !request_id || !packet) {
+ if (self_public_key == nullptr || public_key == nullptr || data == nullptr || request_id == nullptr
+ || packet == nullptr) {
return -1;
}
- if (length <= CRYPTO_SIZE + CRYPTO_MAC_SIZE || length > MAX_CRYPTO_REQUEST_SIZE) {
+ if (packet_length <= CRYPTO_SIZE + CRYPTO_MAC_SIZE || packet_length > MAX_CRYPTO_REQUEST_SIZE) {
return -1;
}
- if (!id_equal(packet + 1, self_public_key)) {
+ if (!pk_equal(packet + 1, self_public_key)) {
return -1;
}
memcpy(public_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_PUBLIC_KEY_SIZE);
const uint8_t *const nonce = packet + 1 + CRYPTO_PUBLIC_KEY_SIZE * 2;
uint8_t temp[MAX_CRYPTO_REQUEST_SIZE];
- int len1 = decrypt_data(public_key, self_secret_key, nonce,
- packet + CRYPTO_SIZE, length - CRYPTO_SIZE, temp);
+ int32_t len1 = decrypt_data(public_key, self_secret_key, nonce,
+ packet + CRYPTO_SIZE, packet_length - CRYPTO_SIZE, temp);
if (len1 == -1 || len1 == 0) {
crypto_memzero(temp, MAX_CRYPTO_REQUEST_SIZE);
return -1;
}
+ assert(len1 == packet_length - CRYPTO_SIZE - CRYPTO_MAC_SIZE);
+ // Because coverity can't figure out this equation:
+ assert(len1 <= MAX_CRYPTO_REQUEST_SIZE - CRYPTO_SIZE - CRYPTO_MAC_SIZE);
+
request_id[0] = temp[0];
--len1;
memcpy(data, temp + 1, len1);
@@ -389,11 +435,8 @@ int handle_request(const uint8_t *self_public_key, const uint8_t *self_secret_ke
return len1;
}
-#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)
-
-/** Return packet size of packed node with ip_family on success.
- * Return -1 on failure.
+/** @return packet size of packed node with ip_family on success.
+ * @retval -1 on failure.
*/
int packed_node_size(Family ip_family)
{
@@ -409,36 +452,40 @@ int packed_node_size(Family ip_family)
}
-/** Packs an IP_Port structure into data of max size length.
+/** @brief Pack an IP_Port structure into data of max size length.
*
* Packed_length is the offset of data currently packed.
*
- * Returns size of packed IP_Port data on success
- * Return -1 on failure.
+ * @return size of packed IP_Port data on success.
+ * @retval -1 on failure.
*/
-int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port)
+int pack_ip_port(const Logger *logger, uint8_t *data, uint16_t length, const IP_Port *ip_port)
{
if (data == nullptr) {
return -1;
}
bool is_ipv4;
- uint8_t net_family;
+ uint8_t family;
if (net_family_is_ipv4(ip_port->ip.family)) {
// TODO(irungentoo): use functions to convert endianness
is_ipv4 = true;
- net_family = TOX_AF_INET;
+ family = TOX_AF_INET;
} else if (net_family_is_tcp_ipv4(ip_port->ip.family)) {
is_ipv4 = true;
- net_family = TOX_TCP_INET;
+ family = TOX_TCP_INET;
} else if (net_family_is_ipv6(ip_port->ip.family)) {
is_ipv4 = false;
- net_family = TOX_AF_INET6;
+ family = TOX_AF_INET6;
} else if (net_family_is_tcp_ipv6(ip_port->ip.family)) {
is_ipv4 = false;
- net_family = TOX_TCP_INET6;
+ family = TOX_TCP_INET6;
} else {
+ Ip_Ntoa ip_str;
+ // TODO(iphydf): Find out why we're trying to pack invalid IPs, stop
+ // doing that, and turn this into an error.
+ LOGGER_TRACE(logger, "cannot pack invalid IP: %s", net_ip_ntoa(&ip_port->ip, &ip_str));
return -1;
}
@@ -449,7 +496,7 @@ int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port)
return -1;
}
- data[0] = net_family;
+ data[0] = family;
memcpy(data + 1, &ip_port->ip.ip.v4, SIZE_IP4);
memcpy(data + 1 + SIZE_IP4, &ip_port->port, sizeof(uint16_t));
return size;
@@ -460,25 +507,41 @@ int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port)
return -1;
}
- data[0] = net_family;
+ data[0] = family;
memcpy(data + 1, &ip_port->ip.ip.v6, SIZE_IP6);
memcpy(data + 1 + SIZE_IP6, &ip_port->port, sizeof(uint16_t));
return size;
}
}
-static int dht_create_packet(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)
+/** @brief Encrypt plain and write resulting DHT packet into packet with max size length.
+ *
+ * @return size of packet on success.
+ * @retval -1 on failure.
+ */
+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)
{
- VLA(uint8_t, encrypted, plain_length + CRYPTO_MAC_SIZE);
+ uint8_t *encrypted = (uint8_t *)malloc(plain_length + CRYPTO_MAC_SIZE);
uint8_t nonce[CRYPTO_NONCE_SIZE];
- random_nonce(nonce);
+ if (encrypted == nullptr) {
+ return -1;
+ }
+
+ random_nonce(rng, nonce);
const int encrypted_length = encrypt_data_symmetric(shared_key, nonce, plain, plain_length, encrypted);
if (encrypted_length == -1) {
+ free(encrypted);
+ return -1;
+ }
+
+ if (length < 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + encrypted_length) {
+ free(encrypted);
return -1;
}
@@ -487,15 +550,16 @@ static int dht_create_packet(const uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE],
memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, nonce, CRYPTO_NONCE_SIZE);
memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, encrypted, encrypted_length);
+ free(encrypted);
return 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + encrypted_length;
}
-/** Unpack IP_Port structure from data of max size length into ip_port.
+/** @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.
- * Return -1 on failure.
+ * @return size of unpacked ip_port on success.
+ * @retval -1 on failure.
*/
int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool tcp_enabled)
{
@@ -508,31 +572,29 @@ int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool
if (data[0] == TOX_AF_INET) {
is_ipv4 = true;
- host_family = net_family_ipv4;
+ host_family = net_family_ipv4();
} else if (data[0] == TOX_TCP_INET) {
if (!tcp_enabled) {
return -1;
}
is_ipv4 = true;
- host_family = net_family_tcp_ipv4;
+ host_family = net_family_tcp_ipv4();
} else if (data[0] == TOX_AF_INET6) {
is_ipv4 = false;
- host_family = net_family_ipv6;
+ host_family = net_family_ipv6();
} else if (data[0] == TOX_TCP_INET6) {
if (!tcp_enabled) {
return -1;
}
is_ipv4 = false;
- host_family = net_family_tcp_ipv6;
+ host_family = net_family_tcp_ipv6();
} else {
return -1;
}
- *ip_port = (IP_Port) {
- 0
- };
+ *ip_port = empty_ip_port;
if (is_ipv4) {
const uint32_t size = 1 + SIZE_IP4 + sizeof(uint16_t);
@@ -559,17 +621,17 @@ int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool
}
}
-/** Pack number of nodes into data of maxlength length.
+/** @brief Pack number of nodes into data of maxlength length.
*
- * return length of packed nodes on success.
- * return -1 on failure.
+ * @return length of packed nodes on success.
+ * @retval -1 on failure.
*/
-int pack_nodes(uint8_t *data, uint16_t length, const Node_format *nodes, uint16_t number)
+int pack_nodes(const Logger *logger, uint8_t *data, uint16_t length, const Node_format *nodes, uint16_t number)
{
uint32_t packed_length = 0;
for (uint32_t i = 0; i < number && packed_length < length; ++i) {
- const int ipp_size = pack_ip_port(data + packed_length, length - packed_length, &nodes[i].ip_port);
+ const int ipp_size = pack_ip_port(logger, data + packed_length, length - packed_length, &nodes[i].ip_port);
if (ipp_size == -1) {
return -1;
@@ -593,12 +655,12 @@ int pack_nodes(uint8_t *data, uint16_t length, const Node_format *nodes, uint16_
return packed_length;
}
-/** Unpack data of length into nodes of size max_num_nodes.
+/** @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.
- * return -1 on failure.
+ * @return number of unpacked nodes on success.
+ * @retval -1 on failure.
*/
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)
@@ -629,21 +691,24 @@ int unpack_nodes(Node_format *nodes, uint16_t max_num_nodes, uint16_t *processed
assert(increment == PACKED_NODE_SIZE_IP4 || increment == PACKED_NODE_SIZE_IP6);
}
- if (processed_data_len) {
+ if (processed_data_len != nullptr) {
*processed_data_len = len_processed;
}
return num;
}
-/** Find index in an array with public_key equal to pk.
+/** @brief Find index in an array with public_key equal to pk.
*
- * return index or UINT32_MAX if not found.
+ * @return index or UINT32_MAX if not found.
*/
+non_null(3) nullable(1)
static uint32_t index_of_client_pk(const Client_data *array, uint32_t size, const uint8_t *pk)
{
+ assert(size == 0 || array != nullptr);
+
for (uint32_t i = 0; i < size; ++i) {
- if (id_equal(array[i].public_key, pk)) {
+ if (pk_equal(array[i].public_key, pk)) {
return i;
}
}
@@ -651,10 +716,13 @@ static uint32_t index_of_client_pk(const Client_data *array, uint32_t size, cons
return UINT32_MAX;
}
+non_null(3) nullable(1)
static uint32_t index_of_friend_pk(const DHT_Friend *array, uint32_t size, const uint8_t *pk)
{
+ assert(size == 0 || array != nullptr);
+
for (uint32_t i = 0; i < size; ++i) {
- if (id_equal(array[i].public_key, pk)) {
+ if (pk_equal(array[i].public_key, pk)) {
return i;
}
}
@@ -662,10 +730,13 @@ static uint32_t index_of_friend_pk(const DHT_Friend *array, uint32_t size, const
return UINT32_MAX;
}
+non_null(3) nullable(1)
static uint32_t index_of_node_pk(const Node_format *array, uint32_t size, const uint8_t *pk)
{
+ assert(size == 0 || array != nullptr);
+
for (uint32_t i = 0; i < size; ++i) {
- if (id_equal(array[i].public_key, pk)) {
+ if (pk_equal(array[i].public_key, pk)) {
return i;
}
}
@@ -673,12 +744,15 @@ static uint32_t index_of_node_pk(const Node_format *array, uint32_t size, const
return UINT32_MAX;
}
-/** Find index of Client_data with ip_port equal to param ip_port.
+/** @brief Find index of Client_data with ip_port equal to param ip_port.
*
- * return index or UINT32_MAX if not found.
+ * @return index or UINT32_MAX if not found.
*/
+non_null(3) nullable(1)
static uint32_t index_of_client_ip_port(const Client_data *array, uint32_t size, const IP_Port *ip_port)
{
+ assert(size == 0 || array != nullptr);
+
for (uint32_t i = 0; i < size; ++i) {
if ((net_family_is_ipv4(ip_port->ip.family) && ipport_equal(&array[i].assoc4.ip_port, ip_port)) ||
(net_family_is_ipv6(ip_port->ip.family) && ipport_equal(&array[i].assoc6.ip_port, ip_port))) {
@@ -689,8 +763,8 @@ static uint32_t index_of_client_ip_port(const Client_data *array, uint32_t size,
return UINT32_MAX;
}
-/** Update ip_port of client if it's needed.
- */
+/** Update ip_port of client if it's needed. */
+non_null()
static void update_client(const Logger *log, const Mono_Time *mono_time, int index, Client_data *client,
const IP_Port *ip_port)
{
@@ -708,12 +782,13 @@ static void update_client(const Logger *log, const Mono_Time *mono_time, int ind
}
if (!ipport_equal(&assoc->ip_port, ip_port)) {
- char ip_str[IP_NTOA_LEN];
+ Ip_Ntoa ip_str_from;
+ Ip_Ntoa ip_str_to;
LOGGER_TRACE(log, "coipil[%u]: switching ipv%d from %s:%u to %s:%u",
index, ip_version,
- ip_ntoa(&assoc->ip_port.ip, ip_str, sizeof(ip_str)),
+ net_ip_ntoa(&assoc->ip_port.ip, &ip_str_from),
net_ntohs(assoc->ip_port.port),
- ip_ntoa(&ip_port->ip, ip_str, sizeof(ip_str)),
+ net_ip_ntoa(&ip_port->ip, &ip_str_to),
net_ntohs(ip_port->port));
}
@@ -725,15 +800,15 @@ static void update_client(const Logger *log, const Mono_Time *mono_time, int ind
assoc->timestamp = mono_time_get(mono_time);
}
-/** Check if client with public_key is already in list of length length.
+/** @brief Check if client with public_key is already in list of length length.
+ *
* If it is then set its corresponding timestamp to current time.
* If the id is already in the list with a different ip_port, update it.
* TODO(irungentoo): Maybe optimize this.
- *
- * return True(1) or False(0)
*/
-static int client_or_ip_port_in_list(const Logger *log, const Mono_Time *mono_time, Client_data *list, uint16_t length,
- const uint8_t *public_key, const IP_Port *ip_port)
+non_null()
+static bool client_or_ip_port_in_list(const Logger *log, const Mono_Time *mono_time, Client_data *list, uint16_t length,
+ const uint8_t *public_key, const IP_Port *ip_port)
{
const uint64_t temp_time = mono_time_get(mono_time);
uint32_t index = index_of_client_pk(list, length, public_key);
@@ -741,7 +816,7 @@ static int client_or_ip_port_in_list(const Logger *log, const Mono_Time *mono_ti
/* if public_key is in list, find it and maybe overwrite ip_port */
if (index != UINT32_MAX) {
update_client(log, mono_time, index, &list[index], ip_port);
- return 1;
+ return true;
}
/* public_key not in list yet: see if we can find an identical ip_port, in
@@ -752,7 +827,7 @@ static int client_or_ip_port_in_list(const Logger *log, const Mono_Time *mono_ti
index = index_of_client_ip_port(list, length, ip_port);
if (index == UINT32_MAX) {
- return 0;
+ return false;
}
IPPTsPng *assoc;
@@ -773,10 +848,9 @@ static int client_or_ip_port_in_list(const Logger *log, const Mono_Time *mono_ti
LOGGER_DEBUG(log, "coipil[%u]: switching public_key (ipv%d)", index, ip_version);
/* kill the other address, if it was set */
- *assoc = (IPPTsPng) {
- 0
- };
- return 1;
+ const IPPTsPng empty_ipptspng = {{{{0}}}};
+ *assoc = empty_ipptspng;
+ return true;
}
bool add_to_list(Node_format *nodes_list, uint32_t length, const uint8_t *pk, const IP_Port *ip_port,
@@ -802,11 +876,13 @@ bool add_to_list(Node_format *nodes_list, uint32_t length, const uint8_t *pk, co
}
/**
- * helper for get_close_nodes(). argument list is a monster :D
+ * helper for `get_close_nodes()`. argument list is a monster :D
*/
+non_null()
static void get_close_nodes_inner(uint64_t cur_time, const uint8_t *public_key, Node_format *nodes_list,
Family sa_family, const Client_data *client_list, uint32_t client_list_length,
- uint32_t *num_nodes_ptr, bool is_LAN)
+ uint32_t *num_nodes_ptr, bool is_LAN,
+ bool want_announce)
{
if (!net_family_is_ipv4(sa_family) && !net_family_is_ipv6(sa_family) && !net_family_is_unspec(sa_family)) {
return;
@@ -844,11 +920,21 @@ static void get_close_nodes_inner(uint64_t cur_time, const uint8_t *public_key,
continue;
}
+#ifdef CHECK_ANNOUNCE_NODE
+
+ if (want_announce && !client->announce_node) {
+ continue;
+ }
+
+#endif
+
if (num_nodes < MAX_SENT_NODES) {
memcpy(nodes_list[num_nodes].public_key, client->public_key, CRYPTO_PUBLIC_KEY_SIZE);
nodes_list[num_nodes].ip_port = ipptp->ip_port;
++num_nodes;
} else {
+ // TODO(zugz): this could be made significantly more efficient by
+ // using a version of add_to_list which works with a sorted list.
add_to_list(nodes_list, MAX_SENT_NODES, client->public_key, &ipptp->ip_port, public_key);
}
}
@@ -856,33 +942,35 @@ static void get_close_nodes_inner(uint64_t cur_time, const uint8_t *public_key,
*num_nodes_ptr = num_nodes;
}
-/** Find MAX_SENT_NODES nodes closest to the public_key for the send nodes request:
+/**
+ * Find MAX_SENT_NODES nodes closest to the public_key for the send nodes request:
* put them in the nodes_list and return how many were found.
*
- * TODO(irungentoo): For the love of based <your favorite deity, in doubt use
- * "love"> make this function cleaner and much more efficient.
+ * want_announce: return only nodes which implement the dht announcements protocol.
*/
+non_null()
static int get_somewhat_close_nodes(const DHT *dht, const uint8_t *public_key, Node_format *nodes_list,
- Family sa_family, bool is_LAN)
+ Family sa_family, bool is_LAN, bool want_announce)
{
uint32_t num_nodes = 0;
get_close_nodes_inner(dht->cur_time, public_key, nodes_list, sa_family,
- dht->close_clientlist, LCLIENT_LIST, &num_nodes, is_LAN);
+ dht->close_clientlist, LCLIENT_LIST, &num_nodes, is_LAN, want_announce);
for (uint32_t i = 0; i < dht->num_friends; ++i) {
get_close_nodes_inner(dht->cur_time, public_key, nodes_list, sa_family,
dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS,
- &num_nodes, is_LAN);
+ &num_nodes, is_LAN, want_announce);
}
return num_nodes;
}
int get_close_nodes(const DHT *dht, const uint8_t *public_key, Node_format *nodes_list, Family sa_family,
- bool is_LAN)
+ bool is_LAN, bool want_announce)
{
memset(nodes_list, 0, MAX_SENT_NODES * sizeof(Node_format));
- return get_somewhat_close_nodes(dht, public_key, nodes_list, sa_family, is_LAN);
+ return get_somewhat_close_nodes(dht, public_key, nodes_list, sa_family,
+ is_LAN, want_announce);
}
typedef struct DHT_Cmp_Data {
@@ -891,6 +979,7 @@ typedef struct DHT_Cmp_Data {
Client_data entry;
} DHT_Cmp_Data;
+non_null()
static int dht_cmp_entry(const void *a, const void *b)
{
const DHT_Cmp_Data *cmp1 = (const DHT_Cmp_Data *)a;
@@ -914,38 +1003,147 @@ static int dht_cmp_entry(const void *a, const void *b)
return 1;
}
- const int close = id_closest(cmp_public_key, entry1.public_key, entry2.public_key);
+ const int closest = id_closest(cmp_public_key, entry1.public_key, entry2.public_key);
- if (close == 1) {
+ if (closest == 1) {
return 1;
}
- if (close == 2) {
+ if (closest == 2) {
return -1;
}
return 0;
}
-/** Is it ok to store node with public_key in client.
+#ifdef CHECK_ANNOUNCE_NODE
+non_null()
+static void set_announce_node_in_list(Client_data *list, uint32_t list_len, const uint8_t *public_key)
+{
+ const uint32_t index = index_of_client_pk(list, list_len, public_key);
+
+ if (index != UINT32_MAX) {
+ list[index].announce_node = true;
+ }
+}
+
+void set_announce_node(DHT *dht, const uint8_t *public_key)
+{
+ unsigned int index = bit_by_bit_cmp(public_key, dht->self_public_key);
+
+ if (index >= LCLIENT_LENGTH) {
+ index = LCLIENT_LENGTH - 1;
+ }
+
+ set_announce_node_in_list(dht->close_clientlist + index * LCLIENT_NODES, LCLIENT_NODES, public_key);
+
+ for (int32_t i = 0; i < dht->num_friends; ++i) {
+ set_announce_node_in_list(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, public_key);
+ }
+}
+
+/** @brief Send data search request, searching for a random key. */
+non_null()
+static bool send_announce_ping(DHT *dht, const uint8_t *public_key, const IP_Port *ip_port)
+{
+ uint8_t plain[CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint64_t)];
+
+ uint8_t unused_secret_key[CRYPTO_SECRET_KEY_SIZE];
+ crypto_new_keypair(dht->rng, plain, unused_secret_key);
+
+ const uint64_t ping_id = ping_array_add(dht->dht_ping_array,
+ dht->mono_time,
+ dht->rng,
+ public_key, CRYPTO_PUBLIC_KEY_SIZE);
+ memcpy(plain + CRYPTO_PUBLIC_KEY_SIZE, &ping_id, sizeof(ping_id));
+
+ uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE];
+ dht_get_shared_key_sent(dht, shared_key, public_key);
+
+ uint8_t request[1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + sizeof(plain) + CRYPTO_MAC_SIZE];
+
+ if (dht_create_packet(dht->rng, dht->self_public_key, shared_key, NET_PACKET_DATA_SEARCH_REQUEST,
+ plain, sizeof(plain), request, sizeof(request)) != sizeof(request)) {
+ return false;
+ }
+
+ return sendpacket(dht->net, ip_port, request, sizeof(request)) == sizeof(request);
+}
+
+/** @brief If the response is valid, set the sender as an announce node. */
+non_null(1, 2, 3) nullable(5)
+static int handle_data_search_response(void *object, const IP_Port *source,
+ const uint8_t *packet, uint16_t length,
+ void *userdata)
+{
+ DHT *dht = (DHT *) object;
+
+ const int32_t plain_len = (int32_t)length - (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE);
+
+ if (plain_len < (int32_t)(CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint64_t))) {
+ return 1;
+ }
+
+ VLA(uint8_t, plain, plain_len);
+ const uint8_t *public_key = packet + 1;
+ uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE];
+ dht_get_shared_key_recv(dht, shared_key, public_key);
+
+ if (decrypt_data_symmetric(shared_key,
+ packet + 1 + CRYPTO_PUBLIC_KEY_SIZE,
+ packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE,
+ plain_len + CRYPTO_MAC_SIZE,
+ plain) != plain_len) {
+ return 1;
+ }
+
+ uint64_t ping_id;
+ memcpy(&ping_id, plain + (plain_len - sizeof(uint64_t)), sizeof(ping_id));
+
+ uint8_t ping_data[CRYPTO_PUBLIC_KEY_SIZE];
+
+ if (ping_array_check(dht->dht_ping_array,
+ dht->mono_time, ping_data,
+ sizeof(ping_data), ping_id) != sizeof(ping_data)) {
+ return 1;
+ }
+
+ if (!pk_equal(ping_data, public_key)) {
+ return 1;
+ }
+
+ set_announce_node(dht, public_key);
+
+ return 0;
+
+}
+#endif
+
+/** @brief Is it ok to store node with public_key in client.
*
- * return 0 if node can't be stored.
- * return 1 if it can.
+ * return false if node can't be stored.
+ * return true if it can.
*/
-static unsigned int store_node_ok(const Client_data *client, uint64_t cur_time, const uint8_t *public_key,
- const uint8_t *comp_public_key)
+non_null()
+static bool store_node_ok(const Client_data *client, uint64_t cur_time, const uint8_t *public_key,
+ const uint8_t *comp_public_key)
{
return (assoc_timeout(cur_time, &client->assoc4)
&& assoc_timeout(cur_time, &client->assoc6))
|| id_closest(comp_public_key, client->public_key, public_key) == 2;
}
+non_null()
static void sort_client_list(Client_data *list, uint64_t cur_time, unsigned int length,
const uint8_t *comp_public_key)
{
// Pass comp_public_key to qsort with each Client_data entry, so the
// comparison function can use it as the base of comparison.
- VLA(DHT_Cmp_Data, cmp_list, length);
+ DHT_Cmp_Data *cmp_list = (DHT_Cmp_Data *)calloc(length, sizeof(DHT_Cmp_Data));
+
+ if (cmp_list == nullptr) {
+ return;
+ }
for (uint32_t i = 0; i < length; ++i) {
cmp_list[i].cur_time = cur_time;
@@ -958,8 +1156,11 @@ static void sort_client_list(Client_data *list, uint64_t cur_time, unsigned int
for (uint32_t i = 0; i < length; ++i) {
list[i] = cmp_list[i].entry;
}
+
+ free(cmp_list);
}
+non_null()
static void update_client_with_reset(const Mono_Time *mono_time, Client_data *client, const IP_Port *ip_port)
{
IPPTsPng *ipptp_write = nullptr;
@@ -985,19 +1186,22 @@ static void update_client_with_reset(const Mono_Time *mono_time, Client_data *cl
memset(ipptp_clear, 0, sizeof(*ipptp_clear));
}
-/** Replace a first bad (or empty) node with this one
- * or replace a possibly bad node (tests failed or not done yet)
- * that is further than any other in the list
- * from the comp_public_key
- * or replace a good node that is further
- * than any other in the list from the comp_public_key
- * and further than public_key.
+/**
+ * Replace a first bad (or empty) node with this one
+ * or replace a possibly bad node (tests failed or not done yet)
+ * that is further than any other in the list
+ * from the comp_public_key
+ * or replace a good node that is further
+ * than any other in the list from the comp_public_key
+ * and further than public_key.
*
* Do not replace any node if the list has no bad or possibly bad nodes
- * and all nodes in the list are closer to comp_public_key
- * than public_key.
+ * and all nodes in the list are closer to comp_public_key
+ * than public_key.
*
- * returns true when the item was stored, false otherwise */
+ * @return true when the item was stored, false otherwise
+ */
+non_null()
static bool replace_all(const DHT *dht,
Client_data *list,
uint16_t length,
@@ -1017,20 +1221,21 @@ static bool replace_all(const DHT *dht,
sort_client_list(list, dht->cur_time, length, comp_public_key);
Client_data *const client = &list[0];
- id_copy(client->public_key, public_key);
+ pk_copy(client->public_key, public_key);
update_client_with_reset(dht->mono_time, client, ip_port);
return true;
}
-/** Add node to close list.
+/** @brief Add node to close list.
*
* simulate is set to 1 if we want to check if a node can be added to the list without adding it.
*
- * return -1 on failure.
- * return 0 on success.
+ * return false on failure.
+ * return true on success.
*/
-static int add_to_close(DHT *dht, const uint8_t *public_key, const IP_Port *ip_port, bool simulate)
+non_null()
+static bool add_to_close(DHT *dht, const uint8_t *public_key, const IP_Port *ip_port, bool simulate)
{
unsigned int index = bit_by_bit_cmp(public_key, dht->self_public_key);
@@ -1049,31 +1254,35 @@ static int add_to_close(DHT *dht, const uint8_t *public_key, const IP_Port *ip_p
}
if (simulate) {
- return 0;
+ return true;
}
- id_copy(client->public_key, public_key);
+ pk_copy(client->public_key, public_key);
update_client_with_reset(dht->mono_time, client, ip_port);
- return 0;
+#ifdef CHECK_ANNOUNCE_NODE
+ client->announce_node = false;
+ send_announce_ping(dht, public_key, ip_port);
+#endif
+ return true;
}
- return -1;
+ return false;
}
-/** Return 1 if node can be added to close list, 0 if it can't.
- */
+/** Return 1 if node can be added to close list, 0 if it can't. */
bool node_addable_to_close_list(DHT *dht, const uint8_t *public_key, const IP_Port *ip_port)
{
- return add_to_close(dht, public_key, ip_port, 1) == 0;
+ return add_to_close(dht, public_key, ip_port, true);
}
+non_null()
static bool is_pk_in_client_list(const Client_data *list, unsigned int client_list_length, uint64_t cur_time,
const uint8_t *public_key, const IP_Port *ip_port)
{
const uint32_t index = index_of_client_pk(list, client_list_length, public_key);
if (index == UINT32_MAX) {
- return 0;
+ return false;
}
const IPPTsPng *assoc = net_family_is_ipv4(ip_port->ip.family)
@@ -1083,6 +1292,7 @@ static bool is_pk_in_client_list(const Client_data *list, unsigned int client_li
return !assoc_timeout(cur_time, assoc);
}
+non_null()
static bool is_pk_in_close_list(const DHT *dht, const uint8_t *public_key, const IP_Port *ip_port)
{
unsigned int index = bit_by_bit_cmp(public_key, dht->self_public_key);
@@ -1095,17 +1305,19 @@ static bool is_pk_in_close_list(const DHT *dht, const uint8_t *public_key, const
ip_port);
}
-/** Check if the node obtained with a get_nodes with public_key should be pinged.
+/** @brief Check if the node obtained with a get_nodes with public_key should be pinged.
+ *
* NOTE: for best results call it after addto_lists.
*
* return false if the node should not be pinged.
* return true if it should.
*/
+non_null()
static bool ping_node_from_getnodes_ok(DHT *dht, const uint8_t *public_key, const IP_Port *ip_port)
{
bool ret = false;
- if (add_to_close(dht, public_key, ip_port, 1) == 0) {
+ if (add_to_close(dht, public_key, ip_port, true)) {
ret = true;
}
@@ -1161,10 +1373,10 @@ static bool ping_node_from_getnodes_ok(DHT *dht, const uint8_t *public_key, cons
return ret;
}
-/** Attempt to add client with ip_port and public_key to the friends client list
+/** @brief Attempt to add client with ip_port and public_key to the friends client list
* and close_clientlist.
*
- * returns 1+ if the item is used in any list, 0 else
+ * @return 1+ if the item is used in any list, 0 else
*/
uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key)
{
@@ -1179,7 +1391,7 @@ uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key
public_key, &ipp_copy);
/* add_to_close should be called only if !in_list (don't extract to variable) */
- if (in_close_list || add_to_close(dht, public_key, &ipp_copy, 0)) {
+ if (in_close_list || !add_to_close(dht, public_key, &ipp_copy, false)) {
++used;
}
@@ -1193,9 +1405,9 @@ uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key
if (in_list
|| replace_all(dht, dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, public_key, &ipp_copy,
dht->friends_list[i].public_key)) {
- DHT_Friend *dht_friend = &dht->friends_list[i];
+ const DHT_Friend *dht_friend = &dht->friends_list[i];
- if (id_equal(public_key, dht_friend->public_key)) {
+ if (pk_equal(public_key, dht_friend->public_key)) {
friend_foundip = dht_friend;
}
@@ -1203,12 +1415,12 @@ uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key
}
}
- if (!friend_foundip) {
+ if (friend_foundip == nullptr) {
return used;
}
for (uint32_t i = 0; i < friend_foundip->lock_count; ++i) {
- if (friend_foundip->callbacks[i].ip_callback) {
+ if (friend_foundip->callbacks[i].ip_callback != nullptr) {
friend_foundip->callbacks[i].ip_callback(friend_foundip->callbacks[i].data,
friend_foundip->callbacks[i].number, &ipp_copy);
}
@@ -1217,6 +1429,7 @@ uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key
return used;
}
+non_null()
static bool update_client_data(const Mono_Time *mono_time, Client_data *array, size_t size, const IP_Port *ip_port,
const uint8_t *pk, bool node_is_self)
{
@@ -1245,20 +1458,22 @@ static bool update_client_data(const Mono_Time *mono_time, Client_data *array, s
return true;
}
-/** If public_key is a friend or us, update ret_ip_port
+/**
+ * If public_key is a friend or us, update ret_ip_port
* nodepublic_key is the id of the node that sent us this info.
*/
+non_null()
static void returnedip_ports(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, const uint8_t *nodepublic_key)
{
IP_Port ipp_copy = ip_port_normalize(ip_port);
- if (id_equal(public_key, dht->self_public_key)) {
+ if (pk_equal(public_key, dht->self_public_key)) {
update_client_data(dht->mono_time, dht->close_clientlist, LCLIENT_LIST, &ipp_copy, nodepublic_key, true);
return;
}
for (uint32_t i = 0; i < dht->num_friends; ++i) {
- if (id_equal(public_key, dht->friends_list[i].public_key)) {
+ if (pk_equal(public_key, dht->friends_list[i].public_key)) {
Client_data *const client_list = dht->friends_list[i].client_list;
if (update_client_data(dht->mono_time, client_list, MAX_FRIEND_CLIENTS, &ipp_copy, nodepublic_key, false)) {
@@ -1271,7 +1486,7 @@ static void returnedip_ports(DHT *dht, const IP_Port *ip_port, const uint8_t *pu
bool dht_getnodes(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, const uint8_t *client_id)
{
/* Check if packet is going to be sent to ourself. */
- if (id_equal(public_key, dht->self_public_key)) {
+ if (pk_equal(public_key, dht->self_public_key)) {
return false;
}
@@ -1281,15 +1496,16 @@ bool dht_getnodes(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, c
memcpy(receiver.public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
receiver.ip_port = *ip_port;
- if (pack_nodes(plain_message, sizeof(plain_message), &receiver, 1) == -1) {
+ if (pack_nodes(dht->log, plain_message, sizeof(plain_message), &receiver, 1) == -1) {
return false;
}
uint64_t ping_id = 0;
- ping_id = ping_array_add(dht->dht_ping_array, dht->mono_time, plain_message, sizeof(receiver));
+ ping_id = ping_array_add(dht->dht_ping_array, dht->mono_time, dht->rng, plain_message, sizeof(receiver));
if (ping_id == 0) {
+ LOGGER_ERROR(dht->log, "adding ping id failed");
return false;
}
@@ -1302,28 +1518,27 @@ bool dht_getnodes(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, c
uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE];
dht_get_shared_key_sent(dht, shared_key, public_key);
- const int len = dht_create_packet(dht->self_public_key, shared_key, NET_PACKET_GET_NODES,
- plain, sizeof(plain), data);
+ const int len = dht_create_packet(dht->rng,
+ dht->self_public_key, shared_key, NET_PACKET_GET_NODES,
+ plain, sizeof(plain), data, sizeof(data));
crypto_memzero(shared_key, sizeof(shared_key));
if (len != sizeof(data)) {
+ LOGGER_ERROR(dht->log, "getnodes packet encryption failed");
return false;
}
- if (sendpacket(dht->net, ip_port, data, len) > 0) {
- return true;
- }
-
- return false;
+ return sendpacket(dht->net, ip_port, data, len) > 0;
}
/** Send a send nodes response: message for IPv6 nodes */
+non_null()
static int sendnodes_ipv6(const DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, const uint8_t *client_id,
const uint8_t *sendback_data, uint16_t length, const uint8_t *shared_encryption_key)
{
/* Check if packet is going to be sent to ourself. */
- if (id_equal(public_key, dht->self_public_key)) {
+ if (pk_equal(public_key, dht->self_public_key)) {
return -1;
}
@@ -1335,14 +1550,14 @@ static int sendnodes_ipv6(const DHT *dht, const IP_Port *ip_port, const uint8_t
Node_format nodes_list[MAX_SENT_NODES];
const uint32_t num_nodes =
- get_close_nodes(dht, client_id, nodes_list, net_family_unspec, ip_is_lan(&ip_port->ip));
+ get_close_nodes(dht, client_id, nodes_list, net_family_unspec(), ip_is_lan(&ip_port->ip), false);
VLA(uint8_t, plain, 1 + node_format_size * MAX_SENT_NODES + length);
int nodes_length = 0;
- if (num_nodes) {
- nodes_length = pack_nodes(plain + 1, node_format_size * MAX_SENT_NODES, nodes_list, num_nodes);
+ if (num_nodes > 0) {
+ nodes_length = pack_nodes(dht->log, plain + 1, node_format_size * MAX_SENT_NODES, nodes_list, num_nodes);
if (nodes_length <= 0) {
return -1;
@@ -1355,8 +1570,9 @@ static int sendnodes_ipv6(const DHT *dht, const IP_Port *ip_port, const uint8_t
const uint32_t crypto_size = 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE;
VLA(uint8_t, data, 1 + nodes_length + length + crypto_size);
- const int len = dht_create_packet(dht->self_public_key, shared_encryption_key, NET_PACKET_SEND_NODES_IPV6,
- plain, 1 + nodes_length + length, data);
+ const int len = dht_create_packet(dht->rng,
+ dht->self_public_key, shared_encryption_key, NET_PACKET_SEND_NODES_IPV6,
+ plain, 1 + nodes_length + length, data, SIZEOF_VLA(data));
if (len != SIZEOF_VLA(data)) {
return -1;
@@ -1367,17 +1583,18 @@ static int sendnodes_ipv6(const DHT *dht, const IP_Port *ip_port, const uint8_t
#define CRYPTO_NODE_SIZE (CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint64_t))
+non_null()
static int handle_getnodes(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata)
{
if (length != (CRYPTO_SIZE + CRYPTO_MAC_SIZE + sizeof(uint64_t))) {
- return true;
+ return 1;
}
DHT *const dht = (DHT *)object;
/* Check if packet is from ourself. */
- if (id_equal(packet + 1, dht->self_public_key)) {
- return true;
+ if (pk_equal(packet + 1, dht->self_public_key)) {
+ return 1;
}
uint8_t plain[CRYPTO_NODE_SIZE];
@@ -1393,7 +1610,7 @@ static int handle_getnodes(void *object, const IP_Port *source, const uint8_t *p
if (len != CRYPTO_NODE_SIZE) {
crypto_memzero(shared_key, sizeof(shared_key));
- return true;
+ return 1;
}
sendnodes_ipv6(dht, source, packet + 1, plain, plain + CRYPTO_PUBLIC_KEY_SIZE, sizeof(uint64_t), shared_key);
@@ -1402,10 +1619,11 @@ static int handle_getnodes(void *object, const IP_Port *source, const uint8_t *p
crypto_memzero(shared_key, sizeof(shared_key));
- return false;
+ return 0;
}
/** Return true if we sent a getnode packet to the peer associated with the supplied info. */
+non_null()
static bool sent_getnode_to_node(DHT *dht, const uint8_t *public_key, const IP_Port *node_ip_port, uint64_t ping_id)
{
uint8_t data[sizeof(Node_format) * 2];
@@ -1420,31 +1638,28 @@ static bool sent_getnode_to_node(DHT *dht, const uint8_t *public_key, const IP_P
return false;
}
- if (!ipport_equal(&test.ip_port, node_ip_port) || !id_equal(test.public_key, public_key)) {
- return false;
- }
-
- return true;
+ return ipport_equal(&test.ip_port, node_ip_port) && pk_equal(test.public_key, public_key);
}
-static int handle_sendnodes_core(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
- Node_format *plain_nodes, uint16_t size_plain_nodes, uint32_t *num_nodes_out)
+non_null()
+static bool handle_sendnodes_core(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
+ Node_format *plain_nodes, uint16_t size_plain_nodes, uint32_t *num_nodes_out)
{
DHT *const dht = (DHT *)object;
const uint32_t cid_size = 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + 1 + sizeof(uint64_t) + CRYPTO_MAC_SIZE;
if (length < cid_size) { /* too short */
- return 1;
+ return false;
}
const uint32_t data_size = length - cid_size;
if (data_size == 0) {
- return 1;
+ return false;
}
if (data_size > sizeof(Node_format) * MAX_SENT_NODES) { /* invalid length */
- return 1;
+ return false;
}
VLA(uint8_t, plain, 1 + data_size + sizeof(uint64_t));
@@ -1460,33 +1675,33 @@ static int handle_sendnodes_core(void *object, const IP_Port *source, const uint
crypto_memzero(shared_key, sizeof(shared_key));
if ((unsigned int)len != SIZEOF_VLA(plain)) {
- return 1;
+ return false;
}
if (plain[0] > size_plain_nodes) {
- return 1;
+ return false;
}
uint64_t ping_id;
memcpy(&ping_id, plain + 1 + data_size, sizeof(ping_id));
if (!sent_getnode_to_node(dht, packet + 1, source, ping_id)) {
- return 1;
+ return false;
}
uint16_t length_nodes = 0;
- const int num_nodes = unpack_nodes(plain_nodes, plain[0], &length_nodes, plain + 1, data_size, 0);
+ const int num_nodes = unpack_nodes(plain_nodes, plain[0], &length_nodes, plain + 1, data_size, false);
if (length_nodes != data_size) {
- return 1;
+ return false;
}
if (num_nodes != plain[0]) {
- return 1;
+ return false;
}
if (num_nodes < 0) {
- return 1;
+ return false;
}
/* store the address the *request* was sent to */
@@ -1494,9 +1709,10 @@ static int handle_sendnodes_core(void *object, const IP_Port *source, const uint
*num_nodes_out = num_nodes;
- return 0;
+ return true;
}
+non_null()
static int handle_sendnodes_ipv6(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
@@ -1504,7 +1720,7 @@ static int handle_sendnodes_ipv6(void *object, const IP_Port *source, const uint
Node_format plain_nodes[MAX_SENT_NODES];
uint32_t num_nodes;
- if (handle_sendnodes_core(object, source, packet, length, plain_nodes, MAX_SENT_NODES, &num_nodes)) {
+ if (!handle_sendnodes_core(object, source, packet, length, plain_nodes, MAX_SENT_NODES, &num_nodes)) {
return 1;
}
@@ -1516,6 +1732,10 @@ static int handle_sendnodes_ipv6(void *object, const IP_Port *source, const uint
if (ipport_isset(&plain_nodes[i].ip_port)) {
ping_node_from_getnodes_ok(dht, plain_nodes[i].public_key, &plain_nodes[i].ip_port);
returnedip_ports(dht, &plain_nodes[i].ip_port, plain_nodes[i].public_key, packet + 1);
+
+ if (dht->get_nodes_response != nullptr) {
+ dht->get_nodes_response(dht, &plain_nodes[i], userdata);
+ }
}
}
@@ -1525,6 +1745,7 @@ static int handle_sendnodes_ipv6(void *object, const IP_Port *source, const uint
/*----------------------------------------------------------------------------------*/
/*------------------------END of packet handling functions--------------------------*/
+non_null(1) nullable(2, 3, 5)
static void dht_friend_lock(DHT_Friend *const dht_friend, dht_ip_cb *ip_callback,
void *data, int32_t number, uint16_t *lock_count)
{
@@ -1534,7 +1755,7 @@ static void dht_friend_lock(DHT_Friend *const dht_friend, dht_ip_cb *ip_callback
dht_friend->callbacks[lock_num].data = data;
dht_friend->callbacks[lock_num].number = number;
- if (lock_count) {
+ if (lock_count != nullptr) {
*lock_count = lock_num + 1;
}
}
@@ -1564,16 +1785,16 @@ int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback,
dht->friends_list = temp;
DHT_Friend *const dht_friend = &dht->friends_list[dht->num_friends];
- memset(dht_friend, 0, sizeof(DHT_Friend));
+ *dht_friend = empty_dht_friend;
memcpy(dht_friend->public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
- dht_friend->nat.nat_ping_id = random_u64();
+ dht_friend->nat.nat_ping_id = random_u64(dht->rng);
++dht->num_friends;
dht_friend_lock(dht_friend, ip_callback, data, number, lock_count);
- dht_friend->num_to_bootstrap = get_close_nodes(dht, dht_friend->public_key, dht_friend->to_bootstrap, net_family_unspec,
- 1);
+ dht_friend->num_to_bootstrap = get_close_nodes(dht, dht_friend->public_key, dht_friend->to_bootstrap, net_family_unspec(),
+ true, false);
return 0;
}
@@ -1589,7 +1810,7 @@ int dht_delfriend(DHT *dht, const uint8_t *public_key, uint16_t lock_count)
DHT_Friend *const dht_friend = &dht->friends_list[friend_num];
--dht_friend->lock_count;
- if (dht_friend->lock_count && lock_count) { /* DHT friend is still in use.*/
+ if (dht_friend->lock_count > 0 && lock_count > 0) { /* DHT friend is still in use.*/
--lock_count;
dht_friend->callbacks[lock_count].ip_callback = nullptr;
dht_friend->callbacks[lock_count].data = nullptr;
@@ -1641,7 +1862,7 @@ int dht_getfriendip(const DHT *dht, const uint8_t *public_key, IP_Port *ip_port)
const Client_data *const client = &frnd->client_list[client_index];
const IPPTsPng *const assocs[] = { &client->assoc6, &client->assoc4, nullptr };
- for (const IPPTsPng * const *it = assocs; *it; ++it) {
+ for (const IPPTsPng * const *it = assocs; *it != nullptr; ++it) {
const IPPTsPng *const assoc = *it;
if (!assoc_timeout(dht->cur_time, assoc)) {
@@ -1654,6 +1875,7 @@ int dht_getfriendip(const DHT *dht, const uint8_t *public_key, IP_Port *ip_port)
}
/** returns number of nodes not in kill-timeout */
+non_null()
static uint8_t do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, const uint8_t *public_key,
Client_data *list, uint32_t list_count, uint32_t *bootstrap_times, bool sortable)
{
@@ -1661,11 +1883,17 @@ static uint8_t do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, co
const uint64_t temp_time = mono_time_get(dht->mono_time);
uint32_t num_nodes = 0;
- VLA(Client_data *, client_list, list_count * 2);
- VLA(IPPTsPng *, assoc_list, list_count * 2);
+ Client_data **client_list = (Client_data **)calloc(list_count * 2, sizeof(Client_data *));
+ IPPTsPng **assoc_list = (IPPTsPng **)calloc(list_count * 2, sizeof(IPPTsPng *));
unsigned int sort = 0;
bool sort_ok = false;
+ if (client_list == nullptr || assoc_list == nullptr) {
+ free(assoc_list);
+ free(client_list);
+ return 0;
+ }
+
for (uint32_t i = 0; i < list_count; ++i) {
/* If node is not dead. */
Client_data *client = &list[i];
@@ -1705,12 +1933,12 @@ static uint8_t do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, co
sort_client_list(list, dht->cur_time, list_count, public_key);
}
- if ((num_nodes != 0) && (mono_time_is_timeout(dht->mono_time, *lastgetnode, GET_NODE_INTERVAL)
- || *bootstrap_times < MAX_BOOTSTRAP_TIMES)) {
- uint32_t rand_node = random_range_u32(num_nodes);
+ if (num_nodes > 0 && (mono_time_is_timeout(dht->mono_time, *lastgetnode, GET_NODE_INTERVAL)
+ || *bootstrap_times < MAX_BOOTSTRAP_TIMES)) {
+ uint32_t rand_node = random_range_u32(dht->rng, num_nodes);
if ((num_nodes - 1) != rand_node) {
- rand_node += random_range_u32(num_nodes - (rand_node + 1));
+ rand_node += random_range_u32(dht->rng, num_nodes - (rand_node + 1));
}
dht_getnodes(dht, &assoc_list[rand_node]->ip_port, client_list[rand_node]->public_key, public_key);
@@ -1719,12 +1947,17 @@ static uint8_t do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, co
++*bootstrap_times;
}
+ free(assoc_list);
+ free(client_list);
return not_kill;
}
-/** Ping each client in the "friends" list every PING_INTERVAL seconds. Send a get nodes request
- * every GET_NODE_INTERVAL seconds to a random good node for each "friend" in our "friends" list.
+/** @brief Ping each client in the "friends" list every PING_INTERVAL seconds.
+ *
+ * Send a get nodes request every GET_NODE_INTERVAL seconds to a random good
+ * node for each "friend" in our "friends" list.
*/
+non_null()
static void do_dht_friends(DHT *dht)
{
for (size_t i = 0; i < dht->num_friends; ++i) {
@@ -1738,13 +1971,15 @@ static void do_dht_friends(DHT *dht)
do_ping_and_sendnode_requests(dht, &dht_friend->lastgetnode, dht_friend->public_key, dht_friend->client_list,
MAX_FRIEND_CLIENTS,
- &dht_friend->bootstrap_times, 1);
+ &dht_friend->bootstrap_times, true);
}
}
-/** Ping each client in the close nodes list every PING_INTERVAL seconds.
+/** @brief Ping each client in the close nodes list every PING_INTERVAL seconds.
+ *
* Send a get nodes request every GET_NODE_INTERVAL seconds to a random good node in the list.
*/
+non_null()
static void do_Close(DHT *dht)
{
for (size_t i = 0; i < dht->num_to_bootstrap; ++i) {
@@ -1753,9 +1988,9 @@ static void do_Close(DHT *dht)
dht->num_to_bootstrap = 0;
- uint8_t not_killed = do_ping_and_sendnode_requests(
- dht, &dht->close_lastgetnodes, dht->self_public_key, dht->close_clientlist, LCLIENT_LIST, &dht->close_bootstrap_times,
- 0);
+ const uint8_t not_killed = do_ping_and_sendnode_requests(
+ dht, &dht->close_lastgetnodes, dht->self_public_key, dht->close_clientlist, LCLIENT_LIST, &dht->close_bootstrap_times,
+ false);
if (not_killed != 0) {
return;
@@ -1774,22 +2009,27 @@ static void do_Close(DHT *dht)
IPPTsPng *const assocs[] = { &client->assoc6, &client->assoc4, nullptr };
- for (IPPTsPng * const *it = assocs; *it; ++it) {
+ for (IPPTsPng * const *it = assocs; *it != nullptr; ++it) {
IPPTsPng *const assoc = *it;
- if (assoc->timestamp) {
+ if (assoc->timestamp != 0) {
assoc->timestamp = badonly;
}
}
}
}
-void dht_bootstrap(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key)
+bool dht_bootstrap(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key)
{
- dht_getnodes(dht, ip_port, public_key, dht->self_public_key);
+ if (pk_equal(public_key, dht->self_public_key)) {
+ // Bootstrapping off ourselves is ok (onion paths are still set up).
+ return true;
+ }
+
+ return dht_getnodes(dht, ip_port, public_key, dht->self_public_key);
}
-int dht_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enabled,
+int dht_bootstrap_from_address(DHT *dht, const char *address, bool ipv6enabled,
uint16_t port, const uint8_t *public_key)
{
IP_Port ip_port_v64;
@@ -1799,12 +2039,12 @@ int dht_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enable
if (ipv6enabled) {
/* setup for getting BOTH: an IPv6 AND an IPv4 address */
- ip_port_v64.ip.family = net_family_unspec;
+ ip_port_v64.ip.family = net_family_unspec();
ip_reset(&ip_port_v4.ip);
ip_extra = &ip_port_v4.ip;
}
- if (addr_resolve_or_parse_ip(address, &ip_port_v64.ip, ip_extra)) {
+ if (addr_resolve_or_parse_ip(dht->ns, address, &ip_port_v64.ip, ip_extra)) {
ip_port_v64.port = port;
dht_bootstrap(dht, &ip_port_v64, public_key);
@@ -1819,18 +2059,19 @@ int dht_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enable
return 0;
}
-/** Send the given packet to node with public_key.
+/** @brief Send the given packet to node with public_key.
*
- * return -1 if failure.
+ * @return number of bytes sent.
+ * @retval -1 if failure.
*/
int route_packet(const DHT *dht, const uint8_t *public_key, const uint8_t *packet, uint16_t length)
{
for (uint32_t i = 0; i < LCLIENT_LIST; ++i) {
- if (id_equal(public_key, dht->close_clientlist[i].public_key)) {
+ if (pk_equal(public_key, dht->close_clientlist[i].public_key)) {
const Client_data *const client = &dht->close_clientlist[i];
const IPPTsPng *const assocs[] = { &client->assoc6, &client->assoc4, nullptr };
- for (const IPPTsPng * const *it = assocs; *it; ++it) {
+ for (const IPPTsPng * const *it = assocs; *it != nullptr; ++it) {
const IPPTsPng *const assoc = *it;
if (ip_isset(&assoc->ip_port.ip)) {
@@ -1845,13 +2086,15 @@ int route_packet(const DHT *dht, const uint8_t *public_key, const uint8_t *packe
return -1;
}
-/** Puts all the different ips returned by the nodes for a friend_num into array ip_portlist.
+/** @brief Puts all the different ips returned by the nodes for a friend_num into array ip_portlist.
+ *
* ip_portlist must be at least MAX_FRIEND_CLIENTS big.
*
- * return the number of ips returned.
- * return 0 if we are connected to friend or if no ips were found.
- * return -1 if no such friend.
+ * @return the number of ips returned.
+ * @retval 0 if we are connected to friend or if no ips were found.
+ * @retval -1 if no such friend.
*/
+non_null()
static int friend_iplist(const DHT *dht, IP_Port *ip_portlist, uint16_t friend_num)
{
if (friend_num >= dht->num_friends) {
@@ -1880,7 +2123,7 @@ static int friend_iplist(const DHT *dht, IP_Port *ip_portlist, uint16_t friend_n
++num_ipv6s;
}
- if (id_equal(client->public_key, dht_friend->public_key)) {
+ if (pk_equal(client->public_key, dht_friend->public_key)) {
if (!assoc_timeout(dht->cur_time, &client->assoc6)
|| !assoc_timeout(dht->cur_time, &client->assoc4)) {
return 0; /* direct connectivity */
@@ -1946,6 +2189,7 @@ typedef bool foreach_ip_port_cb(const DHT *dht, const IP_Port *ip_port, uint32_t
* @param callback The callback to invoke for each IP/port.
* @param userdata Extra pointer passed to the callback.
*/
+non_null()
static uint32_t foreach_ip_port(const DHT *dht, const DHT_Friend *dht_friend,
foreach_ip_port_cb *callback, void *userdata)
{
@@ -1977,6 +2221,7 @@ static uint32_t foreach_ip_port(const DHT *dht, const DHT_Friend *dht_friend,
return n;
}
+non_null()
static bool send_packet_to_friend(const DHT *dht, const IP_Port *ip_port, uint32_t *n, void *userdata)
{
const Packet *packet = (const Packet *)userdata;
@@ -1994,8 +2239,8 @@ static bool send_packet_to_friend(const DHT *dht, const IP_Port *ip_port, uint32
/**
* 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).
+ * @return ip for friend.
+ * @return number of nodes the packet was sent to. (Only works if more than (MAX_FRIEND_CLIENTS / 4).
*/
uint32_t route_to_friend(const DHT *dht, const uint8_t *friend_id, const Packet *packet)
{
@@ -2019,6 +2264,7 @@ uint32_t route_to_friend(const DHT *dht, const uint8_t *friend_id, const Packet
return foreach_ip_port(dht, dht_friend, send_packet_to_friend, &packet_userdata);
}
+non_null()
static bool get_ip_port(const DHT *dht, const IP_Port *ip_port, uint32_t *n, void *userdata)
{
IP_Port *ip_list = (IP_Port *)userdata;
@@ -2027,10 +2273,11 @@ static bool get_ip_port(const DHT *dht, const IP_Port *ip_port, uint32_t *n, voi
return true;
}
-/** Send the following packet to one random person who tells us they are connected to friend_id.
+/** @brief Send the following packet to one random person who tells us they are connected to friend_id.
*
- * return number of nodes the packet was sent to.
+ * @return number of nodes the packet was sent to.
*/
+non_null()
static uint32_t routeone_to_friend(const DHT *dht, const uint8_t *friend_id, const Packet *packet)
{
const uint32_t num = index_of_friend_pk(dht->friends_list, dht->num_friends, friend_id);
@@ -2049,7 +2296,7 @@ static uint32_t routeone_to_friend(const DHT *dht, const uint8_t *friend_id, con
return 0;
}
- const uint32_t rand_idx = random_range_u32(n);
+ const uint32_t rand_idx = random_range_u32(dht->rng, n);
const int retval = send_packet(dht->net, &ip_list[rand_idx], *packet);
if ((unsigned int)retval == packet->length) {
@@ -2062,6 +2309,7 @@ static uint32_t routeone_to_friend(const DHT *dht, const uint8_t *friend_id, con
/*----------------------------------------------------------------------------------*/
/*---------------------BEGINNING OF NAT PUNCHING FUNCTIONS--------------------------*/
+non_null()
static int send_NATping(const DHT *dht, const uint8_t *public_key, uint64_t ping_id, uint8_t type)
{
uint8_t data[sizeof(uint64_t) + 1];
@@ -2071,8 +2319,8 @@ static int send_NATping(const DHT *dht, const uint8_t *public_key, uint64_t ping
memcpy(data + 1, &ping_id, sizeof(uint64_t));
/* 254 is NAT ping request packet id */
const int len = create_request(
- dht->self_public_key, dht->self_secret_key, packet_data, public_key, data,
- sizeof(uint64_t) + 1, CRYPTO_PACKET_NAT_PING);
+ dht->rng, dht->self_public_key, dht->self_secret_key, packet_data, public_key,
+ data, sizeof(uint64_t) + 1, CRYPTO_PACKET_NAT_PING);
if (len == -1) {
return -1;
@@ -2096,6 +2344,7 @@ static int send_NATping(const DHT *dht, const uint8_t *public_key, uint64_t ping
}
/** Handle a received ping request for. */
+non_null()
static int handle_NATping(void *object, const IP_Port *source, const uint8_t *source_pubkey, const uint8_t *packet,
uint16_t length, void *userdata)
{
@@ -2107,7 +2356,7 @@ static int handle_NATping(void *object, const IP_Port *source, const uint8_t *so
uint64_t ping_id;
memcpy(&ping_id, packet + 1, sizeof(uint64_t));
- uint32_t friendnumber = index_of_friend_pk(dht->friends_list, dht->num_friends, source_pubkey);
+ const uint32_t friendnumber = index_of_friend_pk(dht->friends_list, dht->num_friends, source_pubkey);
if (friendnumber == UINT32_MAX) {
return 1;
@@ -2124,8 +2373,8 @@ static int handle_NATping(void *object, const IP_Port *source, const uint8_t *so
if (packet[0] == NAT_PING_RESPONSE) {
if (dht_friend->nat.nat_ping_id == ping_id) {
- dht_friend->nat.nat_ping_id = random_u64();
- dht_friend->nat.hole_punching = 1;
+ dht_friend->nat.nat_ping_id = random_u64(dht->rng);
+ dht_friend->nat.hole_punching = true;
return 0;
}
}
@@ -2133,12 +2382,13 @@ static int handle_NATping(void *object, const IP_Port *source, const uint8_t *so
return 1;
}
-/** Get the most common ip in the ip_portlist.
+/** @brief Get the most common ip in the ip_portlist.
* Only return ip if it appears in list min_num or more.
* len must not be bigger than MAX_FRIEND_CLIENTS.
*
- * return ip of 0 if failure.
+ * @return ip of 0 if failure.
*/
+non_null()
static IP nat_commonip(const IP_Port *ip_portlist, uint16_t len, uint16_t min_num)
{
IP zero;
@@ -2165,12 +2415,13 @@ static IP nat_commonip(const IP_Port *ip_portlist, uint16_t len, uint16_t min_nu
return zero;
}
-/** Return all the ports for one ip in a list.
+/** @brief Return all the ports for one ip in a list.
* portlist must be at least len long,
* where len is the length of ip_portlist.
*
- * return number of ports and puts the list of ports in portlist.
+ * @return number of ports and puts the list of ports in portlist.
*/
+non_null()
static uint16_t nat_getports(uint16_t *portlist, const IP_Port *ip_portlist, uint16_t len, const IP *ip)
{
uint16_t num = 0;
@@ -2185,6 +2436,7 @@ static uint16_t nat_getports(uint16_t *portlist, const IP_Port *ip_portlist, uin
return num;
}
+non_null()
static void punch_holes(DHT *dht, const IP *ip, const uint16_t *port_list, uint16_t numports, uint16_t friend_num)
{
if (!dht->hole_punching_enabled) {
@@ -2196,24 +2448,25 @@ static void punch_holes(DHT *dht, const IP *ip, const uint16_t *port_list, uint1
}
const uint16_t first_port = port_list[0];
- uint32_t i;
+ uint16_t port_candidate;
- for (i = 0; i < numports; ++i) {
- if (first_port != port_list[i]) {
+ for (port_candidate = 0; port_candidate < numports; ++port_candidate) {
+ if (first_port != port_list[port_candidate]) {
break;
}
}
- if (i == numports) { /* If all ports are the same, only try that one port. */
+ if (port_candidate == numports) { /* If all ports are the same, only try that one port. */
IP_Port pinging;
ip_copy(&pinging.ip, ip);
pinging.port = net_htons(first_port);
ping_send_request(dht->ping, &pinging, dht->friends_list[friend_num].public_key);
} else {
+ uint16_t i;
for (i = 0; i < MAX_PUNCHING_PORTS; ++i) {
/* TODO(irungentoo): Improve port guessing algorithm. */
const uint32_t it = i + dht->friends_list[friend_num].nat.punching_index;
- const int8_t sign = (it % 2) ? -1 : 1;
+ const int8_t sign = (it % 2 != 0) ? -1 : 1;
const uint32_t delta = sign * (it / (2 * numports));
const uint32_t index = (it / 2) % numports;
const uint16_t port = port_list[index] + delta;
@@ -2227,12 +2480,13 @@ static void punch_holes(DHT *dht, const IP *ip, const uint16_t *port_list, uint1
}
if (dht->friends_list[friend_num].nat.tries > MAX_NORMAL_PUNCHING_TRIES) {
- const uint16_t port = 1024;
IP_Port pinging;
ip_copy(&pinging.ip, ip);
+ uint16_t i;
for (i = 0; i < MAX_PUNCHING_PORTS; ++i) {
uint32_t it = i + dht->friends_list[friend_num].nat.punching_index2;
+ const uint16_t port = 1024;
pinging.port = net_htons(port + it);
ping_send_request(dht->ping, &pinging, dht->friends_list[friend_num].public_key);
}
@@ -2243,6 +2497,7 @@ static void punch_holes(DHT *dht, const IP *ip, const uint16_t *port_list, uint1
++dht->friends_list[friend_num].nat.tries;
}
+non_null()
static void do_NAT(DHT *dht)
{
const uint64_t temp_time = mono_time_get(dht->mono_time);
@@ -2261,7 +2516,7 @@ static void do_NAT(DHT *dht)
dht->friends_list[i].nat.nat_ping_timestamp = temp_time;
}
- if (dht->friends_list[i].nat.hole_punching == 1 &&
+ if (dht->friends_list[i].nat.hole_punching &&
dht->friends_list[i].nat.punching_timestamp + PUNCH_INTERVAL < temp_time &&
dht->friends_list[i].nat.recv_nat_ping_timestamp + PUNCH_INTERVAL * 2 >= temp_time) {
@@ -2282,7 +2537,7 @@ static void do_NAT(DHT *dht)
punch_holes(dht, &ip, port_list, numports, i);
dht->friends_list[i].nat.punching_timestamp = temp_time;
- dht->friends_list[i].nat.hole_punching = 0;
+ dht->friends_list[i].nat.hole_punching = false;
}
}
}
@@ -2290,12 +2545,13 @@ static void do_NAT(DHT *dht)
/*----------------------------------------------------------------------------------*/
/*-----------------------END OF NAT PUNCHING FUNCTIONS------------------------------*/
-/** Put up to max_num nodes in nodes from the closelist.
+/** @brief Put up to max_num nodes in nodes from the closelist.
*
- * return the number of nodes.
+ * @return the number of nodes.
*/
-static uint16_t list_nodes(const Client_data *list, size_t length, uint64_t cur_time,
- Node_format *nodes, uint16_t max_num)
+non_null()
+static uint16_t list_nodes(const Random *rng, const Client_data *list, size_t length,
+ uint64_t cur_time, Node_format *nodes, uint16_t max_num)
{
if (max_num == 0) {
return 0;
@@ -2313,7 +2569,7 @@ static uint16_t list_nodes(const Client_data *list, size_t length, uint64_t cur_
if (!assoc_timeout(cur_time, &list[i - 1].assoc6)) {
if (assoc == nullptr) {
assoc = &list[i - 1].assoc6;
- } else if (random_u08() % 2) {
+ } else if ((random_u08(rng) % 2) != 0) {
assoc = &list[i - 1].assoc6;
}
}
@@ -2332,9 +2588,12 @@ static uint16_t list_nodes(const Client_data *list, size_t length, uint64_t cur_
return count;
}
-/** Put up to max_num nodes in nodes from the random friends.
+/** @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.
+ * @return the number of nodes.
*/
uint16_t randfriends_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num)
{
@@ -2343,10 +2602,14 @@ uint16_t randfriends_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num)
}
uint16_t count = 0;
- const uint32_t r = random_u32();
+ const uint32_t r = random_u32(dht->rng);
+
+ assert(DHT_FAKE_FRIEND_NUMBER <= dht->num_friends);
- for (size_t i = 0; i < DHT_FAKE_FRIEND_NUMBER; ++i) {
- count += list_nodes(dht->friends_list[(i + r) % DHT_FAKE_FRIEND_NUMBER].client_list, MAX_FRIEND_CLIENTS, dht->cur_time,
+ // Only gather nodes from the initial 2 fake friends.
+ for (uint32_t i = 0; i < DHT_FAKE_FRIEND_NUMBER; ++i) {
+ count += list_nodes(dht->rng, dht->friends_list[(i + r) % DHT_FAKE_FRIEND_NUMBER].client_list,
+ MAX_FRIEND_CLIENTS, dht->cur_time,
nodes + count, max_num - count);
if (count >= max_num) {
@@ -2357,13 +2620,13 @@ uint16_t randfriends_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num)
return count;
}
-/** Put up to max_num nodes in nodes from the closelist.
+/** @brief Put up to max_num nodes in nodes from the closelist.
*
- * return the number of nodes.
+ * @return the number of nodes.
*/
uint16_t closelist_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num)
{
- return list_nodes(dht->close_clientlist, LCLIENT_LIST, dht->cur_time, nodes, max_num);
+ return list_nodes(dht->rng, dht->close_clientlist, LCLIENT_LIST, dht->cur_time, nodes, max_num);
}
/*----------------------------------------------------------------------------------*/
@@ -2374,6 +2637,7 @@ void cryptopacket_registerhandler(DHT *dht, uint8_t byte, cryptopacket_handler_c
dht->cryptopackethandlers[byte].object = object;
}
+non_null()
static int cryptopacket_handle(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
@@ -2387,7 +2651,7 @@ static int cryptopacket_handle(void *object, const IP_Port *source, const uint8_
}
// Check if request is for us.
- if (id_equal(packet + 1, dht->self_public_key)) {
+ if (pk_equal(packet + 1, dht->self_public_key)) {
uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t data[MAX_CRYPTO_REQUEST_SIZE];
uint8_t number;
@@ -2398,7 +2662,7 @@ static int cryptopacket_handle(void *object, const IP_Port *source, const uint8_
return 1;
}
- if (!dht->cryptopackethandlers[number].function) {
+ if (dht->cryptopackethandlers[number].function == nullptr) {
return 1;
}
@@ -2417,9 +2681,37 @@ static int cryptopacket_handle(void *object, const IP_Port *source, const uint8_
return 1;
}
+void dht_callback_get_nodes_response(DHT *dht, dht_get_nodes_response_cb *function)
+{
+ dht->get_nodes_response = function;
+}
+
+non_null(1, 2, 3) nullable(5)
+static int handle_LANdiscovery(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
+ void *userdata)
+{
+ DHT *dht = (DHT *)object;
+
+ if (!dht->lan_discovery_enabled) {
+ return 1;
+ }
+
+ if (!ip_is_lan(&source->ip)) {
+ return 1;
+ }
+
+ if (length != CRYPTO_PUBLIC_KEY_SIZE + 1) {
+ return 1;
+ }
+
+ dht_bootstrap(dht, source, packet + 1);
+ return 0;
+}
+
/*----------------------------------------------------------------------------------*/
-DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool holepunching_enabled)
+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)
{
if (net == nullptr) {
return nullptr;
@@ -2431,14 +2723,17 @@ DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool
return nullptr;
}
+ dht->ns = ns;
dht->mono_time = mono_time;
dht->cur_time = mono_time_get(mono_time);
dht->log = log;
dht->net = net;
+ dht->rng = rng;
- dht->hole_punching_enabled = holepunching_enabled;
+ dht->hole_punching_enabled = hole_punching_enabled;
+ dht->lan_discovery_enabled = lan_discovery_enabled;
- dht->ping = ping_new(mono_time, dht);
+ dht->ping = ping_new(mono_time, rng, dht);
if (dht->ping == nullptr) {
kill_dht(dht);
@@ -2448,9 +2743,14 @@ DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool
networking_registerhandler(dht->net, NET_PACKET_GET_NODES, &handle_getnodes, dht);
networking_registerhandler(dht->net, NET_PACKET_SEND_NODES_IPV6, &handle_sendnodes_ipv6, dht);
networking_registerhandler(dht->net, NET_PACKET_CRYPTO, &cryptopacket_handle, dht);
+ networking_registerhandler(dht->net, NET_PACKET_LAN_DISCOVERY, &handle_LANdiscovery, dht);
cryptopacket_registerhandler(dht, CRYPTO_PACKET_NAT_PING, &handle_NATping, dht);
- crypto_new_keypair(dht->self_public_key, dht->self_secret_key);
+#ifdef CHECK_ANNOUNCE_NODE
+ networking_registerhandler(dht->net, NET_PACKET_DATA_SEARCH_RESPONSE, &handle_data_search_response, dht);
+#endif
+
+ crypto_new_keypair(rng, dht->self_public_key, dht->self_secret_key);
dht->dht_ping_array = ping_array_new(DHT_PING_ARRAY_SIZE, PING_TIMEOUT);
@@ -2463,7 +2763,7 @@ DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool
uint8_t random_public_key_bytes[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t random_secret_key_bytes[CRYPTO_SECRET_KEY_SIZE];
- crypto_new_keypair(random_public_key_bytes, random_secret_key_bytes);
+ crypto_new_keypair(rng, random_public_key_bytes, random_secret_key_bytes);
if (dht_addfriend(dht, random_public_key_bytes, nullptr, nullptr, 0, nullptr) != 0) {
kill_dht(dht);
@@ -2471,6 +2771,12 @@ DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool
}
}
+ if (dht->num_friends != DHT_FAKE_FRIEND_NUMBER) {
+ LOGGER_ERROR(log, "the RNG provided seems to be broken: it generated the same keypair twice");
+ kill_dht(dht);
+ return nullptr;
+ }
+
return dht;
}
@@ -2485,7 +2791,7 @@ void do_dht(DHT *dht)
dht->cur_time = cur_time;
// Load friends/clients if first call to do_dht
- if (dht->loaded_num_nodes) {
+ if (dht->loaded_num_nodes > 0) {
dht_connect_after_load(dht);
}
@@ -2497,9 +2803,16 @@ void do_dht(DHT *dht)
void kill_dht(DHT *dht)
{
+ if (dht == nullptr) {
+ return;
+ }
+
networking_registerhandler(dht->net, NET_PACKET_GET_NODES, nullptr, nullptr);
networking_registerhandler(dht->net, NET_PACKET_SEND_NODES_IPV6, nullptr, nullptr);
+ networking_registerhandler(dht->net, NET_PACKET_CRYPTO, nullptr, nullptr);
+ networking_registerhandler(dht->net, NET_PACKET_LAN_DISCOVERY, nullptr, nullptr);
cryptopacket_registerhandler(dht, CRYPTO_PACKET_NAT_PING, nullptr, nullptr);
+
ping_array_kill(dht->dht_ping_array);
ping_kill(dht->ping);
free(dht->friends_list);
@@ -2531,26 +2844,26 @@ uint32_t dht_size(const DHT *dht)
}
for (uint32_t i = 0; i < LCLIENT_LIST; ++i) {
- numv4 += (dht->close_clientlist[i].assoc4.timestamp != 0);
- numv6 += (dht->close_clientlist[i].assoc6.timestamp != 0);
+ numv4 += dht->close_clientlist[i].assoc4.timestamp != 0;
+ numv6 += dht->close_clientlist[i].assoc6.timestamp != 0;
}
for (uint32_t i = 0; i < DHT_FAKE_FRIEND_NUMBER && i < dht->num_friends; ++i) {
const DHT_Friend *const fr = &dht->friends_list[i];
for (uint32_t j = 0; j < MAX_FRIEND_CLIENTS; ++j) {
- numv4 += (fr->client_list[j].assoc4.timestamp != 0);
- numv6 += (fr->client_list[j].assoc6.timestamp != 0);
+ numv4 += fr->client_list[j].assoc4.timestamp != 0;
+ numv6 += fr->client_list[j].assoc6.timestamp != 0;
}
}
const uint32_t size32 = sizeof(uint32_t);
const uint32_t sizesubhead = size32 * 2;
- return size32 + sizesubhead + packed_node_size(net_family_ipv4) * numv4 + packed_node_size(net_family_ipv6) * numv6;
+ return size32 + sizesubhead + packed_node_size(net_family_ipv4()) * numv4 + packed_node_size(net_family_ipv6()) * numv6;
}
-/** Save the DHT in data where data is an array of size dht_size(). */
+/** Save the DHT in data where data is an array of size `dht_size()`. */
void dht_save(const DHT *dht, uint8_t *data)
{
host_to_lendian_bytes32(data, DHT_STATE_COOKIE_GLOBAL);
@@ -2607,19 +2920,19 @@ void dht_save(const DHT *dht, uint8_t *data)
}
}
- state_write_section_header(old_data, DHT_STATE_COOKIE_TYPE, pack_nodes(data, sizeof(Node_format) * num, clients, num),
- DHT_STATE_TYPE_NODES);
+ state_write_section_header(old_data, DHT_STATE_COOKIE_TYPE, pack_nodes(dht->log, data, sizeof(Node_format) * num,
+ clients, num), DHT_STATE_TYPE_NODES);
free(clients);
}
-/** Bootstrap from this number of nodes every time dht_connect_after_load() is called */
+/** Bootstrap from this number of nodes every time `dht_connect_after_load()` is called */
#define SAVE_BOOTSTAP_FREQUENCY 8
-/** Start sending packets after DHT loaded_friends_list and loaded_clients_list are set.
+/** @brief Start sending packets after DHT loaded_friends_list and loaded_clients_list are set.
*
- * returns 0 if successful
- * returns -1 otherwise
+ * @retval 0 if successful
+ * @retval -1 otherwise
*/
int dht_connect_after_load(DHT *dht)
{
@@ -2627,7 +2940,7 @@ int dht_connect_after_load(DHT *dht)
return -1;
}
- if (!dht->loaded_nodes_list) {
+ if (dht->loaded_nodes_list == nullptr) {
return -1;
}
@@ -2648,6 +2961,7 @@ int dht_connect_after_load(DHT *dht)
return 0;
}
+non_null()
static State_Load_Status dht_load_state_callback(void *outer, const uint8_t *data, uint32_t length, uint16_t type)
{
DHT *dht = (DHT *)outer;
@@ -2668,7 +2982,7 @@ static State_Load_Status dht_load_state_callback(void *outer, const uint8_t *dat
break;
}
- const int num = unpack_nodes(dht->loaded_nodes_list, MAX_SAVED_DHT_NODES, nullptr, data, length, 0);
+ const int num = unpack_nodes(dht->loaded_nodes_list, MAX_SAVED_DHT_NODES, nullptr, data, length, false);
if (num > 0) {
dht->loaded_num_nodes = num;
@@ -2689,10 +3003,10 @@ static State_Load_Status dht_load_state_callback(void *outer, const uint8_t *dat
return STATE_LOAD_STATUS_CONTINUE;
}
-/** Load the DHT from data of size size.
+/** @brief Load the DHT from data of size size.
*
- * return -1 if failure.
- * return 0 if success.
+ * @retval -1 if failure.
+ * @retval 0 if success.
*/
int dht_load(DHT *dht, const uint8_t *data, uint32_t length)
{
@@ -2711,8 +3025,9 @@ int dht_load(DHT *dht, const uint8_t *data, uint32_t length)
return -1;
}
-/** return false if we are not connected to the DHT.
- * return true if we are.
+/**
+ * @retval false if we are not connected to the DHT.
+ * @retval true if we are.
*/
bool dht_isconnected(const DHT *dht)
{
@@ -2728,8 +3043,9 @@ bool dht_isconnected(const DHT *dht)
return false;
}
-/** return false if we are not connected or only connected to lan peers with the DHT.
- * return true if we are.
+/**
+ * @retval false if we are not connected or only connected to lan peers with the DHT.
+ * @retval true if we are.
*/
bool dht_non_lan_connected(const DHT *dht)
{
@@ -2750,13 +3066,15 @@ bool dht_non_lan_connected(const DHT *dht)
return false;
}
-/** Copies our own ip_port structure to `dest`. WAN addresses take priority over LAN addresses.
+/** @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.
*
- * Return 0 if our ip port can't be found (this usually means we're not connected to the DHT).
- * Return 1 if IP is a WAN address.
- * Return 2 if IP is a LAN address.
+ * @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.
*/
unsigned int ipport_self_copy(const DHT *dht, IP_Port *dest)
{
diff --git a/protocols/Tox/libtox/src/toxcore/DHT.h b/protocols/Tox/libtox/src/toxcore/DHT.h
index f72ef924c9..34ecf9dd0b 100644
--- a/protocols/Tox/libtox/src/toxcore/DHT.h
+++ b/protocols/Tox/libtox/src/toxcore/DHT.h
@@ -3,14 +3,15 @@
* Copyright © 2013 Tox project.
*/
-/**
- * An implementation of the DHT as seen in docs/updates/DHT.md
+/** @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"
@@ -21,6 +22,8 @@
extern "C" {
#endif
+/* 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
@@ -49,36 +52,82 @@ extern "C" {
#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) */
+/**
+ * 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.
-/** Create a request to peer.
- * send_public_key and send_secret_key are the pub/secret keys of the sender.
- * recv_public_key is public key of receiver.
- * packet must be an array of MAX_CRYPTO_REQUEST_SIZE big.
- * Data represents the data we send with the request with length being the length of the data.
- * request_id is the id of the request (32 = friend request, 254 = ping request).
+/* 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
*
- * return -1 on failure.
- * return the length of the created packet on success.
+ * @retval -1 on failure.
+ * @return the length of the created packet on success.
*/
-int create_request(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 length, uint8_t request_id);
+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);
-/** Puts the senders public key in the request in public_key, the data from the request
- * in data if a friend or ping request was sent to us and returns the length of the data.
- * packet is the request packet and length is its length.
+/**
+ * @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.
*
- * return -1 if not valid request.
+ * @attention Constraints:
+ * @code
+ * sizeof(data) >= MAX_CRYPTO_REQUEST_SIZE
+ * @endcode
+ *
+ * @retval -1 if not valid request.
+ * @return the length of the unpacked data.
*/
-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 length);
+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;
@@ -101,13 +150,18 @@ 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 {
- /* 1 if currently hole punching, otherwise 0 */
- uint8_t hole_punching;
+ /* true if currently hole punching */
+ bool hole_punching;
uint32_t punching_index;
uint32_t tries;
uint32_t punching_index2;
@@ -125,48 +179,66 @@ typedef struct Node_format {
IP_Port ip_port;
} Node_format;
+extern const Node_format empty_node_format;
+
typedef struct DHT_Friend DHT_Friend;
-const uint8_t *dht_friend_public_key(const DHT_Friend *dht_friend);
-const Client_data *dht_friend_client(const DHT_Friend *dht_friend, size_t index);
+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.
- * Return -1 on failure.
+/** @return packet size of packed node with ip_family on success.
+ * @retval -1 on failure.
*/
int packed_node_size(Family ip_family);
-/** Packs an IP_Port structure into data of max size length.
+/** @brief Pack an IP_Port structure into data of max size length.
*
* Packed_length is the offset of data currently packed.
*
- * Returns size of packed IP_Port data on success
- * Return -1 on failure.
+ * @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.
*/
-int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port);
+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);
-/** Unpack IP_Port structure from data of max size length into ip_port.
+/** @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.
- * Return -1 on failure.
+ * @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);
-/** Pack number of nodes into data of maxlength length.
+/** @brief Pack number of nodes into data of maxlength length.
*
- * return length of packed nodes on success.
- * return -1 on failure.
+ * @return length of packed nodes on success.
+ * @retval -1 on failure.
*/
-int pack_nodes(uint8_t *data, uint16_t length, const Node_format *nodes, uint16_t number);
+non_null()
+int pack_nodes(const Logger *logger, uint8_t *data, uint16_t length, const Node_format *nodes, uint16_t number);
-/** Unpack data of length into nodes of size max_num_nodes.
+/** @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.
- * return -1 on failure.
+ * @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);
@@ -195,224 +267,285 @@ typedef int cryptopacket_handler_cb(void *object, const IP_Port *ip_port, const
typedef struct DHT DHT;
-const uint8_t *dht_get_self_public_key(const DHT *dht);
-const uint8_t *dht_get_self_secret_key(const DHT *dht);
-void dht_set_self_public_key(DHT *dht, const uint8_t *key);
-void dht_set_self_secret_key(DHT *dht, const uint8_t *key);
+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);
-Networking_Core *dht_get_net(const DHT *dht);
-struct Ping *dht_get_ping(const DHT *dht);
-const Client_data *dht_get_close_clientlist(const DHT *dht);
-const Client_data *dht_get_close_client(const DHT *dht, uint32_t client_num);
-uint16_t dht_get_num_friends(const DHT *dht);
+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);
-DHT_Friend *dht_get_friend(DHT *dht, uint32_t friend_num);
-const uint8_t *dht_get_friend_public_key(const DHT *dht, uint32_t friend_num);
+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);
/*----------------------------------------------------------------------------------*/
-/** Shared key generations are costly, it is therefore smart to store commonly used
+/**
+ * Shared key generations are costly, it is therefore smart to store commonly used
* ones so that they can be re-used later without being computed again.
*
* If a shared key is already in shared_keys, copy it to shared_key.
* Otherwise generate it into shared_key and copy it to shared_keys
*/
-void get_shared_key(const Mono_Time *mono_time, Shared_Keys *shared_keys, uint8_t *shared_key,
- const uint8_t *secret_key, const uint8_t *public_key);
+non_null()
+void get_shared_key(
+ const Mono_Time *mono_time, Shared_Keys *shared_keys, uint8_t *shared_key,
+ const uint8_t *secret_key, const uint8_t *public_key);
-/** Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key
+/**
+ * Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key
* for packets that we receive.
*/
+non_null()
void dht_get_shared_key_recv(DHT *dht, uint8_t *shared_key, const uint8_t *public_key);
-/** Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key
+/**
+ * Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key
* for packets that we send.
*/
+non_null()
void dht_get_shared_key_sent(DHT *dht, uint8_t *shared_key, const uint8_t *public_key);
-
-/** Sends a getnodes request to `ip_port` with the public key `public_key` for nodes
+/**
+ * Sends a getnodes request to `ip_port` with the public key `public_key` for nodes
* that are close to `client_id`.
*
- * Return true on success.
+ * @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);
-/** Add a new friend to the friends list.
+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.
* public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long.
*
* ip_callback is the callback of a function that will be called when the ip address
* is found along with arguments data and number.
*
- * lock_count will be set to a non zero number that must be passed to dht_delfriend()
+ * lock_count will be set to a non zero number that must be passed to `dht_delfriend()`
* to properly remove the callback.
*
- * return 0 if success.
- * return -1 if failure (friends list is full).
+ * @retval 0 if success.
+ * @retval -1 if failure (friends list is full).
*/
+non_null(1, 2) nullable(3, 4, 6)
int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback,
void *data, int32_t number, uint16_t *lock_count);
-/** Delete a friend from the friends list.
+/** @brief Delete a friend from the friends list.
* public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long.
*
- * return 0 if success.
- * return -1 if failure (public_key not in friends list).
+ * @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, uint16_t lock_count);
-/** Get ip of friend.
- * public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long.
- * ip must be 4 bytes long.
- * port must be 2 bytes long.
+/** @brief Get ip of friend.
+ *
+ * @param public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long.
*
- * return -1, -- if public_key does NOT refer to a friend
- * return 0, -- if public_key refers to a friend and we failed to find the friend (yet)
- * return 1, ip if public_key refers to a friend and we found him
+ * @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);
-/** Compares pk1 and pk2 with pk.
+/** @brief Compares pk1 and pk2 with pk.
*
- * return 0 if both are same distance.
- * return 1 if pk1 is closer.
- * return 2 if pk2 is closer.
+ * @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.
*/
-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);
+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.
- */
+/** 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);
-/** Get the (maximum MAX_SENT_NODES) closest nodes to public_key we know
+#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_good = do we want tested nodes or not? (TODO(irungentoo))
- *
- * return the number of nodes returned.
+ * 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 is_LAN, bool want_announce);
-/** Put up to max_num nodes in nodes from the random friends.
+/** @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.
+ * @return the number of nodes.
*/
+non_null()
uint16_t randfriends_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num);
-/** Put up to max_num nodes in nodes from the closelist.
+/** @brief Put up to max_num nodes in nodes from the closelist.
*
- * return the number of nodes.
+ * @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.
*/
-/** Sends a "get nodes" request to the given node with ip, port and public_key
+/**
+ * @brief Sends a "get nodes" request to the given node with ip, port and public_key
* to setup connections
*/
-void dht_bootstrap(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key);
+non_null()
+bool dht_bootstrap(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key);
-/** 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
+/** @brief Resolves address into an IP address.
*
- * address can be a hostname or an IP address (IPv4 or IPv6).
- * if ipv6enabled is 0 (zero), the resolving sticks STRICTLY to IPv4 addresses
- * if ipv6enabled is not 0 (zero), the resolving looks for IPv6 addresses first,
- * then IPv4 addresses.
+ * If successful, sends a "get nodes" request to the given node with ip, port
+ * and public_key to setup connections
*
- * returns 1 if the address could be converted into an IP address
- * returns 0 otherwise
+ * @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
*/
-int dht_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enabled,
+non_null()
+int dht_bootstrap_from_address(DHT *dht, const char *address, bool ipv6enabled,
uint16_t port, const uint8_t *public_key);
-/** Start sending packets after DHT loaded_friends_list and loaded_clients_list are set.
+/** @brief Start sending packets after DHT loaded_friends_list and loaded_clients_list are set.
*
- * returns 0 if successful
- * returns -1 otherwise
+ * @retval 0 if successful
+ * @retval -1 otherwise
*/
+non_null()
int dht_connect_after_load(DHT *dht);
/* ROUTING FUNCTIONS */
-/** Send the given packet to node with public_key.
+/** @brief Send the given packet to node with public_key.
*
- * return -1 if failure.
+ * @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).
+ * @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.
- */
+/** 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(). */
+/** 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);
-/** Load the DHT from data of size size.
+/** @brief Load the DHT from data of size size.
*
- * return -1 if failure.
- * return 0 if success.
+ * @retval -1 if failure.
+ * @retval 0 if success.
*/
+non_null()
int dht_load(DHT *dht, const uint8_t *data, uint32_t length);
/** Initialize DHT. */
-DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool holepunching_enabled);
+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);
-/** return false if we are not connected to the DHT.
- * return true if we are.
+/**
+ * @retval false if we are not connected to the DHT.
+ * @retval true if we are.
*/
+non_null()
bool dht_isconnected(const DHT *dht);
-/** return false if we are not connected or only connected to lan peers with the DHT.
- * return true if we are.
+/**
+ * @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);
-/** Copies our own ip_port structure to `dest`. WAN addresses take priority over LAN addresses.
+/** @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.
*
- * Return 0 if our ip port can't be found (this usually means we're not connected to the DHT).
- * Return 1 if IP is a WAN address.
- * Return 2 if IP is a LAN address.
+ * @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
diff --git a/protocols/Tox/libtox/src/toxcore/LAN_discovery.c b/protocols/Tox/libtox/src/toxcore/LAN_discovery.c
index 6f9f77b6bb..82c02b59e9 100644
--- a/protocols/Tox/libtox/src/toxcore/LAN_discovery.c
+++ b/protocols/Tox/libtox/src/toxcore/LAN_discovery.c
@@ -8,6 +8,7 @@
*/
#include "LAN_discovery.h"
+#include <stdlib.h>
#include <string.h>
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
@@ -38,28 +39,35 @@
#include <net/if.h>
#endif
+#include "ccompat.h"
+#include "crypto_core.h"
#include "util.h"
#define MAX_INTERFACES 16
-/* TODO: multiple threads might concurrently try to set these, and it isn't clear that this couldn't lead to undesirable
- * behaviour. Consider storing the data in per-instance variables instead. */
-//!TOKSTYLE-
-// No global mutable state in Tokstyle.
-static int broadcast_count = -1;
-static IP_Port broadcast_ip_ports[MAX_INTERFACES];
-//!TOKSTYLE+
+struct Broadcast_Info {
+ uint32_t count;
+ IP ips[MAX_INTERFACES];
+};
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
-static void fetch_broadcast_info(uint16_t port)
+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) {
- return;
+ free(broadcast);
+ return nullptr;
}
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
@@ -67,39 +75,32 @@ static void fetch_broadcast_info(uint16_t port)
pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
if (pAdapterInfo == nullptr) {
- return;
+ free(broadcast);
+ return nullptr;
}
}
- /* We copy these to the static variables `broadcast_*` only at the end of `fetch_broadcast_info()`.
- * The intention is to ensure that even if multiple threads enter `fetch_broadcast_info()` concurrently, only valid
- * interfaces will be set to be broadcast to.
- * */
- int count = 0;
- IP_Port ip_ports[MAX_INTERFACES];
-
const int ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
if (ret == NO_ERROR) {
IP_ADAPTER_INFO *pAdapter = pAdapterInfo;
- while (pAdapter) {
+ 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_Port *ip_port = &ip_ports[count];
- ip_port->ip.family = net_family_ipv4;
- uint32_t gateway_ip = net_ntohl(gateway.ip.v4.uint32);
- uint32_t subnet_ip = net_ntohl(subnet_mask.ip.v4.uint32);
- uint32_t broadcast_ip = gateway_ip + ~subnet_ip - 1;
- ip_port->ip.ip.v4.uint32 = net_htonl(broadcast_ip);
- ip_port->port = port;
- ++count;
-
- if (count >= MAX_INTERFACES) {
+ 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;
}
}
@@ -109,30 +110,33 @@ static void fetch_broadcast_info(uint16_t port)
}
}
- if (pAdapterInfo) {
+ if (pAdapterInfo != nullptr) {
free(pAdapterInfo);
}
- broadcast_count = count;
-
- for (uint32_t i = 0; i < count; ++i) {
- broadcast_ip_ports[i] = ip_ports[i];
- }
+ return broadcast;
}
#elif !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && (defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__))
-static void fetch_broadcast_info(uint16_t port)
+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...
*/
- broadcast_count = 0;
- const Socket sock = net_socket(net_family_ipv4, TOX_SOCK_STREAM, 0);
+ const Socket sock = net_socket(ns, net_family_ipv4(), TOX_SOCK_STREAM, 0);
if (!sock_valid(sock)) {
- return;
+ free(broadcast);
+ return nullptr;
}
/* Configure ifconf for the ioctl call. */
@@ -143,28 +147,22 @@ static void fetch_broadcast_info(uint16_t port)
ifc.ifc_buf = (char *)i_faces;
ifc.ifc_len = sizeof(i_faces);
- if (ioctl(sock.socket, SIOCGIFCONF, &ifc) < 0) {
- kill_sock(sock);
- return;
+ if (ioctl(sock.sock, SIOCGIFCONF, &ifc) < 0) {
+ kill_sock(ns, sock);
+ free(broadcast);
+ return nullptr;
}
- /* We copy these to the static variables `broadcast_*` only at the end of `fetch_broadcast_info()`.
- * The intention is to ensure that even if multiple threads enter `fetch_broadcast_info()` concurrently, only valid
- * interfaces will be set to be broadcast to.
- * */
- int count = 0;
- IP_Port ip_ports[MAX_INTERFACES];
-
/* `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!)
*/
- int n = ifc.ifc_len / sizeof(struct ifreq);
+ 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.socket, SIOCGIFBRDADDR, &i_faces[i]) < 0) {
+ if (ioctl(sock.sock, SIOCGIFBRDADDR, &i_faces[i]) < 0) {
continue;
}
@@ -175,62 +173,57 @@ static void fetch_broadcast_info(uint16_t port)
const struct sockaddr_in *sock4 = (const struct sockaddr_in *)(void *)&i_faces[i].ifr_broadaddr;
- if (count >= MAX_INTERFACES) {
+ if (broadcast->count >= MAX_INTERFACES) {
break;
}
- IP_Port *ip_port = &ip_ports[count];
- ip_port->ip.family = net_family_ipv4;
- ip_port->ip.ip.v4.uint32 = sock4->sin_addr.s_addr;
+ IP *ip = &broadcast->ips[broadcast->count];
+ ip->family = net_family_ipv4();
+ ip->ip.v4.uint32 = sock4->sin_addr.s_addr;
- if (ip_port->ip.ip.v4.uint32 == 0) {
+ if (ip->ip.v4.uint32 == 0) {
continue;
}
- ip_port->port = port;
- ++count;
+ ++broadcast->count;
}
- kill_sock(sock);
-
- broadcast_count = count;
+ kill_sock(ns, sock);
- for (uint32_t i = 0; i < count; ++i) {
- broadcast_ip_ports[i] = ip_ports[i];
- }
+ return broadcast;
}
#else // TODO(irungentoo): Other platforms?
-static void fetch_broadcast_info(uint16_t port)
+non_null()
+static Broadcast_Info *fetch_broadcast_info(const Network *ns)
{
- broadcast_count = 0;
+ return (Broadcast_Info *)calloc(1, sizeof(Broadcast_Info));
}
#endif
-/** Send packet to all IPv4 broadcast addresses
+/** @brief Send packet to all IPv4 broadcast addresses
*
- * return 1 if sent to at least one broadcast target.
- * return 0 on failure to find any valid broadcast target.
+ * @retval true if sent to at least one broadcast target.
+ * @retval false on failure to find any valid broadcast target.
*/
-static uint32_t send_broadcasts(const Networking_Core *net, uint16_t port, const uint8_t *data, uint16_t length)
+non_null()
+static bool send_broadcasts(const Networking_Core *net, const Broadcast_Info *broadcast, uint16_t port,
+ const uint8_t *data, uint16_t length)
{
- /* fetch only once? on every packet? every X seconds?
- * old: every packet, new: once */
- if (broadcast_count < 0) {
- fetch_broadcast_info(port);
- }
-
- if (!broadcast_count) {
- return 0;
+ if (broadcast->count == 0) {
+ return false;
}
- for (int i = 0; i < broadcast_count; ++i) {
- sendpacket(net, &broadcast_ip_ports[i], data, length);
+ 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 1;
+ return true;
}
/** Return the broadcast ip. */
@@ -241,7 +234,7 @@ static IP broadcast_ip(Family family_socket, Family family_broadcast)
if (net_family_is_ipv6(family_socket)) {
if (net_family_is_ipv6(family_broadcast)) {
- ip.family = net_family_ipv6;
+ 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 */
@@ -249,17 +242,18 @@ static IP broadcast_ip(Family family_socket, Family family_broadcast)
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.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.family = net_family_ipv4();
ip.ip.v4 = ip4_broadcast;
}
return ip;
}
+non_null()
static bool ip4_is_local(const IP4 *ip4)
{
/* Loopback. */
@@ -283,13 +277,10 @@ bool ip_is_local(const IP *ip)
}
/* localhost in IPv6 (::1) */
- if (ip->ip.v6.uint64[0] == 0 && ip->ip.v6.uint32[2] == 0 && ip->ip.v6.uint32[3] == net_htonl(1)) {
- return true;
- }
-
- return false;
+ 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. */
@@ -315,11 +306,7 @@ static bool ip4_is_lan(const IP4 *ip4)
/* RFC 6598: 100.64.0.0 to 100.127.255.255 (100.64.0.0/10)
* (shared address space to stack another layer of NAT) */
- if ((ip4->uint8[0] == 100) && ((ip4->uint8[1] & 0xC0) == 0x40)) {
- return true;
- }
-
- return false;
+ return (ip4->uint8[0] == 100) && ((ip4->uint8[1] & 0xC0) == 0x40);
}
bool ip_is_lan(const IP *ip)
@@ -351,31 +338,18 @@ bool ip_is_lan(const IP *ip)
return false;
}
-static int handle_LANdiscovery(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
- void *userdata)
-{
- DHT *dht = (DHT *)object;
-
- if (!ip_is_lan(&source->ip)) {
- return 1;
- }
- if (length != CRYPTO_PUBLIC_KEY_SIZE + 1) {
- return 1;
+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;
}
- dht_bootstrap(dht, source, packet + 1);
- return 0;
-}
-
-
-bool lan_discovery_send(Networking_Core *net, const uint8_t *dht_pk, uint16_t port)
-{
uint8_t data[CRYPTO_PUBLIC_KEY_SIZE + 1];
data[0] = NET_PACKET_LAN_DISCOVERY;
- id_copy(data + 1, dht_pk);
+ pk_copy(data + 1, dht_pk);
- send_broadcasts(net, port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE);
+ send_broadcasts(net, broadcast, port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE);
bool res = false;
IP_Port ip_port;
@@ -383,34 +357,30 @@ bool lan_discovery_send(Networking_Core *net, const uint8_t *dht_pk, uint16_t po
/* IPv6 multicast */
if (net_family_is_ipv6(net_family(net))) {
- ip_port.ip = broadcast_ip(net_family_ipv6, net_family_ipv6);
+ ip_port.ip = broadcast_ip(net_family_ipv6(), net_family_ipv6());
- if (ip_isset(&ip_port.ip)) {
- if (sendpacket(net, &ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE) > 0) {
- res = true;
- }
+ 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);
+ ip_port.ip = broadcast_ip(net_family(net), net_family_ipv4());
- if (ip_isset(&ip_port.ip)) {
- if (sendpacket(net, &ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE)) {
- res = true;
- }
+ if (ip_isset(&ip_port.ip) && sendpacket(net, &ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE) > 0) {
+ res = true;
}
return res;
}
-void lan_discovery_init(DHT *dht)
+Broadcast_Info *lan_discovery_init(const Network *ns)
{
- networking_registerhandler(dht_get_net(dht), NET_PACKET_LAN_DISCOVERY, &handle_LANdiscovery, dht);
+ return fetch_broadcast_info(ns);
}
-void lan_discovery_kill(DHT *dht)
+void lan_discovery_kill(Broadcast_Info *broadcast)
{
- networking_registerhandler(dht_get_net(dht), NET_PACKET_LAN_DISCOVERY, nullptr, nullptr);
+ free(broadcast);
}
diff --git a/protocols/Tox/libtox/src/toxcore/LAN_discovery.h b/protocols/Tox/libtox/src/toxcore/LAN_discovery.h
index cce7b5c190..85ea9478f4 100644
--- a/protocols/Tox/libtox/src/toxcore/LAN_discovery.h
+++ b/protocols/Tox/libtox/src/toxcore/LAN_discovery.h
@@ -9,33 +9,39 @@
#ifndef C_TOXCORE_TOXCORE_LAN_DISCOVERY_H
#define C_TOXCORE_TOXCORE_LAN_DISCOVERY_H
-#include "DHT.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.
*/
-bool lan_discovery_send(Networking_Core *net, const uint8_t *dht_pk, uint16_t port);
+non_null()
+bool lan_discovery_send(const Networking_Core *net, const Broadcast_Info *broadcast, const uint8_t *dht_pk, uint16_t port);
/**
- * Sets up packet handlers.
+ * Discovers broadcast devices and IP addresses.
*/
-void lan_discovery_init(DHT *dht);
+non_null()
+Broadcast_Info *lan_discovery_init(const Network *ns);
/**
- * Clear packet handlers.
+ * Free all resources associated with the broadcast info.
*/
-void lan_discovery_kill(DHT *dht);
+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);
/**
@@ -43,6 +49,7 @@ bool ip_is_local(const IP *ip);
*
* @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
diff --git a/protocols/Tox/libtox/src/toxcore/Messenger.c b/protocols/Tox/libtox/src/toxcore/Messenger.c
index 6413dc928e..6042fd725b 100644
--- a/protocols/Tox/libtox/src/toxcore/Messenger.c
+++ b/protocols/Tox/libtox/src/toxcore/Messenger.c
@@ -14,6 +14,7 @@
#include <string.h>
#include <time.h>
+#include "ccompat.h"
#include "logger.h"
#include "mono_time.h"
#include "network.h"
@@ -23,24 +24,13 @@
static_assert(MAX_CONCURRENT_FILE_PIPES <= UINT8_MAX + 1,
"uint8_t cannot represent all file transfer numbers");
-static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data,
- uint32_t length, uint8_t congestion_control);
-static void m_register_default_plugins(Messenger *m);
+static const Friend empty_friend = {{0}};
-/**
- * Determines if the friendnumber passed is valid in the Messenger object.
- *
- * @param friendnumber The index in the friend list.
- */
-static bool friend_is_valid(const Messenger *m, int32_t friendnumber)
-{
- return (unsigned int)friendnumber < m->numfriends && m->friendlist[friendnumber].status != 0;
-}
-
-/** Set the size of the friend list to numfriends.
+/** @brief Set the size of the friend list to numfriends.
*
- * return -1 if realloc fails.
+ * @retval -1 if realloc fails.
*/
+non_null()
static int realloc_friendlist(Messenger *m, uint32_t num)
{
if (num == 0) {
@@ -59,31 +49,30 @@ static int realloc_friendlist(Messenger *m, uint32_t num)
return 0;
}
-/** return the friend number associated to that public key.
- * return -1 if no such friend.
+/** @return the friend number associated to that public key.
+ * @retval -1 if no such friend.
*/
int32_t getfriend_id(const Messenger *m, const uint8_t *real_pk)
{
for (uint32_t i = 0; i < m->numfriends; ++i) {
- if (m->friendlist[i].status > 0) {
- if (id_equal(real_pk, m->friendlist[i].real_pk)) {
- return i;
- }
+ if (m->friendlist[i].status > 0 && pk_equal(real_pk, m->friendlist[i].real_pk)) {
+ return i;
}
}
return -1;
}
-/** Copies the public key associated to that friend id into real_pk buffer.
+/** @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.
*
- * return 0 if success.
- * return -1 if failure.
+ * @retval 0 if success.
+ * @retval -1 if failure.
*/
int get_real_pk(const Messenger *m, int32_t friendnumber, uint8_t *real_pk)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -91,12 +80,12 @@ int get_real_pk(const Messenger *m, int32_t friendnumber, uint8_t *real_pk)
return 0;
}
-/** return friend connection id on success.
- * return -1 if failure.
+/** @return friend connection id on success.
+ * @retval -1 if failure.
*/
int getfriendcon_id(const Messenger *m, int32_t friendnumber)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -104,57 +93,48 @@ int getfriendcon_id(const Messenger *m, int32_t friendnumber)
}
/**
- * return a uint16_t that represents the checksum of address of length len.
- */
-static uint16_t address_checksum(const uint8_t *address, uint32_t len)
-{
- uint8_t checksum[2] = {0};
- uint16_t check;
-
- for (uint32_t i = 0; i < len; ++i) {
- checksum[i % 2] ^= address[i];
- }
-
- memcpy(&check, checksum, sizeof(check));
- return check;
-}
-
-/** Format: `[real_pk (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]`
+ * Format: `[real_pk (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]`
*
- * return FRIEND_ADDRESS_SIZE byte address to give to others.
+ * @param[out] address FRIEND_ADDRESS_SIZE byte address to give to others.
*/
void getaddress(const Messenger *m, uint8_t *address)
{
- id_copy(address, nc_get_self_public_key(m->net_crypto));
+ pk_copy(address, nc_get_self_public_key(m->net_crypto));
uint32_t nospam = get_nospam(m->fr);
memcpy(address + CRYPTO_PUBLIC_KEY_SIZE, &nospam, sizeof(nospam));
- uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
+ uint16_t checksum = data_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
memcpy(address + CRYPTO_PUBLIC_KEY_SIZE + sizeof(nospam), &checksum, sizeof(checksum));
}
-static int send_online_packet(Messenger *m, int32_t friendnumber)
+non_null()
+static bool send_online_packet(Messenger *m, int32_t friendnumber)
{
- if (!friend_is_valid(m, friendnumber)) {
- return 0;
+ if (!m_friend_exists(m, friendnumber)) {
+ return false;
}
uint8_t packet = PACKET_ID_ONLINE;
return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
- m->friendlist[friendnumber].friendcon_id), &packet, sizeof(packet), 0) != -1;
+ m->friendlist[friendnumber].friendcon_id), &packet, sizeof(packet), false) != -1;
}
-static int send_offline_packet(Messenger *m, int friendcon_id)
+non_null()
+static bool send_offline_packet(Messenger *m, int friendcon_id)
{
uint8_t packet = PACKET_ID_OFFLINE;
return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, friendcon_id), &packet,
- sizeof(packet), 0) != -1;
+ sizeof(packet), false) != -1;
}
-static int m_handle_status(void *object, int i, uint8_t status, void *userdata);
+non_null(1) nullable(4)
+static int m_handle_status(void *object, int i, bool status, void *userdata);
+non_null(1, 3) nullable(5)
static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t len, void *userdata);
+non_null(1, 3) nullable(5)
static int m_handle_lossy_packet(void *object, int friend_num, const uint8_t *packet, uint16_t length,
void *userdata);
+non_null()
static int32_t init_new_friend(Messenger *m, const uint8_t *real_pk, uint8_t status)
{
if (m->numfriends == UINT32_MAX) {
@@ -168,9 +148,9 @@ static int32_t init_new_friend(Messenger *m, const uint8_t *real_pk, uint8_t sta
return FAERR_NOMEM;
}
- memset(&m->friendlist[m->numfriends], 0, sizeof(Friend));
+ m->friendlist[m->numfriends] = empty_friend;
- int friendcon_id = new_friend_connection(m->fr_c, real_pk);
+ const int friendcon_id = new_friend_connection(m->fr_c, real_pk);
if (friendcon_id == -1) {
return FAERR_NOMEM;
@@ -181,10 +161,10 @@ static int32_t init_new_friend(Messenger *m, const uint8_t *real_pk, uint8_t sta
m->friendlist[i].status = status;
m->friendlist[i].friendcon_id = friendcon_id;
m->friendlist[i].friendrequest_lastsent = 0;
- id_copy(m->friendlist[i].real_pk, real_pk);
+ pk_copy(m->friendlist[i].real_pk, real_pk);
m->friendlist[i].statusmessage_length = 0;
m->friendlist[i].userstatus = USERSTATUS_NONE;
- m->friendlist[i].is_typing = 0;
+ m->friendlist[i].is_typing = false;
m->friendlist[i].message_id = 0;
friend_connection_callbacks(m->fr_c, friendcon_id, MESSENGER_CALLBACK_INDEX, &m_handle_status, &m_handle_packet,
&m_handle_lossy_packet, m, i);
@@ -215,15 +195,15 @@ static int32_t init_new_friend(Messenger *m, const uint8_t *real_pk, uint8_t sta
* @param data is the data.
* @param length is the length.
*
- * return the friend number if success.
- * return FA_TOOLONG if message length is too long.
- * return FAERR_NOMESSAGE if no message (message length must be >= 1 byte).
- * return FAERR_OWNKEY if user's own key.
- * return FAERR_ALREADYSENT if friend request already sent or already a friend.
- * return FAERR_BADCHECKSUM if bad checksum in address.
- * return FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different.
- * (the nospam for that friend was set to the new one).
- * return FAERR_NOMEM if increasing the friend list size fails.
+ * @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.
*/
int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, uint16_t length)
{
@@ -232,14 +212,14 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u
}
uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
- id_copy(real_pk, address);
+ pk_copy(real_pk, address);
if (!public_key_valid(real_pk)) {
return FAERR_BADCHECKSUM;
}
uint16_t check;
- uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
+ const uint16_t checksum = data_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
memcpy(&check, address + CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint32_t), sizeof(check));
if (check != checksum) {
@@ -250,11 +230,11 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u
return FAERR_NOMESSAGE;
}
- if (id_equal(real_pk, nc_get_self_public_key(m->net_crypto))) {
+ if (pk_equal(real_pk, nc_get_self_public_key(m->net_crypto))) {
return FAERR_OWNKEY;
}
- int32_t friend_id = getfriend_id(m, real_pk);
+ const int32_t friend_id = getfriend_id(m, real_pk);
if (friend_id != -1) {
if (m->friendlist[friend_id].status >= FRIEND_CONFIRMED) {
@@ -272,7 +252,7 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u
return FAERR_SETNEWNOSPAM;
}
- int32_t ret = init_new_friend(m, real_pk, FRIEND_ADDED);
+ const int32_t ret = init_new_friend(m, real_pk, FRIEND_ADDED);
if (ret < 0) {
return ret;
@@ -296,22 +276,23 @@ int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk)
return FAERR_BADCHECKSUM;
}
- if (id_equal(real_pk, nc_get_self_public_key(m->net_crypto))) {
+ if (pk_equal(real_pk, nc_get_self_public_key(m->net_crypto))) {
return FAERR_OWNKEY;
}
return init_new_friend(m, real_pk, FRIEND_CONFIRMED);
}
+non_null()
static int clear_receipts(Messenger *m, int32_t friendnumber)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
struct Receipts *receipts = m->friendlist[friendnumber].receipts_start;
- while (receipts) {
+ while (receipts != nullptr) {
struct Receipts *temp_r = receipts->next;
free(receipts);
receipts = temp_r;
@@ -322,22 +303,23 @@ static int clear_receipts(Messenger *m, int32_t friendnumber)
return 0;
}
+non_null()
static int add_receipt(Messenger *m, int32_t friendnumber, uint32_t packet_num, uint32_t msg_id)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
struct Receipts *new_receipts = (struct Receipts *)calloc(1, sizeof(struct Receipts));
- if (!new_receipts) {
+ if (new_receipts == nullptr) {
return -1;
}
new_receipts->packet_num = packet_num;
new_receipts->msg_id = msg_id;
- if (!m->friendlist[friendnumber].receipts_start) {
+ if (m->friendlist[friendnumber].receipts_start == nullptr) {
m->friendlist[friendnumber].receipts_start = new_receipts;
} else {
m->friendlist[friendnumber].receipts_end->next = new_receipts;
@@ -351,9 +333,10 @@ static int add_receipt(Messenger *m, int32_t friendnumber, uint32_t packet_num,
* return -1 on failure.
* return 0 if packet was received.
*/
+non_null()
static int friend_received_packet(const Messenger *m, int32_t friendnumber, uint32_t number)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -361,20 +344,21 @@ static int friend_received_packet(const Messenger *m, int32_t friendnumber, uint
m->friendlist[friendnumber].friendcon_id), number);
}
+non_null(1) nullable(3)
static int do_receipts(Messenger *m, int32_t friendnumber, void *userdata)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
struct Receipts *receipts = m->friendlist[friendnumber].receipts_start;
- while (receipts) {
+ while (receipts != nullptr) {
if (friend_received_packet(m, friendnumber, receipts->packet_num) == -1) {
break;
}
- if (m->read_receipt) {
+ if (m->read_receipt != nullptr) {
m->read_receipt(m, friendnumber, receipts->msg_id, userdata);
}
@@ -387,25 +371,25 @@ static int do_receipts(Messenger *m, int32_t friendnumber, void *userdata)
receipts = r_next;
}
- if (!m->friendlist[friendnumber].receipts_start) {
+ if (m->friendlist[friendnumber].receipts_start == nullptr) {
m->friendlist[friendnumber].receipts_end = nullptr;
}
return 0;
}
-/** Remove a friend.
+/** @brief Remove a friend.
*
- * return 0 if success.
- * return -1 if failure.
+ * @retval 0 if success.
+ * @retval -1 if failure.
*/
int m_delfriend(Messenger *m, int32_t friendnumber)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
- if (m->friend_connectionstatuschange_internal) {
+ if (m->friend_connectionstatuschange_internal != nullptr) {
m->friend_connectionstatuschange_internal(m, friendnumber, 0, m->friend_connectionstatuschange_internal_userdata);
}
@@ -419,7 +403,7 @@ int m_delfriend(Messenger *m, int32_t friendnumber)
}
kill_friend_connection(m->fr_c, m->friendlist[friendnumber].friendcon_id);
- memset(&m->friendlist[friendnumber], 0, sizeof(Friend));
+ m->friendlist[friendnumber] = empty_friend;
uint32_t i;
@@ -440,7 +424,7 @@ int m_delfriend(Messenger *m, int32_t friendnumber)
int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -448,9 +432,9 @@ int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber)
return CONNECTION_NONE;
}
- bool direct_connected = 0;
- unsigned int num_online_relays = 0;
- int crypt_conn_id = friend_connection_crypt_connection_id(m->fr_c, m->friendlist[friendnumber].friendcon_id);
+ bool direct_connected = false;
+ uint32_t num_online_relays = 0;
+ const int crypt_conn_id = friend_connection_crypt_connection_id(m->fr_c, m->friendlist[friendnumber].friendcon_id);
if (!crypto_connection_status(m->net_crypto, crypt_conn_id, &direct_connected, &num_online_relays)) {
return CONNECTION_NONE;
@@ -460,7 +444,7 @@ int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber)
return CONNECTION_UDP;
}
- if (num_online_relays) {
+ if (num_online_relays != 0) {
return CONNECTION_TCP;
}
@@ -471,99 +455,124 @@ int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber)
return m->friendlist[friendnumber].last_connection_udp_tcp;
}
-int m_friend_exists(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.
+ */
+bool m_friend_exists(const Messenger *m, int32_t friendnumber)
{
- if (!friend_is_valid(m, friendnumber)) {
- return 0;
- }
-
- return 1;
+ return (unsigned int)friendnumber < m->numfriends && m->friendlist[friendnumber].status != 0;
}
-/** Send a message of type to an online friend.
+/** @brief Send a message of type to an online friend.
*
- * return -1 if friend not valid.
- * return -2 if too large.
- * return -3 if friend not online.
- * return -4 if send failed (because queue is full).
- * return -5 if bad type.
- * return 0 if success.
+ * @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.
+ * The value in message_id will be passed to your read_receipt callback when the other receives the message.
*/
int m_send_message_generic(Messenger *m, int32_t friendnumber, uint8_t type, const uint8_t *message, uint32_t length,
uint32_t *message_id)
{
if (type > MESSAGE_ACTION) {
- LOGGER_WARNING(m->log, "Message type %d is invalid", type);
+ LOGGER_WARNING(m->log, "message type %d is invalid", type);
return -5;
}
- if (!friend_is_valid(m, friendnumber)) {
- LOGGER_WARNING(m->log, "Friend number %d is invalid", friendnumber);
+ if (!m_friend_exists(m, friendnumber)) {
+ LOGGER_WARNING(m->log, "friend number %d is invalid", friendnumber);
return -1;
}
if (length >= MAX_CRYPTO_DATA_SIZE) {
- LOGGER_WARNING(m->log, "Message length %u is too large", length);
+ LOGGER_WARNING(m->log, "message length %u is too large", length);
return -2;
}
if (m->friendlist[friendnumber].status != FRIEND_ONLINE) {
- LOGGER_WARNING(m->log, "Friend %d is not online", friendnumber);
+ LOGGER_WARNING(m->log, "friend %d is not online", friendnumber);
return -3;
}
VLA(uint8_t, packet, length + 1);
packet[0] = PACKET_ID_MESSAGE + type;
- if (length != 0) {
- memcpy(packet + 1, message, length);
- }
+ assert(message != nullptr);
+ memcpy(packet + 1, message, length);
- int64_t packet_num = write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
- m->friendlist[friendnumber].friendcon_id), packet, length + 1, 0);
+ const int64_t packet_num = write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
+ m->friendlist[friendnumber].friendcon_id), packet, length + 1, false);
if (packet_num == -1) {
- LOGGER_WARNING(m->log, "Failed to write crypto packet for message of length %d to friend %d",
- length, friendnumber);
return -4;
}
- uint32_t msg_id = ++m->friendlist[friendnumber].message_id;
+ const uint32_t msg_id = ++m->friendlist[friendnumber].message_id;
add_receipt(m, friendnumber, packet_num, msg_id);
- if (message_id) {
+ if (message_id != nullptr) {
*message_id = msg_id;
}
return 0;
}
-/** Send a name packet to friendnumber.
+non_null()
+static bool write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data,
+ uint32_t length, bool congestion_control)
+{
+ if (!m_friend_exists(m, friendnumber)) {
+ return false;
+ }
+
+ if (length >= MAX_CRYPTO_DATA_SIZE || m->friendlist[friendnumber].status != FRIEND_ONLINE) {
+ return false;
+ }
+
+ VLA(uint8_t, packet, length + 1);
+ packet[0] = packet_id;
+
+ assert(data != nullptr);
+ memcpy(packet + 1, data, length);
+
+ return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
+ m->friendlist[friendnumber].friendcon_id), packet, length + 1, congestion_control) != -1;
+}
+
+/** @brief Send a name packet to friendnumber.
* length is the length with the NULL terminator.
*/
-static int m_sendname(const Messenger *m, int32_t friendnumber, const uint8_t *name, uint16_t length)
+non_null()
+static bool m_sendname(const Messenger *m, int32_t friendnumber, const uint8_t *name, uint16_t length)
{
if (length > MAX_NAME_LENGTH) {
- return 0;
+ return false;
}
- return write_cryptpacket_id(m, friendnumber, PACKET_ID_NICKNAME, name, length, 0);
+ return write_cryptpacket_id(m, friendnumber, PACKET_ID_NICKNAME, name, length, false);
}
-/** Set the name and name_length of a friend.
+/** @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.
*
- * return 0 if success.
- * return -1 if failure.
+ * @retval 0 if success.
+ * @retval -1 if failure.
*/
int setfriendname(Messenger *m, int32_t friendnumber, const uint8_t *name, uint16_t length)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -576,13 +585,14 @@ int setfriendname(Messenger *m, int32_t friendnumber, const uint8_t *name, uint1
return 0;
}
-/** Set our nickname.
+/** @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.
*
- * return 0 if success.
- * return -1 if failure.
+ * @retval 0 if success.
+ * @retval -1 if failure.
*/
int setname(Messenger *m, const uint8_t *name, uint16_t length)
{
@@ -594,26 +604,27 @@ int setname(Messenger *m, const uint8_t *name, uint16_t length)
return 0;
}
- if (length) {
+ if (length > 0) {
memcpy(m->name, name, length);
}
m->name_length = length;
for (uint32_t i = 0; i < m->numfriends; ++i) {
- m->friendlist[i].name_sent = 0;
+ m->friendlist[i].name_sent = false;
}
return 0;
}
/**
- * Get your nickname.
+ * @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.
- * return 0 on error.
+ * @return length of the name.
+ * @retval 0 on error.
*/
uint16_t getself_name(const Messenger *m, uint8_t *name)
{
@@ -626,15 +637,16 @@ uint16_t getself_name(const Messenger *m, uint8_t *name)
return m->name_length;
}
-/** Get name of friendnumber and put it in 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.
- * return -1 if failure.
+ * @return length of name if success.
+ * @retval -1 if failure.
*/
int getname(const Messenger *m, int32_t friendnumber, uint8_t *name)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -644,7 +656,7 @@ int getname(const Messenger *m, int32_t friendnumber, uint8_t *name)
int m_get_name_size(const Messenger *m, int32_t friendnumber)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -666,14 +678,14 @@ int m_set_statusmessage(Messenger *m, const uint8_t *status, uint16_t length)
return 0;
}
- if (length) {
+ if (length > 0) {
memcpy(m->statusmessage, status, length);
}
m->statusmessage_length = length;
for (uint32_t i = 0; i < m->numfriends; ++i) {
- m->friendlist[i].statusmessage_sent = 0;
+ m->friendlist[i].statusmessage_sent = false;
}
return 0;
@@ -692,7 +704,7 @@ int m_set_userstatus(Messenger *m, uint8_t status)
m->userstatus = (Userstatus)status;
for (uint32_t i = 0; i < m->numfriends; ++i) {
- m->friendlist[i].userstatus_sent = 0;
+ m->friendlist[i].userstatus_sent = false;
}
return 0;
@@ -701,41 +713,42 @@ int m_set_userstatus(Messenger *m, uint8_t status)
/**
* Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH.
*
- * returns the length of friendnumber's status message, including null on success.
- * returns -1 on failure.
+ * @return the length of friendnumber's status message, including null on success.
+ * @retval -1 on failure.
*/
int m_get_statusmessage_size(const Messenger *m, int32_t friendnumber)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
return m->friendlist[friendnumber].statusmessage_length;
}
-/** Copy friendnumber's status message into buf, truncating if size is over maxlen.
+/** @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.
*
- * returns the length of the copied data on success
- * returns -1 on failure.
+ * @return the length of the copied data on success
+ * @retval -1 on failure.
*/
int m_copy_statusmessage(const Messenger *m, int32_t friendnumber, uint8_t *buf, uint32_t maxlen)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
// TODO(iphydf): This should be uint16_t and min_u16. If maxlen exceeds
// uint16_t's range, it won't affect the result.
- uint32_t msglen = min_u32(maxlen, m->friendlist[friendnumber].statusmessage_length);
+ const uint32_t msglen = min_u32(maxlen, m->friendlist[friendnumber].statusmessage_length);
memcpy(buf, m->friendlist[friendnumber].statusmessage, msglen);
memset(buf + msglen, 0, maxlen - msglen);
return msglen;
}
-/** return the size of friendnumber's user status.
+/** @return the size of friendnumber's user status.
* Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH.
*/
int m_get_self_statusmessage_size(const Messenger *m)
@@ -751,7 +764,7 @@ int m_copy_self_statusmessage(const Messenger *m, uint8_t *buf)
uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return USERSTATUS_INVALID;
}
@@ -771,20 +784,16 @@ uint8_t m_get_self_userstatus(const Messenger *m)
uint64_t m_get_last_online(const Messenger *m, int32_t friendnumber)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return UINT64_MAX;
}
return m->friendlist[friendnumber].last_seen_time;
}
-int m_set_usertyping(Messenger *m, int32_t friendnumber, uint8_t is_typing)
+int m_set_usertyping(Messenger *m, int32_t friendnumber, bool is_typing)
{
- if (is_typing != 0 && is_typing != 1) {
- return -1;
- }
-
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -793,39 +802,43 @@ int m_set_usertyping(Messenger *m, int32_t friendnumber, uint8_t is_typing)
}
m->friendlist[friendnumber].user_istyping = is_typing;
- m->friendlist[friendnumber].user_istyping_sent = 0;
+ m->friendlist[friendnumber].user_istyping_sent = false;
return 0;
}
int m_get_istyping(const Messenger *m, int32_t friendnumber)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
- return m->friendlist[friendnumber].is_typing;
+ return m->friendlist[friendnumber].is_typing ? 1 : 0;
}
-static int send_statusmessage(const Messenger *m, int32_t friendnumber, const uint8_t *status, uint16_t length)
+non_null()
+static bool send_statusmessage(const Messenger *m, int32_t friendnumber, const uint8_t *status, uint16_t length)
{
- return write_cryptpacket_id(m, friendnumber, PACKET_ID_STATUSMESSAGE, status, length, 0);
+ return write_cryptpacket_id(m, friendnumber, PACKET_ID_STATUSMESSAGE, status, length, false);
}
-static int send_userstatus(const Messenger *m, int32_t friendnumber, uint8_t status)
+non_null()
+static bool send_userstatus(const Messenger *m, int32_t friendnumber, uint8_t status)
{
- return write_cryptpacket_id(m, friendnumber, PACKET_ID_USERSTATUS, &status, sizeof(status), 0);
+ return write_cryptpacket_id(m, friendnumber, PACKET_ID_USERSTATUS, &status, sizeof(status), false);
}
-static int send_user_istyping(const Messenger *m, int32_t friendnumber, uint8_t is_typing)
+non_null()
+static bool send_user_istyping(const Messenger *m, int32_t friendnumber, bool is_typing)
{
- uint8_t typing = is_typing;
- return write_cryptpacket_id(m, friendnumber, PACKET_ID_TYPING, &typing, sizeof(typing), 0);
+ const uint8_t typing = is_typing ? 1 : 0;
+ return write_cryptpacket_id(m, friendnumber, PACKET_ID_TYPING, &typing, sizeof(typing), false);
}
+non_null()
static int set_friend_statusmessage(const Messenger *m, int32_t friendnumber, const uint8_t *status, uint16_t length)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -833,7 +846,7 @@ static int set_friend_statusmessage(const Messenger *m, int32_t friendnumber, co
return -1;
}
- if (length) {
+ if (length > 0) {
memcpy(m->friendlist[friendnumber].statusmessage, status, length);
}
@@ -841,12 +854,14 @@ static int set_friend_statusmessage(const Messenger *m, int32_t friendnumber, co
return 0;
}
+non_null()
static void set_friend_userstatus(const Messenger *m, int32_t friendnumber, uint8_t status)
{
m->friendlist[friendnumber].userstatus = (Userstatus)status;
}
-static void set_friend_typing(const Messenger *m, int32_t friendnumber, uint8_t is_typing)
+non_null()
+static void set_friend_typing(const Messenger *m, int32_t friendnumber, bool is_typing)
{
m->friendlist[friendnumber].is_typing = is_typing;
}
@@ -854,7 +869,7 @@ static void set_friend_typing(const Messenger *m, int32_t friendnumber, uint8_t
/** Set the function that will be executed when a friend request is received. */
void m_callback_friendrequest(Messenger *m, m_friend_request_cb *function)
{
- callback_friendrequest(m->fr, (fr_friend_request_cb *)function, m);
+ m->friend_request = function;
}
/** Set the function that will be executed when a message from a friend is received. */
@@ -905,18 +920,19 @@ void m_callback_connectionstatus_internal_av(Messenger *m, m_friend_connectionst
m->friend_connectionstatuschange_internal_userdata = userdata;
}
+non_null(1) nullable(3)
static void check_friend_tcp_udp(Messenger *m, int32_t friendnumber, void *userdata)
{
- int last_connection_udp_tcp = m->friendlist[friendnumber].last_connection_udp_tcp;
+ const int last_connection_udp_tcp = m->friendlist[friendnumber].last_connection_udp_tcp;
- int ret = m_get_friend_connectionstatus(m, friendnumber);
+ const int ret = m_get_friend_connectionstatus(m, friendnumber);
if (ret == -1) {
return;
}
if (last_connection_udp_tcp != ret) {
- if (m->friend_connectionstatuschange) {
+ if (m->friend_connectionstatuschange != nullptr) {
m->friend_connectionstatuschange(m, friendnumber, ret, userdata);
}
}
@@ -924,113 +940,90 @@ static void check_friend_tcp_udp(Messenger *m, int32_t friendnumber, void *userd
m->friendlist[friendnumber].last_connection_udp_tcp = (Connection_Status)ret;
}
+non_null()
static void break_files(const Messenger *m, int32_t friendnumber);
+
+non_null(1) nullable(4)
static void check_friend_connectionstatus(Messenger *m, int32_t friendnumber, uint8_t status, void *userdata)
{
if (status == NOFRIEND) {
return;
}
- const uint8_t was_online = m->friendlist[friendnumber].status == FRIEND_ONLINE;
- const uint8_t is_online = status == FRIEND_ONLINE;
+ const bool was_online = m->friendlist[friendnumber].status == FRIEND_ONLINE;
+ const bool is_online = status == FRIEND_ONLINE;
if (is_online != was_online) {
if (was_online) {
break_files(m, friendnumber);
clear_receipts(m, friendnumber);
} else {
- m->friendlist[friendnumber].name_sent = 0;
- m->friendlist[friendnumber].userstatus_sent = 0;
- m->friendlist[friendnumber].statusmessage_sent = 0;
- m->friendlist[friendnumber].user_istyping_sent = 0;
+ m->friendlist[friendnumber].name_sent = false;
+ m->friendlist[friendnumber].userstatus_sent = false;
+ m->friendlist[friendnumber].statusmessage_sent = false;
+ m->friendlist[friendnumber].user_istyping_sent = false;
}
m->friendlist[friendnumber].status = status;
check_friend_tcp_udp(m, friendnumber, userdata);
- if (m->friend_connectionstatuschange_internal) {
+ if (m->friend_connectionstatuschange_internal != nullptr) {
m->friend_connectionstatuschange_internal(m, friendnumber, is_online,
m->friend_connectionstatuschange_internal_userdata);
}
}
}
+non_null(1) nullable(4)
static void set_friend_status(Messenger *m, int32_t friendnumber, uint8_t status, void *userdata)
{
check_friend_connectionstatus(m, friendnumber, status, userdata);
m->friendlist[friendnumber].status = status;
}
-static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data,
- uint32_t length, uint8_t congestion_control)
-{
- if (!friend_is_valid(m, friendnumber)) {
- return 0;
- }
-
- if (length >= MAX_CRYPTO_DATA_SIZE || m->friendlist[friendnumber].status != FRIEND_ONLINE) {
- return 0;
- }
-
- VLA(uint8_t, packet, length + 1);
- packet[0] = packet_id;
-
- if (length != 0) {
- memcpy(packet + 1, data, length);
- }
-
- return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
- m->friendlist[friendnumber].friendcon_id), packet, length + 1, congestion_control) != -1;
-}
-
/*** CONFERENCES */
-/** Set the callback for conference invites.
- */
+/** @brief Set the callback for conference invites. */
void m_callback_conference_invite(Messenger *m, m_conference_invite_cb *function)
{
m->conference_invite = function;
}
-/** Send a conference invite packet.
+/** @brief Send a conference invite packet.
*
- * return 1 on success
- * return 0 on failure
+ * return true on success
+ * return false on failure
*/
-int send_conference_invite_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length)
+bool send_conference_invite_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length)
{
- return write_cryptpacket_id(m, friendnumber, PACKET_ID_INVITE_CONFERENCE, data, length, 0);
+ return write_cryptpacket_id(m, friendnumber, PACKET_ID_INVITE_CONFERENCE, data, length, false);
}
/*** FILE SENDING */
-/** Set the callback for file send requests.
- */
+/** @brief Set the callback for file send requests. */
void callback_file_sendrequest(Messenger *m, m_file_recv_cb *function)
{
m->file_sendrequest = function;
}
-/** Set the callback for file control requests.
- */
+/** @brief Set the callback for file control requests. */
void callback_file_control(Messenger *m, m_file_recv_control_cb *function)
{
m->file_filecontrol = function;
}
-/** Set the callback for file data.
- */
+/** @brief Set the callback for file data. */
void callback_file_data(Messenger *m, m_file_recv_chunk_cb *function)
{
m->file_filedata = function;
}
-/** Set the callback for file request chunk.
- */
+/** @brief Set the callback for file request chunk. */
void callback_file_reqchunk(Messenger *m, m_file_chunk_request_cb *function)
{
m->file_reqchunk = function;
@@ -1038,15 +1031,15 @@ void callback_file_reqchunk(Messenger *m, m_file_chunk_request_cb *function)
#define MAX_FILENAME_LENGTH 255
-/** Copy the file transfer file id to file_id
+/** @brief Copy the file transfer file id to file_id
*
- * return 0 on success.
- * return -1 if friend not valid.
- * return -2 if filenumber not valid
+ * @retval 0 on success.
+ * @retval -1 if friend not valid.
+ * @retval -2 if filenumber not valid
*/
int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint8_t *file_id)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -1055,14 +1048,14 @@ int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, u
}
uint32_t temp_filenum;
- uint8_t send_receive;
+ bool inbound;
uint8_t file_number;
if (filenumber >= (1 << 16)) {
- send_receive = 1;
+ inbound = true;
temp_filenum = (filenumber >> 16) - 1;
} else {
- send_receive = 0;
+ inbound = false;
temp_filenum = filenumber;
}
@@ -1072,13 +1065,9 @@ int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, u
file_number = temp_filenum;
- struct File_Transfers *ft;
-
- if (send_receive) {
- ft = &m->friendlist[friendnumber].file_receiving[file_number];
- } else {
- ft = &m->friendlist[friendnumber].file_sending[file_number];
- }
+ const struct File_Transfers *const ft = inbound
+ ? &m->friendlist[friendnumber].file_receiving[file_number]
+ : &m->friendlist[friendnumber].file_sending[file_number];
if (ft->status == FILESTATUS_NONE) {
return -2;
@@ -1088,20 +1077,21 @@ int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, u
return 0;
}
-/** Send a file send request.
+/** @brief Send a file send request.
* Maximum filename length is 255 bytes.
- * return 1 on success
- * return 0 on failure
+ * @retval 1 on success
+ * @retval 0 on failure
*/
-static int file_sendrequest(const Messenger *m, int32_t friendnumber, uint8_t filenumber, uint32_t file_type,
- uint64_t filesize, const uint8_t *file_id, const uint8_t *filename, uint16_t filename_length)
+non_null()
+static bool file_sendrequest(const Messenger *m, int32_t friendnumber, uint8_t filenumber, uint32_t file_type,
+ uint64_t filesize, const uint8_t *file_id, const uint8_t *filename, uint16_t filename_length)
{
- if (!friend_is_valid(m, friendnumber)) {
- return 0;
+ if (!m_friend_exists(m, friendnumber)) {
+ return false;
}
if (filename_length > MAX_FILENAME_LENGTH) {
- return 0;
+ return false;
}
VLA(uint8_t, packet, 1 + sizeof(file_type) + sizeof(filesize) + FILE_ID_LENGTH + filename_length);
@@ -1111,26 +1101,27 @@ static int file_sendrequest(const Messenger *m, int32_t friendnumber, uint8_t fi
net_pack_u64(packet + 1 + sizeof(file_type), filesize);
memcpy(packet + 1 + sizeof(file_type) + sizeof(filesize), file_id, FILE_ID_LENGTH);
- if (filename_length) {
+ if (filename_length > 0) {
memcpy(packet + 1 + sizeof(file_type) + sizeof(filesize) + FILE_ID_LENGTH, filename, filename_length);
}
- return write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_SENDREQUEST, packet, SIZEOF_VLA(packet), 0);
+ return write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_SENDREQUEST, packet, SIZEOF_VLA(packet), false);
}
-/** Send a file send request.
+/** @brief Send a file send request.
+ *
* Maximum filename length is 255 bytes.
- * return file number on success
- * return -1 if friend not found.
- * return -2 if filename length invalid.
- * return -3 if no more file sending slots left.
- * return -4 if could not send packet (friend offline).
*
+ * @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).
*/
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)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -1150,7 +1141,7 @@ long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_
return -3;
}
- if (file_sendrequest(m, friendnumber, i, file_type, filesize, file_id, filename, filename_length) == 0) {
+ if (!file_sendrequest(m, friendnumber, i, file_type, filesize, file_id, filename, filename_length)) {
return -4;
}
@@ -1171,41 +1162,44 @@ long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_
return i;
}
-static int send_file_control_packet(const Messenger *m, int32_t friendnumber, uint8_t send_receive, uint8_t filenumber,
- uint8_t control_type, const uint8_t *data, uint16_t data_length)
+non_null(1) nullable(6)
+static bool send_file_control_packet(const Messenger *m, int32_t friendnumber, bool inbound, uint8_t filenumber,
+ uint8_t control_type, const uint8_t *data, uint16_t data_length)
{
+ assert(data_length == 0 || data != nullptr);
+
if ((unsigned int)(1 + 3 + data_length) > MAX_CRYPTO_DATA_SIZE) {
- return -1;
+ return false;
}
VLA(uint8_t, packet, 3 + data_length);
- packet[0] = send_receive;
+ packet[0] = inbound ? 1 : 0;
packet[1] = filenumber;
packet[2] = control_type;
- if (data_length) {
+ if (data_length > 0) {
memcpy(packet + 3, data, data_length);
}
- return write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_CONTROL, packet, SIZEOF_VLA(packet), 0);
+ return write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_CONTROL, packet, SIZEOF_VLA(packet), false);
}
-/** Send a file control request.
+/** @brief Send a file control request.
*
- * return 0 on success
- * return -1 if friend not valid.
- * return -2 if friend not online.
- * return -3 if file number invalid.
- * return -4 if file control is bad.
- * return -5 if file already paused.
- * return -6 if resume file failed because it was only paused by the other.
- * return -7 if resume file failed because it wasn't paused.
- * return -8 if packet failed to send.
+ * @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.
*/
int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, unsigned int control)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -1214,14 +1208,14 @@ int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber,
}
uint32_t temp_filenum;
- uint8_t send_receive;
+ bool inbound;
uint8_t file_number;
if (filenumber >= (1 << 16)) {
- send_receive = 1;
+ inbound = true;
temp_filenum = (filenumber >> 16) - 1;
} else {
- send_receive = 0;
+ inbound = false;
temp_filenum = filenumber;
}
@@ -1233,7 +1227,7 @@ int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber,
struct File_Transfers *ft;
- if (send_receive) {
+ if (inbound) {
ft = &m->friendlist[friendnumber].file_receiving[file_number];
} else {
ft = &m->friendlist[friendnumber].file_sending[file_number];
@@ -1247,14 +1241,14 @@ int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber,
return -4;
}
- if (control == FILECONTROL_PAUSE && ((ft->paused & FILE_PAUSE_US) || ft->status != FILESTATUS_TRANSFERRING)) {
+ if (control == FILECONTROL_PAUSE && ((ft->paused & FILE_PAUSE_US) != 0 || ft->status != FILESTATUS_TRANSFERRING)) {
return -5;
}
if (control == FILECONTROL_ACCEPT) {
if (ft->status == FILESTATUS_TRANSFERRING) {
- if (!(ft->paused & FILE_PAUSE_US)) {
- if (ft->paused & FILE_PAUSE_OTHER) {
+ if ((ft->paused & FILE_PAUSE_US) == 0) {
+ if ((ft->paused & FILE_PAUSE_OTHER) != 0) {
return -6;
}
@@ -1265,27 +1259,34 @@ int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber,
return -7;
}
- if (!send_receive) {
+ if (!inbound) {
return -6;
}
}
}
- if (send_file_control_packet(m, friendnumber, send_receive, file_number, control, nullptr, 0)) {
- if (control == FILECONTROL_KILL) {
- if (send_receive == 0 && (ft->status == FILESTATUS_TRANSFERRING || ft->status == FILESTATUS_FINISHED)) {
- // We are actively sending that file, remove from list
- --m->friendlist[friendnumber].num_sending_files;
- }
+ if (send_file_control_packet(m, friendnumber, inbound, file_number, control, nullptr, 0)) {
+ switch (control) {
+ case FILECONTROL_KILL: {
+ if (!inbound && (ft->status == FILESTATUS_TRANSFERRING || ft->status == FILESTATUS_FINISHED)) {
+ // We are actively sending that file, remove from list
+ --m->friendlist[friendnumber].num_sending_files;
+ }
- ft->status = FILESTATUS_NONE;
- } else if (control == FILECONTROL_PAUSE) {
- ft->paused |= FILE_PAUSE_US;
- } else if (control == FILECONTROL_ACCEPT) {
- ft->status = FILESTATUS_TRANSFERRING;
+ ft->status = FILESTATUS_NONE;
+ break;
+ }
+ case FILECONTROL_PAUSE: {
+ ft->paused |= FILE_PAUSE_US;
+ break;
+ }
+ case FILECONTROL_ACCEPT: {
+ ft->status = FILESTATUS_TRANSFERRING;
- if (ft->paused & FILE_PAUSE_US) {
- ft->paused ^= FILE_PAUSE_US;
+ if ((ft->paused & FILE_PAUSE_US) != 0) {
+ ft->paused ^= FILE_PAUSE_US;
+ }
+ break;
}
}
} else {
@@ -1295,20 +1296,20 @@ int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber,
return 0;
}
-/** Send a seek file control request.
+/** @brief Send a seek file control request.
*
- * return 0 on success
- * return -1 if friend not valid.
- * return -2 if friend not online.
- * return -3 if file number invalid.
- * return -4 if not receiving file.
- * return -5 if file status wrong.
- * return -6 if position bad.
- * return -8 if packet failed to send.
+ * @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.
*/
int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -1321,14 +1322,14 @@ int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uin
return -4;
}
- uint32_t temp_filenum = (filenumber >> 16) - 1;
+ const uint32_t temp_filenum = (filenumber >> 16) - 1;
if (temp_filenum >= MAX_CONCURRENT_FILE_PIPES) {
return -3;
}
assert(temp_filenum <= UINT8_MAX);
- uint8_t file_number = temp_filenum;
+ const uint8_t file_number = temp_filenum;
// We're always receiving at this point.
struct File_Transfers *ft = &m->friendlist[friendnumber].file_receiving[file_number];
@@ -1348,7 +1349,7 @@ int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uin
uint8_t sending_pos[sizeof(uint64_t)];
net_pack_u64(sending_pos, position);
- if (send_file_control_packet(m, friendnumber, 1, file_number, FILECONTROL_SEEK, sending_pos,
+ if (send_file_control_packet(m, friendnumber, true, file_number, FILECONTROL_SEEK, sending_pos,
sizeof(sending_pos))) {
ft->transferred = position;
} else {
@@ -1358,13 +1359,16 @@ int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uin
return 0;
}
-/** return packet number on success.
- * return -1 on failure.
+/** @return packet number on success.
+ * @retval -1 on failure.
*/
+non_null(1) nullable(4)
static int64_t send_file_data_packet(const Messenger *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data,
uint16_t length)
{
- if (!friend_is_valid(m, friendnumber)) {
+ assert(length == 0 || data != nullptr);
+
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -1372,31 +1376,33 @@ static int64_t send_file_data_packet(const Messenger *m, int32_t friendnumber, u
packet[0] = PACKET_ID_FILE_DATA;
packet[1] = filenumber;
- if (length) {
+ if (length > 0) {
memcpy(packet + 2, data, length);
}
return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
- m->friendlist[friendnumber].friendcon_id), packet, SIZEOF_VLA(packet), 1);
+ m->friendlist[friendnumber].friendcon_id), packet, SIZEOF_VLA(packet), true);
}
#define MAX_FILE_DATA_SIZE (MAX_CRYPTO_DATA_SIZE - 2)
#define MIN_SLOTS_FREE (CRYPTO_MIN_QUEUE_LENGTH / 4)
-/** Send file data.
+/** @brief Send file data.
*
- * return 0 on success
- * return -1 if friend not valid.
- * return -2 if friend not online.
- * return -3 if filenumber invalid.
- * return -4 if file transfer not transferring.
- * return -5 if bad data size.
- * return -6 if packet queue full.
- * return -7 if wrong position.
+ * @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.
*/
-int file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
- uint16_t length)
+int send_file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position,
+ const uint8_t *data, uint16_t length)
{
- if (!friend_is_valid(m, friendnumber)) {
+ assert(length == 0 || data != nullptr);
+
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -1437,7 +1443,7 @@ int file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uin
return -6;
}
- int64_t ret = send_file_data_packet(m, friendnumber, filenumber, data, length);
+ const int64_t ret = send_file_data_packet(m, friendnumber, filenumber, data, length);
if (ret != -1) {
// TODO(irungentoo): record packet ids to check if other received complete file.
@@ -1468,6 +1474,7 @@ int file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uin
* @return true if there's still work to do, false otherwise.
*
*/
+non_null()
static bool do_all_filetransfers(Messenger *m, int32_t friendnumber, void *userdata, uint32_t *free_slots)
{
Friend *const friendcon = &m->friendlist[friendnumber];
@@ -1491,9 +1498,16 @@ static bool do_all_filetransfers(Messenger *m, int32_t friendnumber, void *userd
continue;
}
+ if (max_speed_reached(m->net_crypto, friend_connection_crypt_connection_id(
+ m->fr_c, friendcon->friendcon_id))) {
+ LOGGER_DEBUG(m->log, "maximum connection speed reached");
+ // connection doesn't support any more data
+ return false;
+ }
+
// If the file transfer is complete, we request a chunk of size 0.
if (ft->status == FILESTATUS_FINISHED && friend_received_packet(m, friendnumber, ft->last_packet_number) == 0) {
- if (m->file_reqchunk) {
+ if (m->file_reqchunk != nullptr) {
m->file_reqchunk(m, friendnumber, i, ft->transferred, 0, userdata);
}
@@ -1503,7 +1517,7 @@ static bool do_all_filetransfers(Messenger *m, int32_t friendnumber, void *userd
} else if (ft->status == FILESTATUS_TRANSFERRING && ft->paused == FILE_PAUSE_NOT) {
if (ft->size == 0) {
/* Send 0 data to friend if file is 0 length. */
- file_data(m, friendnumber, i, 0, nullptr, 0);
+ send_file_data(m, friendnumber, i, 0, nullptr, 0);
continue;
}
@@ -1516,26 +1530,19 @@ static bool do_all_filetransfers(Messenger *m, int32_t friendnumber, void *userd
const uint64_t position = ft->requested;
ft->requested += length;
- if (m->file_reqchunk) {
+ if (m->file_reqchunk != nullptr) {
m->file_reqchunk(m, friendnumber, i, position, length, userdata);
}
// The allocated slot is no longer free.
--*free_slots;
}
-
- // Must be last to allow finishing file transfers
- if (max_speed_reached(m->net_crypto, friend_connection_crypt_connection_id(
- m->fr_c, friendcon->friendcon_id))) {
- LOGGER_TRACE(m->log, "Maximum connection speed reached");
- // connection doesn't support any more data
- return false;
- }
}
return true;
}
+non_null(1) nullable(3)
static void do_reqchunk_filecb(Messenger *m, int32_t friendnumber, void *userdata)
{
// We're not currently doing any file transfers.
@@ -1560,7 +1567,9 @@ static void do_reqchunk_filecb(Messenger *m, int32_t friendnumber, void *userdat
// that the file transfer has finished and may end up in an infinite loop.
//
// Request up to that number of chunks per file from the client
- const uint32_t max_ft_loops = 16;
+ //
+ // TODO(Jfreegman): set this cap dynamically
+ const uint32_t max_ft_loops = 128;
for (uint32_t i = 0; i < max_ft_loops; ++i) {
if (!do_all_filetransfers(m, friendnumber, userdata, &free_slots)) {
@@ -1575,34 +1584,32 @@ static void do_reqchunk_filecb(Messenger *m, int32_t friendnumber, void *userdat
}
-/** Run this when the friend disconnects.
- * Kill all current file transfers.
+/** @brief Run this when the friend disconnects.
+ * Kill all current file transfers.
*/
static void break_files(const Messenger *m, int32_t friendnumber)
{
+ Friend *const f = &m->friendlist[friendnumber];
+
// TODO(irungentoo): Inform the client which file transfers get killed with a callback?
for (uint32_t i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) {
- if (m->friendlist[friendnumber].file_sending[i].status != FILESTATUS_NONE) {
- m->friendlist[friendnumber].file_sending[i].status = FILESTATUS_NONE;
- }
-
- if (m->friendlist[friendnumber].file_receiving[i].status != FILESTATUS_NONE) {
- m->friendlist[friendnumber].file_receiving[i].status = FILESTATUS_NONE;
- }
+ f->file_sending[i].status = FILESTATUS_NONE;
+ f->file_receiving[i].status = FILESTATUS_NONE;
}
}
-static struct File_Transfers *get_file_transfer(uint8_t receive_send, uint8_t filenumber,
+non_null()
+static struct File_Transfers *get_file_transfer(bool outbound, uint8_t filenumber,
uint32_t *real_filenumber, Friend *sender)
{
struct File_Transfers *ft;
- if (receive_send == 0) {
- *real_filenumber = (filenumber + 1) << 16;
- ft = &sender->file_receiving[filenumber];
- } else {
+ if (outbound) {
*real_filenumber = filenumber;
ft = &sender->file_sending[filenumber];
+ } else {
+ *real_filenumber = (filenumber + 1) << 16;
+ ft = &sender->file_receiving[filenumber];
}
if (ft->status == FILESTATUS_NONE) {
@@ -1612,34 +1619,30 @@ static struct File_Transfers *get_file_transfer(uint8_t receive_send, uint8_t fi
return ft;
}
-/** return -1 on failure, 0 on success.
+/** @retval -1 on failure
+ * @retval 0 on success.
*/
-static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
+non_null(1, 6) nullable(8)
+static int handle_filecontrol(Messenger *m, int32_t friendnumber, bool outbound, uint8_t filenumber,
uint8_t control_type, const uint8_t *data, uint16_t length, void *userdata)
{
- if (receive_send > 1) {
- LOGGER_DEBUG(m->log, "file control (friend %d, file %d): receive_send value is invalid (should be 0 or 1): %d",
- friendnumber, filenumber, receive_send);
- return -1;
- }
-
uint32_t real_filenumber;
- struct File_Transfers *ft = get_file_transfer(receive_send, filenumber, &real_filenumber, &m->friendlist[friendnumber]);
+ struct File_Transfers *ft = get_file_transfer(outbound, filenumber, &real_filenumber, &m->friendlist[friendnumber]);
if (ft == nullptr) {
LOGGER_DEBUG(m->log, "file control (friend %d, file %d): file transfer does not exist; telling the other to kill it",
friendnumber, filenumber);
- send_file_control_packet(m, friendnumber, !receive_send, filenumber, FILECONTROL_KILL, nullptr, 0);
+ send_file_control_packet(m, friendnumber, !outbound, filenumber, FILECONTROL_KILL, nullptr, 0);
return -1;
}
switch (control_type) {
case FILECONTROL_ACCEPT: {
- if (receive_send && ft->status == FILESTATUS_NOT_ACCEPTED) {
+ if (outbound && ft->status == FILESTATUS_NOT_ACCEPTED) {
ft->status = FILESTATUS_TRANSFERRING;
++m->friendlist[friendnumber].num_sending_files;
} else {
- if (ft->paused & FILE_PAUSE_OTHER) {
+ if ((ft->paused & FILE_PAUSE_OTHER) != 0) {
ft->paused ^= FILE_PAUSE_OTHER;
} else {
LOGGER_DEBUG(m->log, "file control (friend %d, file %d): friend told us to resume file transfer that wasn't paused",
@@ -1648,7 +1651,7 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv
}
}
- if (m->file_filecontrol) {
+ if (m->file_filecontrol != nullptr) {
m->file_filecontrol(m, friendnumber, real_filenumber, control_type, userdata);
}
@@ -1656,7 +1659,7 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv
}
case FILECONTROL_PAUSE: {
- if ((ft->paused & FILE_PAUSE_OTHER) || ft->status != FILESTATUS_TRANSFERRING) {
+ if ((ft->paused & FILE_PAUSE_OTHER) != 0 || ft->status != FILESTATUS_TRANSFERRING) {
LOGGER_DEBUG(m->log, "file control (friend %d, file %d): friend told us to pause file transfer that is already paused",
friendnumber, filenumber);
return -1;
@@ -1664,7 +1667,7 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv
ft->paused |= FILE_PAUSE_OTHER;
- if (m->file_filecontrol) {
+ if (m->file_filecontrol != nullptr) {
m->file_filecontrol(m, friendnumber, real_filenumber, control_type, userdata);
}
@@ -1672,11 +1675,11 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv
}
case FILECONTROL_KILL: {
- if (m->file_filecontrol) {
+ if (m->file_filecontrol != nullptr) {
m->file_filecontrol(m, friendnumber, real_filenumber, control_type, userdata);
}
- if (receive_send && (ft->status == FILESTATUS_TRANSFERRING || ft->status == FILESTATUS_FINISHED)) {
+ if (outbound && (ft->status == FILESTATUS_TRANSFERRING || ft->status == FILESTATUS_FINISHED)) {
--m->friendlist[friendnumber].num_sending_files;
}
@@ -1695,7 +1698,7 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv
}
/* seek can only be sent by the receiver to seek before resuming broken transfers. */
- if (ft->status != FILESTATUS_NOT_ACCEPTED || !receive_send) {
+ if (ft->status != FILESTATUS_NOT_ACCEPTED || !outbound) {
LOGGER_DEBUG(m->log,
"file control (friend %d, file %d): seek was either sent by a sender or by the receiver after accepting",
friendnumber, filenumber);
@@ -1724,22 +1727,21 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv
}
}
-/** Set the callback for msi packets.
- */
+/** @brief Set the callback for msi packets. */
void m_callback_msi_packet(Messenger *m, m_msi_packet_cb *function, void *userdata)
{
m->msi_packet = function;
m->msi_packet_userdata = userdata;
}
-/** Send an msi packet.
+/** @brief Send an msi packet.
*
- * return 1 on success
- * return 0 on failure
+ * @retval true on success
+ * @retval false on failure
*/
-int m_msi_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length)
+bool m_msi_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length)
{
- return write_cryptpacket_id(m, friendnumber, PACKET_ID_MSI, data, length, 0);
+ return write_cryptpacket_id(m, friendnumber, PACKET_ID_MSI, data, length, false);
}
static int m_handle_lossy_packet(void *object, int friend_num, const uint8_t *packet, uint16_t length,
@@ -1747,7 +1749,7 @@ static int m_handle_lossy_packet(void *object, int friend_num, const uint8_t *pa
{
Messenger *m = (Messenger *)object;
- if (!friend_is_valid(m, friend_num)) {
+ if (!m_friend_exists(m, friend_num)) {
return 1;
}
@@ -1755,14 +1757,14 @@ static int m_handle_lossy_packet(void *object, int friend_num, const uint8_t *pa
const RTP_Packet_Handler *const ph =
&m->friendlist[friend_num].lossy_rtp_packethandlers[packet[0] % PACKET_ID_RANGE_LOSSY_AV_SIZE];
- if (ph->function) {
+ if (ph->function != nullptr) {
return ph->function(m, friend_num, packet, length, ph->object);
}
return 1;
}
- if (m->lossy_packethandler) {
+ if (m->lossy_packethandler != nullptr) {
m->lossy_packethandler(m, friend_num, packet[0], packet, length, userdata);
}
@@ -1777,7 +1779,7 @@ void custom_lossy_packet_registerhandler(Messenger *m, m_friend_lossy_packet_cb
int m_callback_rtp_packet(Messenger *m, int32_t friendnumber, uint8_t byte, m_lossy_rtp_packet_cb *function,
void *object)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -1791,7 +1793,7 @@ int m_callback_rtp_packet(Messenger *m, int32_t friendnumber, uint8_t byte, m_lo
}
-/** High level function to send custom lossy packets.
+/** @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
@@ -1800,16 +1802,16 @@ int m_callback_rtp_packet(Messenger *m, int32_t friendnumber, uint8_t byte, m_lo
* I.e. custom_lossy_packet_registerhandler's "custom lossy packet" and this "custom lossy packet"
* are not the same set of packets.
*
- * return -1 if friend invalid.
- * return -2 if length wrong.
- * return -3 if first byte invalid.
- * return -4 if friend offline.
- * return -5 if packet failed to send because of other error.
- * return 0 on success.
+ * @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.
*/
int m_send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -1834,12 +1836,13 @@ int m_send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const u
return 0;
}
+non_null(1, 3) nullable(5)
static int handle_custom_lossless_packet(void *object, int friend_num, const uint8_t *packet, uint16_t length,
void *userdata)
{
Messenger *m = (Messenger *)object;
- if (!friend_is_valid(m, friend_num)) {
+ if (!m_friend_exists(m, friend_num)) {
return -1;
}
@@ -1847,7 +1850,7 @@ static int handle_custom_lossless_packet(void *object, int friend_num, const uin
return -1;
}
- if (m->lossless_packethandler) {
+ if (m->lossless_packethandler != nullptr) {
m->lossless_packethandler(m, friend_num, packet[0], packet, length, userdata);
}
@@ -1861,7 +1864,7 @@ void custom_lossless_packet_registerhandler(Messenger *m, m_friend_lossless_pack
int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length)
{
- if (!friend_is_valid(m, friendnumber)) {
+ if (!m_friend_exists(m, friendnumber)) {
return -1;
}
@@ -1879,7 +1882,7 @@ int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const
}
if (write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
- m->friendlist[friendnumber].friendcon_id), data, length, 1) == -1) {
+ m->friendlist[friendnumber].friendcon_id), data, length, true) == -1) {
return -5;
}
@@ -1887,6 +1890,7 @@ int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const
}
/** Function to filter out some friend requests*/
+non_null()
static int friend_already_added(const uint8_t *real_pk, void *data)
{
const Messenger *m = (const Messenger *)data;
@@ -1898,193 +1902,13 @@ static int friend_already_added(const uint8_t *real_pk, void *data)
return -1;
}
-/** Run this at startup.
- * return allocated instance of Messenger on success.
- * return 0 if there are problems.
+/** @brief Check for and handle a timed-out friend request.
*
- * if error is not NULL it will be set to one of the values in the enum above.
- */
-Messenger *new_messenger(Mono_Time *mono_time, Messenger_Options *options, unsigned int *error)
-{
- if (!options) {
- return nullptr;
- }
-
- if (error) {
- *error = MESSENGER_ERROR_OTHER;
- }
-
- Messenger *m = (Messenger *)calloc(1, sizeof(Messenger));
-
- if (!m) {
- return nullptr;
- }
-
- m->mono_time = mono_time;
-
- m->fr = friendreq_new();
-
- if (!m->fr) {
- free(m);
- return nullptr;
- }
-
- m->log = logger_new();
-
- if (m->log == nullptr) {
- friendreq_kill(m->fr);
- free(m);
- return nullptr;
- }
-
- logger_callback_log(m->log, options->log_callback, options->log_context, options->log_user_data);
-
- unsigned int net_err = 0;
-
- if (!options->udp_disabled && options->proxy_info.proxy_type != TCP_PROXY_NONE) {
- // We don't currently support UDP over proxy.
- LOGGER_WARNING(m->log, "UDP enabled and proxy set: disabling UDP");
- options->udp_disabled = true;
- }
-
- if (options->udp_disabled) {
- m->net = new_networking_no_udp(m->log);
- } else {
- IP ip;
- ip_init(&ip, options->ipv6enabled);
- m->net = new_networking_ex(m->log, &ip, options->port_range[0], options->port_range[1], &net_err);
- }
-
- if (m->net == nullptr) {
- friendreq_kill(m->fr);
- logger_kill(m->log);
- free(m);
-
- if (error && net_err == 1) {
- *error = MESSENGER_ERROR_PORT;
- }
-
- return nullptr;
- }
-
- m->dht = new_dht(m->log, m->mono_time, m->net, options->hole_punching_enabled);
-
- if (m->dht == nullptr) {
- kill_networking(m->net);
- friendreq_kill(m->fr);
- logger_kill(m->log);
- free(m);
- return nullptr;
- }
-
- m->net_crypto = new_net_crypto(m->log, m->mono_time, m->dht, &options->proxy_info);
-
- if (m->net_crypto == nullptr) {
- kill_dht(m->dht);
- kill_networking(m->net);
- friendreq_kill(m->fr);
- logger_kill(m->log);
- free(m);
- return nullptr;
- }
-
- m->onion = new_onion(m->log, m->mono_time, m->dht);
- m->onion_a = new_onion_announce(m->log, m->mono_time, m->dht);
- m->onion_c = new_onion_client(m->log, m->mono_time, m->net_crypto);
- m->fr_c = new_friend_connections(m->log, m->mono_time, m->onion_c, options->local_discovery_enabled);
-
- if (!(m->onion && m->onion_a && m->onion_c && m->fr_c)) {
- kill_friend_connections(m->fr_c);
- kill_onion(m->onion);
- kill_onion_announce(m->onion_a);
- kill_onion_client(m->onion_c);
- kill_net_crypto(m->net_crypto);
- kill_dht(m->dht);
- kill_networking(m->net);
- friendreq_kill(m->fr);
- logger_kill(m->log);
- free(m);
- return nullptr;
- }
-
- if (options->tcp_server_port) {
- m->tcp_server = new_TCP_server(m->log, options->ipv6enabled, 1, &options->tcp_server_port,
- dht_get_self_secret_key(m->dht), m->onion);
-
- if (m->tcp_server == nullptr) {
- kill_friend_connections(m->fr_c);
- kill_onion(m->onion);
- kill_onion_announce(m->onion_a);
- kill_onion_client(m->onion_c);
- kill_net_crypto(m->net_crypto);
- kill_dht(m->dht);
- kill_networking(m->net);
- friendreq_kill(m->fr);
- logger_kill(m->log);
- free(m);
-
- if (error) {
- *error = MESSENGER_ERROR_TCP_SERVER;
- }
-
- return nullptr;
- }
- }
-
- m->options = *options;
- friendreq_init(m->fr, m->fr_c);
- set_nospam(m->fr, random_u32());
- set_filter_function(m->fr, &friend_already_added, m);
-
- m->lastdump = 0;
-
- m_register_default_plugins(m);
-
- if (error) {
- *error = MESSENGER_ERROR_NONE;
- }
-
- return m;
-}
-
-/** Run this before closing shop
- * Free all datastructures.
- */
-void kill_messenger(Messenger *m)
-{
- if (!m) {
- return;
- }
-
- if (m->tcp_server) {
- kill_TCP_server(m->tcp_server);
- }
-
- kill_friend_connections(m->fr_c);
- kill_onion(m->onion);
- kill_onion_announce(m->onion_a);
- kill_onion_client(m->onion_c);
- kill_net_crypto(m->net_crypto);
- kill_dht(m->dht);
- kill_networking(m->net);
-
- for (uint32_t i = 0; i < m->numfriends; ++i) {
- clear_receipts(m, i);
- }
-
- logger_kill(m->log);
- free(m->friendlist);
- friendreq_kill(m->fr);
-
- free(m->options.state_plugins);
- free(m);
-}
-
-/** Check for and handle a timed-out friend request. If the request has
- * timed-out then the friend status is set back to FRIEND_ADDED.
- * i: friendlist index of the timed-out friend
- * t: time
+ * If the request has timed-out then the friend status is set back to FRIEND_ADDED.
+ * @param i friendlist index of the timed-out friend
+ * @param t time
*/
+non_null(1) nullable(4)
static void check_friend_request_timed_out(Messenger *m, uint32_t i, uint64_t t, void *userdata)
{
Friend *f = &m->friendlist[i];
@@ -2098,7 +1922,7 @@ static void check_friend_request_timed_out(Messenger *m, uint32_t i, uint64_t t,
}
}
-static int m_handle_status(void *object, int i, uint8_t status, void *userdata)
+static int m_handle_status(void *object, int i, bool status, void *userdata)
{
Messenger *m = (Messenger *)object;
@@ -2120,9 +1944,9 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
}
Messenger *m = (Messenger *)object;
- uint8_t packet_id = temp[0];
+ const uint8_t packet_id = temp[0];
const uint8_t *data = temp + 1;
- uint16_t data_length = len - 1;
+ const uint16_t data_length = len - 1;
if (m->friendlist[i].status != FRIEND_ONLINE) {
if (packet_id == PACKET_ID_ONLINE && len == 1) {
@@ -2135,7 +1959,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
switch (packet_id) {
case PACKET_ID_OFFLINE: {
- if (data_length != 0) {
+ if (data_length > 0) {
break;
}
@@ -2154,7 +1978,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
data_terminated[data_length] = 0;
/* inform of namechange before we overwrite the old name */
- if (m->friend_namechange) {
+ if (m->friend_namechange != nullptr) {
m->friend_namechange(m, i, data_terminated, data_length, userdata);
}
@@ -2174,7 +1998,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
memcpy(data_terminated, data, data_length);
data_terminated[data_length] = 0;
- if (m->friend_statusmessagechange) {
+ if (m->friend_statusmessagechange != nullptr) {
m->friend_statusmessagechange(m, i, data_terminated, data_length, userdata);
}
@@ -2187,13 +2011,13 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
break;
}
- Userstatus status = (Userstatus)data[0];
+ const Userstatus status = (Userstatus)data[0];
if (status >= USERSTATUS_INVALID) {
break;
}
- if (m->friend_userstatuschange) {
+ if (m->friend_userstatuschange != nullptr) {
m->friend_userstatuschange(m, i, status, userdata);
}
@@ -2206,11 +2030,11 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
break;
}
- bool typing = !!data[0];
+ const bool typing = data[0] != 0;
set_friend_typing(m, i, typing);
- if (m->friend_typingchange) {
+ if (m->friend_typingchange != nullptr) {
m->friend_typingchange(m, i, typing, userdata);
}
@@ -2224,15 +2048,15 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
}
const uint8_t *message = data;
- uint16_t message_length = data_length;
+ const uint16_t message_length = data_length;
/* Make sure the NULL terminator is present. */
VLA(uint8_t, message_terminated, message_length + 1);
memcpy(message_terminated, message, message_length);
message_terminated[message_length] = 0;
- uint8_t type = packet_id - PACKET_ID_MESSAGE;
+ const uint8_t type = packet_id - PACKET_ID_MESSAGE;
- if (m->friend_message) {
+ if (m->friend_message != nullptr) {
m->friend_message(m, i, type, message_terminated, message_length, userdata);
}
@@ -2244,7 +2068,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
break;
}
- if (m->conference_invite) {
+ if (m->conference_invite != nullptr) {
m->conference_invite(m, i, data, data_length, userdata);
}
@@ -2258,7 +2082,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
break;
}
- uint8_t filenumber = data[0];
+ const uint8_t filenumber = data[0];
#if UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES
@@ -2270,7 +2094,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
uint64_t filesize;
uint32_t file_type;
- uint16_t filename_length = data_length - head_length;
+ const uint16_t filename_length = data_length - head_length;
if (filename_length > MAX_FILENAME_LENGTH) {
break;
@@ -2295,7 +2119,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
VLA(uint8_t, filename_terminated, filename_length + 1);
const uint8_t *filename = nullptr;
- if (filename_length) {
+ if (filename_length > 0) {
/* Force NULL terminate file name. */
memcpy(filename_terminated, data + head_length, filename_length);
filename_terminated[filename_length] = 0;
@@ -2306,7 +2130,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
real_filenumber += 1;
real_filenumber <<= 16;
- if (m->file_sendrequest) {
+ if (m->file_sendrequest != nullptr) {
m->file_sendrequest(m, i, real_filenumber, file_type, filesize, filename, filename_length,
userdata);
}
@@ -2319,9 +2143,12 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
break;
}
- uint8_t send_receive = data[0];
+ // On the other side, "outbound" is "inbound", i.e. if they send 1,
+ // that means "inbound" on their side, but we call it "outbound"
+ // here.
+ const bool outbound = data[0] == 1;
uint8_t filenumber = data[1];
- uint8_t control_type = data[2];
+ const uint8_t control_type = data[2];
#if UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES
@@ -2331,7 +2158,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
#endif
- if (handle_filecontrol(m, i, send_receive, filenumber, control_type, data + 3, data_length - 3, userdata) == -1) {
+ if (handle_filecontrol(m, i, outbound, filenumber, control_type, data + 3, data_length - 3, userdata) == -1) {
// TODO(iphydf): Do something different here? Right now, this
// check is pointless.
break;
@@ -2379,19 +2206,19 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
file_data_length = ft->size - ft->transferred;
}
- if (m->file_filedata) {
+ if (m->file_filedata != nullptr) {
m->file_filedata(m, i, real_filenumber, position, file_data, file_data_length, userdata);
}
ft->transferred += file_data_length;
- if (file_data_length && (ft->transferred >= ft->size || file_data_length != MAX_FILE_DATA_SIZE)) {
+ if (file_data_length > 0 && (ft->transferred >= ft->size || file_data_length != MAX_FILE_DATA_SIZE)) {
file_data_length = 0;
file_data = nullptr;
position = ft->transferred;
/* Full file received. */
- if (m->file_filedata) {
+ if (m->file_filedata != nullptr) {
m->file_filedata(m, i, real_filenumber, position, file_data, file_data_length, userdata);
}
}
@@ -2409,7 +2236,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
break;
}
- if (m->msi_packet) {
+ if (m->msi_packet != nullptr) {
m->msi_packet(m, i, data, data_length, m->msi_packet_userdata);
}
@@ -2425,13 +2252,14 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le
return 0;
}
+non_null(1) nullable(2)
static void do_friends(Messenger *m, void *userdata)
{
- uint64_t temp_time = mono_time_get(m->mono_time);
+ const uint64_t temp_time = mono_time_get(m->mono_time);
for (uint32_t i = 0; i < m->numfriends; ++i) {
if (m->friendlist[i].status == FRIEND_ADDED) {
- int fr = send_friend_request_packet(m->fr_c, m->friendlist[i].friendcon_id, m->friendlist[i].friendrequest_nospam,
+ const int fr = send_friend_request_packet(m->fr_c, m->friendlist[i].friendcon_id, m->friendlist[i].friendrequest_nospam,
m->friendlist[i].info,
m->friendlist[i].info_size);
@@ -2452,27 +2280,27 @@ static void do_friends(Messenger *m, void *userdata)
}
if (m->friendlist[i].status == FRIEND_ONLINE) { /* friend is online. */
- if (m->friendlist[i].name_sent == 0) {
+ if (!m->friendlist[i].name_sent) {
if (m_sendname(m, i, m->name, m->name_length)) {
- m->friendlist[i].name_sent = 1;
+ m->friendlist[i].name_sent = true;
}
}
- if (m->friendlist[i].statusmessage_sent == 0) {
+ if (!m->friendlist[i].statusmessage_sent) {
if (send_statusmessage(m, i, m->statusmessage, m->statusmessage_length)) {
- m->friendlist[i].statusmessage_sent = 1;
+ m->friendlist[i].statusmessage_sent = true;
}
}
- if (m->friendlist[i].userstatus_sent == 0) {
+ if (!m->friendlist[i].userstatus_sent) {
if (send_userstatus(m, i, m->userstatus)) {
- m->friendlist[i].userstatus_sent = 1;
+ m->friendlist[i].userstatus_sent = true;
}
}
- if (m->friendlist[i].user_istyping_sent == 0) {
+ if (!m->friendlist[i].user_istyping_sent) {
if (send_user_istyping(m, i, m->friendlist[i].user_istyping)) {
- m->friendlist[i].user_istyping_sent = 1;
+ m->friendlist[i].user_istyping_sent = true;
}
}
@@ -2485,12 +2313,13 @@ static void do_friends(Messenger *m, void *userdata)
}
}
-static void connection_status_callback(Messenger *m, void *userdata)
+non_null(1) nullable(2)
+static void m_connection_status_callback(Messenger *m, void *userdata)
{
- unsigned int conn_status = onion_connection_status(m->onion_c);
+ const Onion_Connection_Status conn_status = onion_connection_status(m->onion_c);
if (conn_status != m->last_connection_status) {
- if (m->core_connection_change) {
+ if (m->core_connection_change != nullptr) {
m->core_connection_change(m, conn_status, userdata);
}
@@ -2503,6 +2332,7 @@ static void connection_status_callback(Messenger *m, void *userdata)
#define IDSTRING_LEN (CRYPTO_PUBLIC_KEY_SIZE * 2 + 1)
/** id_str should be of length at least IDSTRING_LEN */
+non_null()
static char *id_to_string(const uint8_t *pk, char *id_str, size_t length)
{
if (length < IDSTRING_LEN) {
@@ -2514,22 +2344,24 @@ static char *id_to_string(const uint8_t *pk, char *id_str, size_t length)
snprintf(&id_str[i * 2], length - i * 2, "%02X", pk[i]);
}
- id_str[CRYPTO_PUBLIC_KEY_SIZE * 2] = 0;
+ id_str[CRYPTO_PUBLIC_KEY_SIZE * 2] = '\0';
return id_str;
}
-/** Minimum messenger run interval in ms
- * TODO(mannol): A/V */
+/** @brief Minimum messenger run interval in ms
+ * TODO(mannol): A/V
+ */
#define MIN_RUN_INTERVAL 50
-/** Return the time in milliseconds before do_messenger() should be called again
- * for optimal performance.
+/**
+ * @brief Return the time in milliseconds before `do_messenger()` should be called again
+ * for optimal performance.
*
- * returns time (in ms) before the next do_messenger() needs to be run on success.
+ * @return time (in ms) before the next `do_messenger()` needs to be run on success.
*/
uint32_t messenger_run_interval(const Messenger *m)
{
- uint32_t crypto_interval = crypto_run_interval(m->net_crypto);
+ const uint32_t crypto_interval = crypto_run_interval(m->net_crypto);
if (crypto_interval > MIN_RUN_INTERVAL) {
return MIN_RUN_INTERVAL;
@@ -2538,7 +2370,7 @@ uint32_t messenger_run_interval(const Messenger *m)
return crypto_interval;
}
-/** The main loop that needs to be run at least 20 times per second. */
+/** @brief The main loop that needs to be run at least 20 times per second. */
void do_messenger(Messenger *m, void *userdata)
{
// Add the TCP relays, but only if this is the first time calling do_messenger
@@ -2551,11 +2383,11 @@ void do_messenger(Messenger *m, void *userdata)
m->num_loaded_relays = 0;
- if (m->tcp_server) {
+ if (m->tcp_server != nullptr) {
/* Add self tcp server. */
IP_Port local_ip_port;
local_ip_port.port = m->options.tcp_server_port;
- local_ip_port.ip.family = net_family_ipv4;
+ local_ip_port.ip.family = net_family_ipv4();
local_ip_port.ip.ip.v4 = get_ip4_loopback();
add_tcp_relay(m->net_crypto, &local_ip_port, tcp_server_public_key(m->tcp_server));
}
@@ -2566,7 +2398,7 @@ void do_messenger(Messenger *m, void *userdata)
do_dht(m->dht);
}
- if (m->tcp_server) {
+ if (m->tcp_server != nullptr) {
do_TCP_server(m->tcp_server, m->mono_time);
}
@@ -2574,7 +2406,7 @@ void do_messenger(Messenger *m, void *userdata)
do_onion_client(m->onion_c);
do_friend_connections(m->fr_c, userdata);
do_friends(m, userdata);
- connection_status_callback(m, userdata);
+ m_connection_status_callback(m, userdata);
if (mono_time_get(m->mono_time) > m->lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) {
m->lastdump = mono_time_get(m->mono_time);
@@ -2584,7 +2416,7 @@ void do_messenger(Messenger *m, void *userdata)
const Client_data *cptr = dht_get_close_client(m->dht, client);
const IPPTsPng *const assocs[] = { &cptr->assoc4, &cptr->assoc6, nullptr };
- for (const IPPTsPng * const *it = assocs; *it; ++it) {
+ for (const IPPTsPng * const *it = assocs; *it != nullptr; ++it) {
const IPPTsPng *const assoc = *it;
if (ip_isset(&assoc->ip_port.ip)) {
@@ -2594,10 +2426,10 @@ void do_messenger(Messenger *m, void *userdata)
last_pinged = 999;
}
- char ip_str[IP_NTOA_LEN];
+ Ip_Ntoa ip_str;
char id_str[IDSTRING_LEN];
LOGGER_TRACE(m->log, "C[%2u] %s:%u [%3u] %s",
- client, ip_ntoa(&assoc->ip_port.ip, ip_str, sizeof(ip_str)),
+ client, net_ip_ntoa(&assoc->ip_port.ip, &ip_str),
net_ntohs(assoc->ip_port.port), last_pinged,
id_to_string(cptr->public_key, id_str, sizeof(id_str)));
}
@@ -2606,7 +2438,7 @@ void do_messenger(Messenger *m, void *userdata)
/* dht contains additional "friends" (requests) */
- uint32_t num_dhtfriends = dht_get_num_friends(m->dht);
+ const uint32_t num_dhtfriends = dht_get_num_friends(m->dht);
VLA(int32_t, m2dht, num_dhtfriends);
VLA(int32_t, dht2m, num_dhtfriends);
@@ -2619,8 +2451,9 @@ void do_messenger(Messenger *m, void *userdata)
}
for (uint32_t dhtfriend = 0; dhtfriend < dht_get_num_friends(m->dht); ++dhtfriend) {
- if (id_equal(m->friendlist[friend_idx].real_pk, dht_get_friend_public_key(m->dht, dhtfriend))) {
- m2dht[friend_idx] = dhtfriend;
+ if (pk_equal(m->friendlist[friend_idx].real_pk, dht_get_friend_public_key(m->dht, dhtfriend))) {
+ assert(dhtfriend < INT32_MAX);
+ m2dht[friend_idx] = (int32_t)dhtfriend;
break;
}
}
@@ -2628,7 +2461,8 @@ void do_messenger(Messenger *m, void *userdata)
for (uint32_t friend_idx = 0; friend_idx < num_dhtfriends; ++friend_idx) {
if (m2dht[friend_idx] >= 0) {
- dht2m[m2dht[friend_idx]] = friend_idx;
+ assert(friend_idx < INT32_MAX);
+ dht2m[m2dht[friend_idx]] = (int32_t)friend_idx;
}
}
@@ -2640,7 +2474,7 @@ void do_messenger(Messenger *m, void *userdata)
const Friend *const msgfptr = dht2m[friend_idx] >= 0 ? &m->friendlist[dht2m[friend_idx]] : nullptr;
const DHT_Friend *const dhtfptr = dht_get_friend(m->dht, friend_idx);
- if (msgfptr) {
+ if (msgfptr != nullptr) {
char id_str[IDSTRING_LEN];
LOGGER_TRACE(m->log, "F[%2u:%2u] <%s> %s",
dht2m[friend_idx], friend_idx, msgfptr->name,
@@ -2665,10 +2499,10 @@ void do_messenger(Messenger *m, void *userdata)
last_pinged = 999;
}
- char ip_str[IP_NTOA_LEN];
+ Ip_Ntoa ip_str;
char id_str[IDSTRING_LEN];
LOGGER_TRACE(m->log, "F[%2u] => C[%2u] %s:%u [%3u] %s",
- friend_idx, client, ip_ntoa(&assoc->ip_port.ip, ip_str, sizeof(ip_str)),
+ friend_idx, client, net_ip_ntoa(&assoc->ip_port.ip, &ip_str),
net_ntohs(assoc->ip_port.port), last_pinged,
id_to_string(cptr->public_key, id_str, sizeof(id_str)));
}
@@ -2702,24 +2536,30 @@ static uint32_t friend_size(void)
uint32_t data = 0;
const struct Saved_Friend *const temp = nullptr;
-#define VALUE_MEMBER(name) do { data += sizeof(temp->name); } while (0)
-#define ARRAY_MEMBER(name) do { data += sizeof(temp->name); } while (0)
+#define VALUE_MEMBER(data, name) \
+ do { \
+ data += sizeof(name); \
+ } while (0)
+#define ARRAY_MEMBER(data, name) \
+ do { \
+ data += sizeof(name); \
+ } while (0)
// Exactly the same in friend_load, friend_save, and friend_size
- VALUE_MEMBER(status);
- ARRAY_MEMBER(real_pk);
- ARRAY_MEMBER(info);
+ VALUE_MEMBER(data, temp->status);
+ ARRAY_MEMBER(data, temp->real_pk);
+ ARRAY_MEMBER(data, temp->info);
++data; // padding
- VALUE_MEMBER(info_size);
- ARRAY_MEMBER(name);
- VALUE_MEMBER(name_length);
- ARRAY_MEMBER(statusmessage);
+ VALUE_MEMBER(data, temp->info_size);
+ ARRAY_MEMBER(data, temp->name);
+ VALUE_MEMBER(data, temp->name_length);
+ ARRAY_MEMBER(data, temp->statusmessage);
++data; // padding
- VALUE_MEMBER(statusmessage_length);
- VALUE_MEMBER(userstatus);
+ VALUE_MEMBER(data, temp->statusmessage_length);
+ VALUE_MEMBER(data, temp->userstatus);
data += 3; // padding
- VALUE_MEMBER(friendrequest_nospam);
- ARRAY_MEMBER(last_seen_time);
+ VALUE_MEMBER(data, temp->friendrequest_nospam);
+ ARRAY_MEMBER(data, temp->last_seen_time);
#undef VALUE_MEMBER
#undef ARRAY_MEMBER
@@ -2727,33 +2567,36 @@ static uint32_t friend_size(void)
return data;
}
+non_null()
static uint8_t *friend_save(const struct Saved_Friend *temp, uint8_t *data)
{
-#define VALUE_MEMBER(name) do { \
- memcpy(data, &temp->name, sizeof(temp->name)); \
- data += sizeof(temp->name); \
-} while (0)
+#define VALUE_MEMBER(data, name) \
+ do { \
+ memcpy(data, &name, sizeof(name)); \
+ data += sizeof(name); \
+ } while (0)
-#define ARRAY_MEMBER(name) do { \
- memcpy(data, temp->name, sizeof(temp->name)); \
- data += sizeof(temp->name); \
-} while (0)
+#define ARRAY_MEMBER(data, name) \
+ do { \
+ memcpy(data, name, sizeof(name)); \
+ data += sizeof(name); \
+ } while (0)
// Exactly the same in friend_load, friend_save, and friend_size
- VALUE_MEMBER(status);
- ARRAY_MEMBER(real_pk);
- ARRAY_MEMBER(info);
+ VALUE_MEMBER(data, temp->status);
+ ARRAY_MEMBER(data, temp->real_pk);
+ ARRAY_MEMBER(data, temp->info);
++data; // padding
- VALUE_MEMBER(info_size);
- ARRAY_MEMBER(name);
- VALUE_MEMBER(name_length);
- ARRAY_MEMBER(statusmessage);
+ VALUE_MEMBER(data, temp->info_size);
+ ARRAY_MEMBER(data, temp->name);
+ VALUE_MEMBER(data, temp->name_length);
+ ARRAY_MEMBER(data, temp->statusmessage);
++data; // padding
- VALUE_MEMBER(statusmessage_length);
- VALUE_MEMBER(userstatus);
+ VALUE_MEMBER(data, temp->statusmessage_length);
+ VALUE_MEMBER(data, temp->userstatus);
data += 3; // padding
- VALUE_MEMBER(friendrequest_nospam);
- ARRAY_MEMBER(last_seen_time);
+ VALUE_MEMBER(data, temp->friendrequest_nospam);
+ ARRAY_MEMBER(data, temp->last_seen_time);
#undef VALUE_MEMBER
#undef ARRAY_MEMBER
@@ -2762,33 +2605,36 @@ static uint8_t *friend_save(const struct Saved_Friend *temp, uint8_t *data)
}
+non_null()
static const uint8_t *friend_load(struct Saved_Friend *temp, const uint8_t *data)
{
-#define VALUE_MEMBER(name) do { \
- memcpy(&temp->name, data, sizeof(temp->name)); \
- data += sizeof(temp->name); \
-} while (0)
+#define VALUE_MEMBER(data, name) \
+ do { \
+ memcpy(&name, data, sizeof(name)); \
+ data += sizeof(name); \
+ } while (0)
-#define ARRAY_MEMBER(name) do { \
- memcpy(temp->name, data, sizeof(temp->name)); \
- data += sizeof(temp->name); \
-} while (0)
+#define ARRAY_MEMBER(data, name) \
+ do { \
+ memcpy(name, data, sizeof(name)); \
+ data += sizeof(name); \
+ } while (0)
// Exactly the same in friend_load, friend_save, and friend_size
- VALUE_MEMBER(status);
- ARRAY_MEMBER(real_pk);
- ARRAY_MEMBER(info);
+ VALUE_MEMBER(data, temp->status);
+ ARRAY_MEMBER(data, temp->real_pk);
+ ARRAY_MEMBER(data, temp->info);
++data; // padding
- VALUE_MEMBER(info_size);
- ARRAY_MEMBER(name);
- VALUE_MEMBER(name_length);
- ARRAY_MEMBER(statusmessage);
+ VALUE_MEMBER(data, temp->info_size);
+ ARRAY_MEMBER(data, temp->name);
+ VALUE_MEMBER(data, temp->name_length);
+ ARRAY_MEMBER(data, temp->statusmessage);
++data; // padding
- VALUE_MEMBER(statusmessage_length);
- VALUE_MEMBER(userstatus);
+ VALUE_MEMBER(data, temp->statusmessage_length);
+ VALUE_MEMBER(data, temp->userstatus);
data += 3; // padding
- VALUE_MEMBER(friendrequest_nospam);
- ARRAY_MEMBER(last_seen_time);
+ VALUE_MEMBER(data, temp->friendrequest_nospam);
+ ARRAY_MEMBER(data, temp->last_seen_time);
#undef VALUE_MEMBER
#undef ARRAY_MEMBER
@@ -2797,6 +2643,7 @@ static const uint8_t *friend_load(struct Saved_Friend *temp, const uint8_t *data
}
+non_null()
static uint32_t m_state_plugins_size(const Messenger *m)
{
const uint32_t size32 = sizeof(uint32_t);
@@ -2813,19 +2660,19 @@ static uint32_t m_state_plugins_size(const Messenger *m)
return size;
}
-/** Registers a state plugin for saving, loadding, and getting the size of a section of the save
+/** @brief Registers a state plugin for saving, loading, and getting the size of a section of the save.
*
- * returns true on success
- * returns false on error
+ * @retval true on success
+ * @retval false on error
*/
-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)
+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)
{
Messenger_State_Plugin *temp = (Messenger_State_Plugin *)realloc(m->options.state_plugins,
sizeof(Messenger_State_Plugin) * (m->options.state_plugins_length + 1));
- if (!temp) {
+ if (temp == nullptr) {
return false;
}
@@ -2841,6 +2688,7 @@ bool m_register_state_plugin(Messenger *m, State_Type type, m_state_size_cb size
return true;
}
+non_null()
static uint32_t m_plugin_size(const Messenger *m, State_Type type)
{
for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) {
@@ -2862,7 +2710,7 @@ uint32_t messenger_size(const Messenger *m)
return m_state_plugins_size(m);
}
-/** Save the messenger in data (must be allocated memory of size at least Messenger_size()) */
+/** Save the messenger in data (must be allocated memory of size at least `Messenger_size()`) */
uint8_t *messenger_save(const Messenger *m, uint8_t *data)
{
for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) {
@@ -2874,11 +2722,13 @@ uint8_t *messenger_save(const Messenger *m, uint8_t *data)
}
// nospam state plugin
+non_null()
static uint32_t nospam_keys_size(const Messenger *m)
{
return sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SECRET_KEY_SIZE;
}
+non_null()
static State_Load_Status load_nospam_keys(Messenger *m, const uint8_t *data, uint32_t length)
{
if (length != m_plugin_size(m, STATE_TYPE_NOSPAMKEYS)) {
@@ -2890,19 +2740,20 @@ static State_Load_Status load_nospam_keys(Messenger *m, const uint8_t *data, uin
set_nospam(m->fr, nospam);
load_secret_key(m->net_crypto, data + sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE);
- if (public_key_cmp(data + sizeof(uint32_t), nc_get_self_public_key(m->net_crypto)) != 0) {
+ if (!pk_equal(data + sizeof(uint32_t), nc_get_self_public_key(m->net_crypto))) {
return STATE_LOAD_STATUS_ERROR;
}
return STATE_LOAD_STATUS_CONTINUE;
}
+non_null()
static uint8_t *save_nospam_keys(const Messenger *m, uint8_t *data)
{
const uint32_t len = m_plugin_size(m, STATE_TYPE_NOSPAMKEYS);
- assert(sizeof(get_nospam(m->fr)) == sizeof(uint32_t));
+ static_assert(sizeof(get_nospam(m->fr)) == sizeof(uint32_t), "nospam doesn't fit in a 32 bit int");
data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_NOSPAMKEYS);
- uint32_t nospam = get_nospam(m->fr);
+ const uint32_t nospam = get_nospam(m->fr);
host_to_lendian_bytes32(data, nospam);
save_keys(m->net_crypto, data + sizeof(uint32_t));
data += len;
@@ -2910,11 +2761,13 @@ static uint8_t *save_nospam_keys(const Messenger *m, uint8_t *data)
}
// DHT state plugin
+non_null()
static uint32_t m_dht_size(const Messenger *m)
{
return dht_size(m->dht);
}
+non_null()
static uint8_t *save_dht(const Messenger *m, uint8_t *data)
{
const uint32_t len = m_plugin_size(m, STATE_TYPE_DHT);
@@ -2924,6 +2777,7 @@ static uint8_t *save_dht(const Messenger *m, uint8_t *data)
return data;
}
+non_null()
static State_Load_Status m_dht_load(Messenger *m, const uint8_t *data, uint32_t length)
{
dht_load(m->dht, data, length); // TODO(endoffile78): Should we throw an error if dht_load fails?
@@ -2931,11 +2785,13 @@ static State_Load_Status m_dht_load(Messenger *m, const uint8_t *data, uint32_t
}
// friendlist state plugin
+non_null()
static uint32_t saved_friendslist_size(const Messenger *m)
{
return count_friendlist(m) * friend_size();
}
+non_null()
static uint8_t *friends_list_save(const Messenger *m, uint8_t *data)
{
const uint32_t len = m_plugin_size(m, STATE_TYPE_FRIENDS);
@@ -2986,6 +2842,7 @@ static uint8_t *friends_list_save(const Messenger *m, uint8_t *data)
return data;
}
+non_null()
static State_Load_Status friends_list_load(Messenger *m, const uint8_t *data, uint32_t length)
{
const uint32_t l_friend_size = friend_size();
@@ -2994,7 +2851,7 @@ static State_Load_Status friends_list_load(Messenger *m, const uint8_t *data, ui
return STATE_LOAD_STATUS_ERROR; // TODO(endoffile78): error or continue?
}
- uint32_t num = length / l_friend_size;
+ const uint32_t num = length / l_friend_size;
const uint8_t *cur_data = data;
for (uint32_t i = 0; i < num; ++i) {
@@ -3005,7 +2862,7 @@ static State_Load_Status friends_list_load(Messenger *m, const uint8_t *data, ui
cur_data = next_data;
if (temp.status >= 3) {
- int fnum = m_addfriend_norequest(m, temp.real_pk);
+ const int fnum = m_addfriend_norequest(m, temp.real_pk);
if (fnum < 0) {
continue;
@@ -3018,9 +2875,9 @@ static State_Load_Status friends_list_load(Messenger *m, const uint8_t *data, ui
} else if (temp.status != 0) {
/* TODO(irungentoo): This is not a good way to do this. */
uint8_t address[FRIEND_ADDRESS_SIZE];
- id_copy(address, temp.real_pk);
+ pk_copy(address, temp.real_pk);
memcpy(address + CRYPTO_PUBLIC_KEY_SIZE, &temp.friendrequest_nospam, sizeof(uint32_t));
- uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
+ uint16_t checksum = data_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
memcpy(address + CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint32_t), &checksum, sizeof(checksum));
m_addfriend(m, address, temp.info, net_ntohs(temp.info_size));
}
@@ -3030,11 +2887,13 @@ static State_Load_Status friends_list_load(Messenger *m, const uint8_t *data, ui
}
// name state plugin
+non_null()
static uint32_t name_size(const Messenger *m)
{
return m->name_length;
}
+non_null()
static uint8_t *save_name(const Messenger *m, uint8_t *data)
{
const uint32_t len = m_plugin_size(m, STATE_TYPE_NAME);
@@ -3044,6 +2903,7 @@ static uint8_t *save_name(const Messenger *m, uint8_t *data)
return data;
}
+non_null()
static State_Load_Status load_name(Messenger *m, const uint8_t *data, uint32_t length)
{
if (length > 0 && length <= MAX_NAME_LENGTH) {
@@ -3054,11 +2914,13 @@ static State_Load_Status load_name(Messenger *m, const uint8_t *data, uint32_t l
}
// status message state plugin
+non_null()
static uint32_t status_message_size(const Messenger *m)
{
return m->statusmessage_length;
}
+non_null()
static uint8_t *save_status_message(const Messenger *m, uint8_t *data)
{
const uint32_t len = m_plugin_size(m, STATE_TYPE_STATUSMESSAGE);
@@ -3068,6 +2930,7 @@ static uint8_t *save_status_message(const Messenger *m, uint8_t *data)
return data;
}
+non_null()
static State_Load_Status load_status_message(Messenger *m, const uint8_t *data, uint32_t length)
{
if (length > 0 && length <= MAX_STATUSMESSAGE_LENGTH) {
@@ -3078,11 +2941,13 @@ static State_Load_Status load_status_message(Messenger *m, const uint8_t *data,
}
// status state plugin
+non_null()
static uint32_t status_size(const Messenger *m)
{
return 1;
}
+non_null()
static uint8_t *save_status(const Messenger *m, uint8_t *data)
{
const uint32_t len = m_plugin_size(m, STATE_TYPE_STATUS);
@@ -3092,6 +2957,7 @@ static uint8_t *save_status(const Messenger *m, uint8_t *data)
return data;
}
+non_null()
static State_Load_Status load_status(Messenger *m, const uint8_t *data, uint32_t length)
{
if (length == 1) {
@@ -3102,14 +2968,16 @@ static State_Load_Status load_status(Messenger *m, const uint8_t *data, uint32_t
}
// TCP Relay state plugin
+non_null()
static uint32_t tcp_relay_size(const Messenger *m)
{
- return NUM_SAVED_TCP_RELAYS * packed_node_size(net_family_tcp_ipv6);
+ return NUM_SAVED_TCP_RELAYS * packed_node_size(net_family_tcp_ipv6());
}
+non_null()
static uint8_t *save_tcp_relays(const Messenger *m, uint8_t *data)
{
- Node_format relays[NUM_SAVED_TCP_RELAYS] = {0};
+ Node_format relays[NUM_SAVED_TCP_RELAYS] = {{{0}}};
uint8_t *temp_data = data;
data = state_write_section_header(temp_data, STATE_COOKIE_TYPE, 0, STATE_TYPE_TCP_RELAY);
@@ -3120,7 +2988,7 @@ static uint8_t *save_tcp_relays(const Messenger *m, uint8_t *data)
uint32_t num = m->num_loaded_relays;
num += copy_connected_tcp_relays(m->net_crypto, relays + num, NUM_SAVED_TCP_RELAYS - num);
- int l = pack_nodes(data, NUM_SAVED_TCP_RELAYS * packed_node_size(net_family_tcp_ipv6), relays, num);
+ const int l = pack_nodes(m->log, data, NUM_SAVED_TCP_RELAYS * packed_node_size(net_family_tcp_ipv6()), relays, num);
if (l > 0) {
const uint32_t len = l;
@@ -3131,10 +2999,11 @@ static uint8_t *save_tcp_relays(const Messenger *m, uint8_t *data)
return data;
}
+non_null()
static State_Load_Status load_tcp_relays(Messenger *m, const uint8_t *data, uint32_t length)
{
- if (length != 0) {
- const int num = unpack_nodes(m->loaded_relays, NUM_SAVED_TCP_RELAYS, nullptr, data, length, 1);
+ if (length > 0) {
+ const int num = unpack_nodes(m->loaded_relays, NUM_SAVED_TCP_RELAYS, nullptr, data, length, true);
if (num == -1) {
m->num_loaded_relays = 0;
@@ -3149,11 +3018,13 @@ static State_Load_Status load_tcp_relays(Messenger *m, const uint8_t *data, uint
}
// path node state plugin
+non_null()
static uint32_t path_node_size(const Messenger *m)
{
- return NUM_SAVED_PATH_NODES * packed_node_size(net_family_tcp_ipv6);
+ return NUM_SAVED_PATH_NODES * packed_node_size(net_family_tcp_ipv6());
}
+non_null()
static uint8_t *save_path_nodes(const Messenger *m, uint8_t *data)
{
Node_format nodes[NUM_SAVED_PATH_NODES];
@@ -3161,7 +3032,7 @@ static uint8_t *save_path_nodes(const Messenger *m, uint8_t *data)
data = state_write_section_header(data, STATE_COOKIE_TYPE, 0, STATE_TYPE_PATH_NODE);
memset(nodes, 0, sizeof(nodes));
const unsigned int num = onion_backup_nodes(m->onion_c, nodes, NUM_SAVED_PATH_NODES);
- const int l = pack_nodes(data, NUM_SAVED_PATH_NODES * packed_node_size(net_family_tcp_ipv6), nodes, num);
+ const int l = pack_nodes(m->log, data, NUM_SAVED_PATH_NODES * packed_node_size(net_family_tcp_ipv6()), nodes, num);
if (l > 0) {
const uint32_t len = l;
@@ -3172,12 +3043,12 @@ static uint8_t *save_path_nodes(const Messenger *m, uint8_t *data)
return data;
}
+non_null()
static State_Load_Status load_path_nodes(Messenger *m, const uint8_t *data, uint32_t length)
{
- Node_format nodes[NUM_SAVED_PATH_NODES];
-
- if (length != 0) {
- const int num = unpack_nodes(nodes, NUM_SAVED_PATH_NODES, nullptr, data, length, 0);
+ if (length > 0) {
+ Node_format nodes[NUM_SAVED_PATH_NODES];
+ const int num = unpack_nodes(nodes, NUM_SAVED_PATH_NODES, nullptr, data, length, false);
if (num == -1) {
return STATE_LOAD_STATUS_CONTINUE;
@@ -3191,6 +3062,7 @@ static State_Load_Status load_path_nodes(Messenger *m, const uint8_t *data, uint
return STATE_LOAD_STATUS_CONTINUE;
}
+non_null()
static void m_register_default_plugins(Messenger *m)
{
m_register_state_plugin(m, STATE_TYPE_NOSPAMKEYS, nospam_keys_size, load_nospam_keys, save_nospam_keys);
@@ -3219,9 +3091,11 @@ bool messenger_load_state_section(Messenger *m, const uint8_t *data, uint32_t le
return false;
}
-/** Return the number of friends in the instance m.
+/** @brief Return the number of friends in the instance m.
+ *
* You should use this to determine how much memory to allocate
- * for copy_friendlist. */
+ * for copy_friendlist.
+ */
uint32_t count_friendlist(const Messenger *m)
{
uint32_t ret = 0;
@@ -3235,14 +3109,15 @@ uint32_t count_friendlist(const Messenger *m)
return ret;
}
-/** Copy a list of valid friend IDs into the array out_list.
+/** @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. */
+ * of out_list will be truncated to list_size.
+ */
uint32_t copy_friendlist(Messenger const *m, uint32_t *out_list, uint32_t list_size)
{
- if (!out_list) {
+ if (out_list == nullptr) {
return 0;
}
@@ -3265,3 +3140,240 @@ uint32_t copy_friendlist(Messenger const *m, uint32_t *out_list, uint32_t list_s
return ret;
}
+
+static fr_friend_request_cb m_handle_friend_request;
+non_null(1, 2, 3) nullable(5)
+static void m_handle_friend_request(
+ void *object, const uint8_t *public_key, const uint8_t *message, size_t length, void *user_data)
+{
+ Messenger *m = (Messenger *)object;
+ assert(m != nullptr);
+ m->friend_request(m, public_key, message, length, user_data);
+}
+
+/** @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.
+ */
+Messenger *new_messenger(Mono_Time *mono_time, const Random *rng, const Network *ns, Messenger_Options *options, Messenger_Error *error)
+{
+ if (options == nullptr) {
+ return nullptr;
+ }
+
+ if (error != nullptr) {
+ *error = MESSENGER_ERROR_OTHER;
+ }
+
+ Messenger *m = (Messenger *)calloc(1, sizeof(Messenger));
+
+ if (m == nullptr) {
+ return nullptr;
+ }
+
+ m->mono_time = mono_time;
+ m->rng = rng;
+ m->ns = ns;
+
+ m->fr = friendreq_new();
+
+ if (m->fr == nullptr) {
+ free(m);
+ return nullptr;
+ }
+
+ m->log = logger_new();
+
+ if (m->log == nullptr) {
+ friendreq_kill(m->fr);
+ free(m);
+ return nullptr;
+ }
+
+ logger_callback_log(m->log, options->log_callback, options->log_context, options->log_user_data);
+
+ unsigned int net_err = 0;
+
+ if (!options->udp_disabled && options->proxy_info.proxy_type != TCP_PROXY_NONE) {
+ // We don't currently support UDP over proxy.
+ LOGGER_INFO(m->log, "UDP enabled and proxy set: disabling UDP");
+ options->udp_disabled = true;
+ }
+
+ if (options->udp_disabled) {
+ m->net = new_networking_no_udp(m->log, m->ns);
+ } else {
+ IP ip;
+ ip_init(&ip, options->ipv6enabled);
+ m->net = new_networking_ex(m->log, m->ns, &ip, options->port_range[0], options->port_range[1], &net_err);
+ }
+
+ if (m->net == nullptr) {
+ friendreq_kill(m->fr);
+ logger_kill(m->log);
+ free(m);
+
+ if (error != nullptr && net_err == 1) {
+ *error = MESSENGER_ERROR_PORT;
+ }
+
+ return nullptr;
+ }
+
+ m->dht = new_dht(m->log, m->rng, m->ns, m->mono_time, m->net, options->hole_punching_enabled, options->local_discovery_enabled);
+
+ if (m->dht == nullptr) {
+ kill_networking(m->net);
+ friendreq_kill(m->fr);
+ logger_kill(m->log);
+ free(m);
+ return nullptr;
+ }
+
+ m->net_crypto = new_net_crypto(m->log, m->rng, m->ns, m->mono_time, m->dht, &options->proxy_info);
+
+ if (m->net_crypto == nullptr) {
+ kill_dht(m->dht);
+ kill_networking(m->net);
+ friendreq_kill(m->fr);
+ logger_kill(m->log);
+ free(m);
+ return nullptr;
+ }
+
+ if (options->dht_announcements_enabled) {
+ m->forwarding = new_forwarding(m->log, m->rng, m->mono_time, m->dht);
+ m->announce = new_announcements(m->log, m->rng, m->mono_time, m->forwarding);
+ } else {
+ m->forwarding = nullptr;
+ m->announce = nullptr;
+ }
+
+ m->onion = new_onion(m->log, m->mono_time, m->rng, m->dht);
+ m->onion_a = new_onion_announce(m->log, m->rng, m->mono_time, m->dht);
+ m->onion_c = new_onion_client(m->log, m->rng, m->mono_time, m->net_crypto);
+ m->fr_c = new_friend_connections(m->log, m->mono_time, m->ns, m->onion_c, options->local_discovery_enabled);
+
+ if ((options->dht_announcements_enabled && (m->forwarding == nullptr || m->announce == nullptr)) ||
+ m->onion == nullptr || m->onion_a == nullptr || m->onion_c == nullptr || m->fr_c == nullptr) {
+ kill_friend_connections(m->fr_c);
+ kill_onion(m->onion);
+ kill_onion_announce(m->onion_a);
+ kill_onion_client(m->onion_c);
+ kill_announcements(m->announce);
+ kill_forwarding(m->forwarding);
+ kill_net_crypto(m->net_crypto);
+ kill_dht(m->dht);
+ kill_networking(m->net);
+ friendreq_kill(m->fr);
+ logger_kill(m->log);
+ free(m);
+ return nullptr;
+ }
+
+ if (options->tcp_server_port != 0) {
+ m->tcp_server = new_TCP_server(m->log, m->rng, m->ns, options->ipv6enabled, 1, &options->tcp_server_port,
+ dht_get_self_secret_key(m->dht), m->onion, m->forwarding);
+
+ if (m->tcp_server == nullptr) {
+ kill_friend_connections(m->fr_c);
+ kill_onion(m->onion);
+ kill_onion_announce(m->onion_a);
+ kill_onion_client(m->onion_c);
+ kill_announcements(m->announce);
+ kill_forwarding(m->forwarding);
+ kill_net_crypto(m->net_crypto);
+ kill_dht(m->dht);
+ kill_networking(m->net);
+ friendreq_kill(m->fr);
+ logger_kill(m->log);
+ free(m);
+
+ if (error != nullptr) {
+ *error = MESSENGER_ERROR_TCP_SERVER;
+ }
+
+ return nullptr;
+ }
+ }
+
+ m->options = *options;
+ friendreq_init(m->fr, m->fr_c);
+ set_nospam(m->fr, random_u32(m->rng));
+ set_filter_function(m->fr, &friend_already_added, m);
+
+ m->lastdump = 0;
+ m->is_receiving_file = 0;
+
+ m_register_default_plugins(m);
+ callback_friendrequest(m->fr, m_handle_friend_request, m);
+
+ if (error != nullptr) {
+ *error = MESSENGER_ERROR_NONE;
+ }
+
+ return m;
+}
+
+/** @brief Run this before closing shop.
+ *
+ * Free all datastructures.
+ */
+void kill_messenger(Messenger *m)
+{
+ if (m == nullptr) {
+ return;
+ }
+
+ if (m->tcp_server != nullptr) {
+ kill_TCP_server(m->tcp_server);
+ }
+
+ kill_friend_connections(m->fr_c);
+ kill_onion(m->onion);
+ kill_onion_announce(m->onion_a);
+ kill_onion_client(m->onion_c);
+ kill_announcements(m->announce);
+ kill_forwarding(m->forwarding);
+ kill_net_crypto(m->net_crypto);
+ kill_dht(m->dht);
+ kill_networking(m->net);
+
+ for (uint32_t i = 0; i < m->numfriends; ++i) {
+ clear_receipts(m, i);
+ }
+
+ logger_kill(m->log);
+ free(m->friendlist);
+ friendreq_kill(m->fr);
+
+ free(m->options.state_plugins);
+ free(m);
+}
+
+bool m_is_receiving_file(Messenger *m)
+{
+ // Only run the expensive loop below once every 64 tox_iterate calls.
+ const uint8_t skip_count = 64;
+
+ if (m->is_receiving_file != 0) {
+ --m->is_receiving_file;
+ return true;
+ }
+
+ // TODO(iphydf): This is a very expensive loop. Consider keeping track of
+ // the number of live file transfers.
+ for (size_t friend_number = 0; friend_number < m->numfriends; ++friend_number) {
+ for (size_t i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) {
+ if (m->friendlist[friend_number].file_receiving[i].status == FILESTATUS_TRANSFERRING) {
+ m->is_receiving_file = skip_count;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/protocols/Tox/libtox/src/toxcore/Messenger.h b/protocols/Tox/libtox/src/toxcore/Messenger.h
index 477b997391..4abd7905d0 100644
--- a/protocols/Tox/libtox/src/toxcore/Messenger.h
+++ b/protocols/Tox/libtox/src/toxcore/Messenger.h
@@ -11,6 +11,8 @@
#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 "logger.h"
@@ -60,6 +62,7 @@ typedef struct Messenger_Options {
bool hole_punching_enabled;
bool local_discovery_enabled;
+ bool dht_announcements_enabled;
logger_cb *log_callback;
void *log_context;
@@ -85,7 +88,8 @@ typedef enum Friend_Status {
FRIEND_ONLINE,
} Friend_Status;
-/** Errors for m_addfriend
+/** @brief Errors for m_addfriend
+ *
* FAERR - Friend Add Error
*/
typedef enum Friend_Add_Error {
@@ -108,10 +112,9 @@ typedef enum Connection_Status {
CONNECTION_UDP,
} Connection_Status;
-/** USERSTATUS -
+/**
* Represents userstatuses someone can have.
*/
-
typedef enum Userstatus {
USERSTATUS_NONE,
USERSTATUS_AWAY,
@@ -158,7 +161,7 @@ typedef enum Filekind {
} Filekind;
-typedef void m_self_connection_status_cb(Messenger *m, unsigned int connection_status, void *user_data);
+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);
@@ -207,15 +210,15 @@ typedef struct Friend {
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;
- uint8_t name_sent; // 0 if we didn't send our name to this friend 1 if we have.
+ 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;
- uint8_t statusmessage_sent;
+ bool statusmessage_sent;
Userstatus userstatus;
- uint8_t userstatus_sent;
- uint8_t user_istyping;
- uint8_t user_istyping_sent;
- uint8_t is_typing;
+ 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.
@@ -234,11 +237,16 @@ typedef struct 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;
@@ -258,13 +266,15 @@ struct Messenger {
Friend *friendlist;
uint32_t numfriends;
- time_t lastdump;
+ uint64_t lastdump;
+ uint8_t is_receiving_file;
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;
@@ -290,15 +300,17 @@ struct Messenger {
m_friend_lossless_packet_cb *lossless_packethandler;
m_self_connection_status_cb *core_connection_change;
- unsigned int last_connection_status;
+ Onion_Connection_Status last_connection_status;
Messenger_Options options;
};
-/** Format: `[real_pk (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]`
+/**
+ * Format: `[real_pk (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]`
*
- * return FRIEND_ADDRESS_SIZE byte address to give to others.
+ * @param[out] address FRIEND_ADDRESS_SIZE byte address to give to others.
*/
+non_null()
void getaddress(const Messenger *m, uint8_t *address);
/**
@@ -312,365 +324,391 @@ void getaddress(const Messenger *m, uint8_t *address);
* @param data is the data.
* @param length is the length.
*
- * return the friend number if success.
- * return FA_TOOLONG if message length is too long.
- * return FAERR_NOMESSAGE if no message (message length must be >= 1 byte).
- * return FAERR_OWNKEY if user's own key.
- * return FAERR_ALREADYSENT if friend request already sent or already a friend.
- * return FAERR_BADCHECKSUM if bad checksum in address.
- * return FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different.
- * (the nospam for that friend was set to the new one).
- * return FAERR_NOMEM if increasing the friend list size fails.
- */
+ * @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);
-/** Add a friend without sending a friendrequest.
- * return the friend number if success.
- * return -3 if user's own key.
- * return -4 if friend request already sent or already a friend.
- * return -6 if bad checksum in address.
- * return -8 if increasing the friend list size fails.
+/** @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);
-/** return the friend number associated to that public key.
- * return -1 if no such friend.
+/** @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);
-/** Copies the public key associated to that friend id into real_pk buffer.
+/** @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.
*
- * return 0 if success.
- * return -1 if failure.
+ * @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.
- * return -1 if failure.
+/** @return friend connection id on success.
+ * @retval -1 if failure.
*/
+non_null()
int getfriendcon_id(const Messenger *m, int32_t friendnumber);
-/** Remove a friend.
+/** @brief Remove a friend.
*
- * return 0 if success.
- * return -1 if failure.
+ * @retval 0 if success.
+ * @retval -1 if failure.
*/
+non_null()
int m_delfriend(Messenger *m, int32_t friendnumber);
-/** Checks friend's connection status.
+/** @brief Checks friend's connection status.
*
- * return CONNECTION_UDP (2) if friend is directly connected to us (Online UDP).
- * return CONNECTION_TCP (1) if friend is connected to us (Online TCP).
- * return CONNECTION_NONE (0) if friend is not connected to us (Offline).
- * return -1 on failure.
+ * @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.
+/**
+ * Checks if there exists a friend with given friendnumber.
+ *
+ * @param friendnumber The index in the friend list.
*
- * return 1 if friend exists.
- * return 0 if friend doesn't exist.
+ * @retval true if friend exists.
+ * @retval false if friend doesn't exist.
*/
-int m_friend_exists(const Messenger *m, int32_t friendnumber);
+non_null()
+bool m_friend_exists(const Messenger *m, int32_t friendnumber);
-/** Send a message of type to an online friend.
+/** @brief Send a message of type to an online friend.
*
- * return -1 if friend not valid.
- * return -2 if too large.
- * return -3 if friend not online.
- * return -4 if send failed (because queue is full).
- * return -5 if bad type.
- * return 0 if success.
+ * @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.
+ * 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);
-/** Set the name and name_length of a friend.
+/** @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.
*
- * return 0 if success.
- * return -1 if failure.
+ * @retval 0 if success.
+ * @retval -1 if failure.
*/
+non_null()
int setfriendname(Messenger *m, int32_t friendnumber, const uint8_t *name, uint16_t length);
-/** Set our nickname.
+/** @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.
*
- * return 0 if success.
- * return -1 if failure.
+ * @retval 0 if success.
+ * @retval -1 if failure.
*/
+non_null()
int setname(Messenger *m, const uint8_t *name, uint16_t length);
/**
- * Get your nickname.
+ * @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.
- * return 0 on error.
+ * @return length of the name.
+ * @retval 0 on error.
*/
+non_null()
uint16_t getself_name(const Messenger *m, uint8_t *name);
-/** Get name of friendnumber and put it in 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.
- * return -1 if failure.
+ * @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.
- * return -1 on failure.
+/** @return the length of name, including null on success.
+ * @retval -1 on failure.
*/
-int m_get_name_size(const Messenger *m, int32_t friendnumber);
-int m_get_self_name_size(const Messenger *m);
+non_null() int m_get_name_size(const Messenger *m, int32_t friendnumber);
+non_null() int m_get_self_name_size(const Messenger *m);
-/** Set our user status.
+/** @brief Set our user status.
* You are responsible for freeing status after.
*
- * returns 0 on success.
- * returns -1 on failure.
+ * @retval 0 on success.
+ * @retval -1 on failure.
*/
-int m_set_statusmessage(Messenger *m, const uint8_t *status, uint16_t length);
-int m_set_userstatus(Messenger *m, uint8_t status);
+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.
*
- * returns the length of friendnumber's status message, including null on success.
- * returns -1 on failure.
+ * @return the length of friendnumber's status message, including null on success.
+ * @retval -1 on failure.
*/
-int m_get_statusmessage_size(const Messenger *m, int32_t friendnumber);
-int m_get_self_statusmessage_size(const Messenger *m);
+non_null() int m_get_statusmessage_size(const Messenger *m, int32_t friendnumber);
+non_null() int m_get_self_statusmessage_size(const Messenger *m);
-/** Copy friendnumber's status message into buf, truncating if size is over maxlen.
+/** @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.
*
- * returns the length of the copied data on success
- * returns -1 on failure.
+ * @return the length of the copied data on success
+ * @retval -1 on failure.
*/
-int m_copy_statusmessage(const Messenger *m, int32_t friendnumber, uint8_t *buf, uint32_t maxlen);
-int m_copy_self_statusmessage(const Messenger *m, uint8_t *buf);
+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);
-/** 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.
+/** @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.
*/
-uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber);
-uint8_t m_get_self_userstatus(const Messenger *m);
+non_null() uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber);
+non_null() uint8_t m_get_self_userstatus(const Messenger *m);
-/** returns timestamp of last time friendnumber was seen online or 0 if never seen.
+/** @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.
*/
-uint64_t m_get_last_online(const Messenger *m, int32_t friendnumber);
+non_null() uint64_t m_get_last_online(const Messenger *m, int32_t friendnumber);
-/** Set our typing status for a friend.
+/** @brief Set our typing status for a friend.
* You are responsible for turning it on or off.
*
- * returns 0 on success.
- * returns -1 on failure.
+ * @retval 0 on success.
+ * @retval -1 on failure.
*/
-int m_set_usertyping(Messenger *m, int32_t friendnumber, uint8_t is_typing);
+non_null()
+int m_set_usertyping(Messenger *m, int32_t friendnumber, bool is_typing);
-/** Get the typing status of a friend.
+/** @brief Get the typing status of a friend.
*
- * returns 0 if friend is not typing.
- * returns 1 if friend is typing.
+ * @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. */
-void m_callback_friendmessage(Messenger *m, m_friend_message_cb *function);
+non_null() void m_callback_friendmessage(Messenger *m, m_friend_message_cb *function);
-/** Set the callback for name changes.
- * You are not responsible for freeing newname.
+/** @brief Set the callback for name changes.
+ * You are not responsible for freeing newname.
*/
-void m_callback_namechange(Messenger *m, m_friend_name_cb *function);
+non_null() void m_callback_namechange(Messenger *m, m_friend_name_cb *function);
-/** Set the callback for status message changes.
+/** @brief Set the callback for status message changes.
*
- * You are not responsible for freeing newstatus
+ * You are not responsible for freeing newstatus
*/
-void m_callback_statusmessage(Messenger *m, m_friend_status_message_cb *function);
+non_null() void m_callback_statusmessage(Messenger *m, m_friend_status_message_cb *function);
-/** Set the callback for status type changes.
- */
-void m_callback_userstatus(Messenger *m, m_friend_status_cb *function);
+/** @brief Set the callback for status type changes. */
+non_null() void m_callback_userstatus(Messenger *m, m_friend_status_cb *function);
-/** Set the callback for typing changes.
- */
-void m_callback_typingchange(Messenger *m, m_friend_typing_cb *function);
+/** @brief Set the callback for typing changes. */
+non_null() void m_callback_typingchange(Messenger *m, m_friend_typing_cb *function);
-/** Set the callback for read receipts.
+/** @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.
+ * 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.
*/
-void m_callback_read_receipt(Messenger *m, m_friend_read_receipt_cb *function);
+non_null() void m_callback_read_receipt(Messenger *m, m_friend_read_receipt_cb *function);
-/** Set the callback for connection status changes.
+/** @brief Set the callback for connection status changes.
*
- * Status:
- * 0 -- friend went offline after being previously online.
- * 1 -- friend went online.
+ * 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.
+ * 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.
*/
-void m_callback_connectionstatus(Messenger *m, m_friend_connection_status_cb *function);
+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 */
-void m_callback_connectionstatus_internal_av(Messenger *m, m_friend_connectionstatuschange_internal_cb *function,
- void *userdata);
+non_null() void m_callback_connectionstatus_internal_av(
+ Messenger *m, m_friend_connectionstatuschange_internal_cb *function, void *userdata);
-/** Set the callback for typing changes.
- */
-void m_callback_core_connection(Messenger *m, m_self_connection_status_cb *function);
+/** @brief Set the callback for typing changes. */
+non_null() void m_callback_core_connection(Messenger *m, m_self_connection_status_cb *function);
/*** CONFERENCES */
-/** Set the callback for conference invites.
- */
+/** @brief Set the callback for conference invites. */
+non_null(1) nullable(2)
void m_callback_conference_invite(Messenger *m, m_conference_invite_cb *function);
-/** Send a conference invite packet.
+/** @brief Send a conference invite packet.
*
- * return 1 on success
- * return 0 on failure
+ * return true on success
+ * return false on failure
*/
-int send_conference_invite_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length);
+non_null()
+bool send_conference_invite_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length);
/*** FILE SENDING */
-/** Set the callback for file send requests.
- */
-void callback_file_sendrequest(Messenger *m, m_file_recv_cb *function);
+/** @brief Set the callback for file send requests. */
+non_null() void callback_file_sendrequest(Messenger *m, m_file_recv_cb *function);
-/** Set the callback for file control requests.
- */
-void callback_file_control(Messenger *m, m_file_recv_control_cb *function);
+/** @brief Set the callback for file control requests. */
+non_null() void callback_file_control(Messenger *m, m_file_recv_control_cb *function);
-/** Set the callback for file data.
- */
-void callback_file_data(Messenger *m, m_file_recv_chunk_cb *function);
+/** @brief Set the callback for file data. */
+non_null() void callback_file_data(Messenger *m, m_file_recv_chunk_cb *function);
-/** Set the callback for file request chunk.
- */
-void callback_file_reqchunk(Messenger *m, m_file_chunk_request_cb *function);
+/** @brief Set the callback for file request chunk. */
+non_null() void callback_file_reqchunk(Messenger *m, m_file_chunk_request_cb *function);
-/** Copy the file transfer file id to file_id
+/** @brief Copy the file transfer file id to file_id
*
- * return 0 on success.
- * return -1 if friend not valid.
- * return -2 if filenumber not valid
+ * @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);
-/** Send a file send request.
+/** @brief Send a file send request.
+ *
* Maximum filename length is 255 bytes.
- * return file number on success
- * return -1 if friend not found.
- * return -2 if filename length invalid.
- * return -3 if no more file sending slots left.
- * return -4 if could not send packet (friend offline).
*
+ * @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);
-/** Send a file control request.
+/** @brief Send a file control request.
*
- * return 0 on success
- * return -1 if friend not valid.
- * return -2 if friend not online.
- * return -3 if file number invalid.
- * return -4 if file control is bad.
- * return -5 if file already paused.
- * return -6 if resume file failed because it was only paused by the other.
- * return -7 if resume file failed because it wasn't paused.
- * return -8 if packet failed to send.
+ * @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);
-/** Send a seek file control request.
+/** @brief Send a seek file control request.
*
- * return 0 on success
- * return -1 if friend not valid.
- * return -2 if friend not online.
- * return -3 if file number invalid.
- * return -4 if not receiving file.
- * return -5 if file status wrong.
- * return -6 if position bad.
- * return -8 if packet failed to send.
+ * @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);
-/** Send file data.
+/** @brief Send file data.
*
- * return 0 on success
- * return -1 if friend not valid.
- * return -2 if friend not online.
- * return -3 if filenumber invalid.
- * return -4 if file transfer not transferring.
- * return -5 if bad data size.
- * return -6 if packet queue full.
- * return -7 if wrong position.
+ * @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.
*/
-int file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
- uint16_t length);
+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 */
-/** Set the callback for msi packets.
- */
+/** @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);
-/** Send an msi packet.
+/** @brief Send an msi packet.
*
- * return 1 on success
- * return 0 on failure
+ * @retval true on success
+ * @retval false on failure
*/
-int m_msi_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length);
+non_null()
+bool m_msi_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length);
-/** Set handlers for lossy rtp packets.
+/** @brief Set handlers for lossy rtp packets.
*
- * return -1 on failure.
- * return 0 on success.
+ * @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 */
-/** Set handlers for custom lossy packets.
- *
- */
-void custom_lossy_packet_registerhandler(Messenger *m, m_friend_lossy_packet_cb *lossy_packethandler);
+/** @brief Set handlers for custom lossy packets. */
+non_null() void custom_lossy_packet_registerhandler(Messenger *m, m_friend_lossy_packet_cb *lossy_packethandler);
-/** High level function to send custom lossy packets.
+/** @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
@@ -679,30 +717,31 @@ void custom_lossy_packet_registerhandler(Messenger *m, m_friend_lossy_packet_cb
* I.e. custom_lossy_packet_registerhandler's "custom lossy packet" and this "custom lossy packet"
* are not the same set of packets.
*
- * return -1 if friend invalid.
- * return -2 if length wrong.
- * return -3 if first byte invalid.
- * return -4 if friend offline.
- * return -5 if packet failed to send because of other error.
- * return 0 on success.
+ * @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);
-/** Set handlers for custom lossless packets.
- *
- */
+/** @brief Set handlers for custom lossless packets. */
+non_null()
void custom_lossless_packet_registerhandler(Messenger *m, m_friend_lossless_packet_cb *lossless_packethandler);
-/** High level function to send custom lossless packets.
+/** @brief High level function to send custom lossless packets.
*
- * return -1 if friend invalid.
- * return -2 if length wrong.
- * return -3 if first byte invalid.
- * return -4 if friend offline.
- * return -5 if packet failed to send because of other error.
- * return 0 on success.
+ * @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. */
@@ -714,46 +753,58 @@ typedef enum Messenger_Error {
MESSENGER_ERROR_OTHER,
} Messenger_Error;
-/** Run this at startup.
- * return allocated instance of Messenger on success.
- * return 0 if there are problems.
+/** @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.
+ * if error is not NULL it will be set to one of the values in the enum above.
*/
-Messenger *new_messenger(Mono_Time *mono_time, Messenger_Options *options, unsigned int *error);
+non_null()
+Messenger *new_messenger(Mono_Time *mono_time, const Random *rng, const Network *ns, Messenger_Options *options, Messenger_Error *error);
-/** Run this before closing shop
+/** @brief Run this before closing shop.
+ *
* Free all datastructures.
*/
+nullable(1)
void kill_messenger(Messenger *m);
-/** The main loop that needs to be run at least 20 times per second. */
+/** @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);
-/** Return the time in milliseconds before do_messenger() should be called again
- * for optimal performance.
+/**
+ * @brief Return the time in milliseconds before `do_messenger()` should be called again
+ * for optimal performance.
*
- * returns time (in ms) before the next do_messenger() needs to be run on success.
+ * @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: */
-/** Registers a state plugin for saving, loadding, and getting the size of a section of the save
+/** @brief Registers a state plugin for saving, loading, and getting the size of a section of the save.
*
- * returns true on success
- * returns false on error
+ * @retval true on success
+ * @retval false on error
*/
-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);
+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()) */
+/** 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);
-/** Load a state section.
+/** @brief Load a state section.
*
* @param data Data to load.
* @param length Length of data.
@@ -761,19 +812,28 @@ uint8_t *messenger_save(const Messenger *m, uint8_t *data);
* @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);
-/** Return the number of friends in the instance m.
+/** @brief Return the number of friends in the instance m.
+ *
* You should use this to determine how much memory to allocate
- * for copy_friendlist. */
+ * for copy_friendlist.
+ */
+non_null()
uint32_t count_friendlist(const Messenger *m);
-/** Copy a list of valid friend IDs into the array out_list.
+/** @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. */
+ * 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
diff --git a/protocols/Tox/libtox/src/toxcore/TCP_client.c b/protocols/Tox/libtox/src/toxcore/TCP_client.c
index 30aa268f1c..1bc0964004 100644
--- a/protocols/Tox/libtox/src/toxcore/TCP_client.c
+++ b/protocols/Tox/libtox/src/toxcore/TCP_client.c
@@ -13,6 +13,7 @@
#include <string.h>
#include "TCP_common.h"
+#include "ccompat.h"
#include "mono_time.h"
#include "util.h"
@@ -56,6 +57,9 @@ struct TCP_Client_Connection {
tcp_onion_response_cb *onion_callback;
void *onion_callback_object;
+ forwarded_response_cb *forwarded_response_callback;
+ void *forwarded_response_callback_object;
+
/* Can be used by user. */
void *custom_object;
uint32_t custom_uint;
@@ -92,26 +96,25 @@ void tcp_con_set_custom_uint(TCP_Client_Connection *con, uint32_t value)
con->custom_uint = value;
}
-/** return 1 on success
- * return 0 on failure
+/**
+ * @retval true on success
+ * @retval false on failure
*/
-static int connect_sock_to(const Logger *logger, Socket sock, const IP_Port *ip_port, const TCP_Proxy_Info *proxy_info)
+non_null()
+static bool connect_sock_to(const Logger *logger, Socket sock, const IP_Port *ip_port, const TCP_Proxy_Info *proxy_info)
{
- IP_Port ipp_copy = *ip_port;
-
if (proxy_info->proxy_type != TCP_PROXY_NONE) {
- ipp_copy = proxy_info->ip_port;
+ return net_connect(logger, sock, &proxy_info->ip_port);
+ } else {
+ return net_connect(logger, sock, ip_port);
}
-
- /* nonblocking socket, connect will never return success */
- net_connect(logger, sock, &ipp_copy);
-
- return 1;
}
-/** return 1 on success.
- * return 0 on failure.
+/**
+ * @retval 1 on success.
+ * @retval 0 on failure.
*/
+non_null()
static int proxy_http_generate_connection_request(TCP_Client_Connection *tcp_conn)
{
char one[] = "CONNECT ";
@@ -134,20 +137,22 @@ static int proxy_http_generate_connection_request(TCP_Client_Connection *tcp_con
tcp_conn->con.last_packet_length = written;
tcp_conn->con.last_packet_sent = 0;
-
return 1;
}
-/** return 1 on success.
- * return 0 if no data received.
- * return -1 on failure (connection refused).
+/**
+ * @retval 1 on success.
+ * @retval 0 if no data received.
+ * @retval -1 on failure (connection refused).
*/
+non_null()
static int proxy_http_read_connection_response(const Logger *logger, const TCP_Client_Connection *tcp_conn)
{
char success[] = "200";
uint8_t data[16]; // draining works the best if the length is a power of 2
- int ret = read_TCP_packet(logger, tcp_conn->con.sock, data, sizeof(data) - 1, &tcp_conn->con.ip_port);
+ const int ret = read_TCP_packet(logger, tcp_conn->con.ns, tcp_conn->con.sock, data, sizeof(data) - 1,
+ &tcp_conn->con.ip_port);
if (ret == -1) {
return 0;
@@ -155,13 +160,21 @@ static int proxy_http_read_connection_response(const Logger *logger, const TCP_C
data[sizeof(data) - 1] = 0;
- if (strstr((const char *)data, success)) {
+ if (strstr((const char *)data, success) != nullptr) {
// drain all data
- const uint16_t data_left = net_socket_data_recv_buffer(tcp_conn->con.sock);
+ uint16_t data_left = net_socket_data_recv_buffer(tcp_conn->con.ns, tcp_conn->con.sock);
- if (data_left) {
- VLA(uint8_t, temp_data, data_left);
- read_TCP_packet(logger, tcp_conn->con.sock, temp_data, data_left, &tcp_conn->con.ip_port);
+ while (data_left > 0) {
+ uint8_t temp_data[16];
+ const uint16_t temp_data_size = min_u16(data_left, sizeof(temp_data));
+
+ if (read_TCP_packet(logger, tcp_conn->con.ns, tcp_conn->con.sock, temp_data, temp_data_size,
+ &tcp_conn->con.ip_port) == -1) {
+ LOGGER_ERROR(logger, "failed to drain TCP data (but ignoring failure)");
+ return 1;
+ }
+
+ data_left -= temp_data_size;
}
return 1;
@@ -179,6 +192,7 @@ static int proxy_http_read_connection_response(const Logger *logger, const TCP_C
#define TCP_SOCKS5_PROXY_HS_ADDR_TYPE_IPV4 0x01
#define TCP_SOCKS5_PROXY_HS_ADDR_TYPE_IPV6 0x04
+non_null()
static void proxy_socks5_generate_greetings(TCP_Client_Connection *tcp_conn)
{
tcp_conn->con.last_packet[0] = TCP_SOCKS5_PROXY_HS_VERSION_SOCKS5;
@@ -189,14 +203,16 @@ static void proxy_socks5_generate_greetings(TCP_Client_Connection *tcp_conn)
tcp_conn->con.last_packet_sent = 0;
}
-/** return 1 on success.
- * return 0 if no data received.
- * return -1 on failure (connection refused).
+/**
+ * @retval 1 on success.
+ * @retval 0 if no data received.
+ * @retval -1 on failure (connection refused).
*/
+non_null()
static int socks5_read_handshake_response(const Logger *logger, const TCP_Client_Connection *tcp_conn)
{
uint8_t data[2];
- int ret = read_TCP_packet(logger, tcp_conn->con.sock, data, sizeof(data), &tcp_conn->con.ip_port);
+ const int ret = read_TCP_packet(logger, tcp_conn->con.ns, tcp_conn->con.sock, data, sizeof(data), &tcp_conn->con.ip_port);
if (ret == -1) {
return 0;
@@ -209,6 +225,7 @@ static int socks5_read_handshake_response(const Logger *logger, const TCP_Client
return -1;
}
+non_null()
static void proxy_socks5_generate_connection_request(TCP_Client_Connection *tcp_conn)
{
tcp_conn->con.last_packet[0] = TCP_SOCKS5_PROXY_HS_VERSION_SOCKS5;
@@ -235,15 +252,17 @@ static void proxy_socks5_generate_connection_request(TCP_Client_Connection *tcp_
tcp_conn->con.last_packet_sent = 0;
}
-/** return 1 on success.
- * return 0 if no data received.
- * return -1 on failure (connection refused).
+/**
+ * @retval 1 on success.
+ * @retval 0 if no data received.
+ * @retval -1 on failure (connection refused).
*/
+non_null()
static int proxy_socks5_read_connection_response(const Logger *logger, const TCP_Client_Connection *tcp_conn)
{
if (net_family_is_ipv4(tcp_conn->ip_port.ip.family)) {
uint8_t data[4 + sizeof(IP4) + sizeof(uint16_t)];
- int ret = read_TCP_packet(logger, tcp_conn->con.sock, data, sizeof(data), &tcp_conn->con.ip_port);
+ const int ret = read_TCP_packet(logger, tcp_conn->con.ns, tcp_conn->con.sock, data, sizeof(data), &tcp_conn->con.ip_port);
if (ret == -1) {
return 0;
@@ -254,7 +273,7 @@ static int proxy_socks5_read_connection_response(const Logger *logger, const TCP
}
} else {
uint8_t data[4 + sizeof(IP6) + sizeof(uint16_t)];
- int ret = read_TCP_packet(logger, tcp_conn->con.sock, data, sizeof(data), &tcp_conn->con.ip_port);
+ int ret = read_TCP_packet(logger, tcp_conn->con.ns, tcp_conn->con.sock, data, sizeof(data), &tcp_conn->con.ip_port);
if (ret == -1) {
return 0;
@@ -268,18 +287,20 @@ static int proxy_socks5_read_connection_response(const Logger *logger, const TCP
return -1;
}
-/** return 0 on success.
- * return -1 on failure.
+/**
+ * @retval 0 on success.
+ * @retval -1 on failure.
*/
+non_null()
static int generate_handshake(TCP_Client_Connection *tcp_conn)
{
uint8_t plain[CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE];
- crypto_new_keypair(plain, tcp_conn->temp_secret_key);
- random_nonce(tcp_conn->con.sent_nonce);
+ crypto_new_keypair(tcp_conn->con.rng, plain, tcp_conn->temp_secret_key);
+ random_nonce(tcp_conn->con.rng, tcp_conn->con.sent_nonce);
memcpy(plain + CRYPTO_PUBLIC_KEY_SIZE, tcp_conn->con.sent_nonce, CRYPTO_NONCE_SIZE);
memcpy(tcp_conn->con.last_packet, tcp_conn->self_public_key, CRYPTO_PUBLIC_KEY_SIZE);
- random_nonce(tcp_conn->con.last_packet + CRYPTO_PUBLIC_KEY_SIZE);
- int len = encrypt_data_symmetric(tcp_conn->con.shared_key, tcp_conn->con.last_packet + CRYPTO_PUBLIC_KEY_SIZE, plain,
+ random_nonce(tcp_conn->con.rng, tcp_conn->con.last_packet + CRYPTO_PUBLIC_KEY_SIZE);
+ const int len = encrypt_data_symmetric(tcp_conn->con.shared_key, tcp_conn->con.last_packet + CRYPTO_PUBLIC_KEY_SIZE, plain,
sizeof(plain), tcp_conn->con.last_packet + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE);
if (len != sizeof(plain) + CRYPTO_MAC_SIZE) {
@@ -291,15 +312,17 @@ static int generate_handshake(TCP_Client_Connection *tcp_conn)
return 0;
}
-/** data must be of length TCP_SERVER_HANDSHAKE_SIZE
+/**
+ * @param data must be of length TCP_SERVER_HANDSHAKE_SIZE
*
- * return 0 on success.
- * return -1 on failure.
+ * @retval 0 on success.
+ * @retval -1 on failure.
*/
+non_null()
static int handle_handshake(TCP_Client_Connection *tcp_conn, const uint8_t *data)
{
uint8_t plain[CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE];
- int len = decrypt_data_symmetric(tcp_conn->con.shared_key, data, data + CRYPTO_NONCE_SIZE,
+ const int len = decrypt_data_symmetric(tcp_conn->con.shared_key, data, data + CRYPTO_NONCE_SIZE,
TCP_SERVER_HANDSHAKE_SIZE - CRYPTO_NONCE_SIZE, plain);
if (len != sizeof(plain)) {
@@ -312,16 +335,17 @@ static int handle_handshake(TCP_Client_Connection *tcp_conn, const uint8_t *data
return 0;
}
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure (connection must be killed).
*/
int send_routing_request(const Logger *logger, TCP_Client_Connection *con, const uint8_t *public_key)
{
uint8_t packet[1 + CRYPTO_PUBLIC_KEY_SIZE];
packet[0] = TCP_PACKET_ROUTING_REQUEST;
memcpy(packet + 1, public_key, CRYPTO_PUBLIC_KEY_SIZE);
- return write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), 1);
+ return write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), true);
}
void routing_response_handler(TCP_Client_Connection *con, tcp_routing_response_cb *response_callback, void *object)
@@ -336,12 +360,13 @@ void routing_status_handler(TCP_Client_Connection *con, tcp_routing_status_cb *s
con->status_callback_object = object;
}
-static int tcp_send_ping_response(const Logger *logger, TCP_Client_Connection *con);
-static int tcp_send_ping_request(const Logger *logger, TCP_Client_Connection *con);
+non_null() static int tcp_send_ping_response(const Logger *logger, TCP_Client_Connection *con);
+non_null() static int tcp_send_ping_request(const Logger *logger, TCP_Client_Connection *con);
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure.
+/**
+ * @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure.
*/
int send_data(const Logger *logger, TCP_Client_Connection *con, uint8_t con_id, const uint8_t *data, uint16_t length)
{
@@ -360,12 +385,13 @@ int send_data(const Logger *logger, TCP_Client_Connection *con, uint8_t con_id,
VLA(uint8_t, packet, 1 + length);
packet[0] = con_id + NUM_RESERVED_PORTS;
memcpy(packet + 1, data, length);
- return write_packet_TCP_secure_connection(logger, &con->con, packet, SIZEOF_VLA(packet), 0);
+ return write_packet_TCP_secure_connection(logger, &con->con, packet, SIZEOF_VLA(packet), false);
}
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure.
+/**
+ * @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure.
*/
int send_oob_packet(const Logger *logger, TCP_Client_Connection *con, const uint8_t *public_key, const uint8_t *data,
uint16_t length)
@@ -378,11 +404,11 @@ int send_oob_packet(const Logger *logger, TCP_Client_Connection *con, const uint
packet[0] = TCP_PACKET_OOB_SEND;
memcpy(packet + 1, public_key, CRYPTO_PUBLIC_KEY_SIZE);
memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, data, length);
- return write_packet_TCP_secure_connection(logger, &con->con, packet, SIZEOF_VLA(packet), 0);
+ return write_packet_TCP_secure_connection(logger, &con->con, packet, SIZEOF_VLA(packet), false);
}
-/** Set the number that will be used as an argument in the callbacks related to 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.
*
@@ -415,32 +441,35 @@ void oob_data_handler(TCP_Client_Connection *con, tcp_oob_data_cb *oob_data_call
con->oob_data_callback_object = object;
}
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure (connection must be killed).
*/
+non_null()
static int client_send_disconnect_notification(const Logger *logger, TCP_Client_Connection *con, uint8_t id)
{
uint8_t packet[1 + 1];
packet[0] = TCP_PACKET_DISCONNECT_NOTIFICATION;
packet[1] = id;
- return write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), 1);
+ return write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), true);
}
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure (connection must be killed).
*/
static int tcp_send_ping_request(const Logger *logger, TCP_Client_Connection *con)
{
- if (!con->ping_request_id) {
+ if (con->ping_request_id == 0) {
return 1;
}
uint8_t packet[1 + sizeof(uint64_t)];
packet[0] = TCP_PACKET_PING;
memcpy(packet + 1, &con->ping_request_id, sizeof(uint64_t));
- const int ret = write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), 1);
+ const int ret = write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), true);
if (ret == 1) {
con->ping_request_id = 0;
@@ -449,20 +478,21 @@ static int tcp_send_ping_request(const Logger *logger, TCP_Client_Connection *co
return ret;
}
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure (connection must be killed).
*/
static int tcp_send_ping_response(const Logger *logger, TCP_Client_Connection *con)
{
- if (!con->ping_response_id) {
+ if (con->ping_response_id == 0) {
return 1;
}
uint8_t packet[1 + sizeof(uint64_t)];
packet[0] = TCP_PACKET_PONG;
memcpy(packet + 1, &con->ping_response_id, sizeof(uint64_t));
- const int ret = write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), 1);
+ const int ret = write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), true);
if (ret == 1) {
con->ping_response_id = 0;
@@ -471,9 +501,10 @@ static int tcp_send_ping_response(const Logger *logger, TCP_Client_Connection *c
return ret;
}
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure (connection must be killed).
*/
int send_disconnect_request(const Logger *logger, TCP_Client_Connection *con, uint8_t con_id)
{
@@ -486,16 +517,17 @@ int send_disconnect_request(const Logger *logger, TCP_Client_Connection *con, ui
return client_send_disconnect_notification(logger, con, con_id + NUM_RESERVED_PORTS);
}
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure (connection must be killed).
*/
int send_onion_request(const Logger *logger, TCP_Client_Connection *con, const uint8_t *data, uint16_t length)
{
VLA(uint8_t, packet, 1 + length);
packet[0] = TCP_PACKET_ONION_REQUEST;
memcpy(packet + 1, data, length);
- return write_packet_TCP_secure_connection(logger, &con->con, packet, SIZEOF_VLA(packet), 0);
+ return write_packet_TCP_secure_connection(logger, &con->con, packet, SIZEOF_VLA(packet), false);
}
void onion_response_handler(TCP_Client_Connection *con, tcp_onion_response_cb *onion_callback, void *object)
@@ -504,21 +536,45 @@ void onion_response_handler(TCP_Client_Connection *con, tcp_onion_response_cb *o
con->onion_callback_object = object;
}
-/** Create new TCP connection to ip_port/public_key
+/** @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure (connection must be killed).
*/
-TCP_Client_Connection *new_TCP_connection(const Logger *logger, const Mono_Time *mono_time, 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)
+int send_forward_request_tcp(const Logger *logger, TCP_Client_Connection *con, const IP_Port *dest, const uint8_t *data, uint16_t length)
{
- if (networking_at_startup() != 0) {
- return nullptr;
+ if (length > MAX_FORWARD_DATA_SIZE) {
+ return -1;
+ }
+
+ VLA(uint8_t, packet, 1 + MAX_PACKED_IPPORT_SIZE + length);
+ packet[0] = TCP_PACKET_FORWARD_REQUEST;
+ const int ipport_length = pack_ip_port(logger, packet + 1, MAX_PACKED_IPPORT_SIZE, dest);
+
+ if (ipport_length == -1) {
+ return 0;
}
+ memcpy(packet + 1 + ipport_length, data, length);
+ return write_packet_TCP_secure_connection(logger, &con->con, packet, 1 + ipport_length + length, false);
+}
+
+void forwarding_handler(TCP_Client_Connection *con, forwarded_response_cb *forwarded_response_callback, void *object)
+{
+ con->forwarded_response_callback = forwarded_response_callback;
+ con->forwarded_response_callback_object = object;
+}
+
+/** Create new TCP connection to ip_port/public_key */
+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)
+{
if (!net_family_is_ipv4(ip_port->ip.family) && !net_family_is_ipv6(ip_port->ip.family)) {
return nullptr;
}
- const TCP_Proxy_Info default_proxyinfo = {0};
+ const TCP_Proxy_Info default_proxyinfo = {{{{0}}}};
if (proxy_info == nullptr) {
proxy_info = &default_proxyinfo;
@@ -530,29 +586,31 @@ TCP_Client_Connection *new_TCP_connection(const Logger *logger, const Mono_Time
family = proxy_info->ip_port.ip.family;
}
- Socket sock = net_socket(family, TOX_SOCK_STREAM, TOX_PROTO_TCP);
+ const Socket sock = net_socket(ns, family, TOX_SOCK_STREAM, TOX_PROTO_TCP);
if (!sock_valid(sock)) {
return nullptr;
}
- if (!set_socket_nosigpipe(sock)) {
- kill_sock(sock);
+ if (!set_socket_nosigpipe(ns, sock)) {
+ kill_sock(ns, sock);
return nullptr;
}
- if (!(set_socket_nonblock(sock) && connect_sock_to(logger, sock, ip_port, proxy_info))) {
- kill_sock(sock);
+ if (!(set_socket_nonblock(ns, sock) && connect_sock_to(logger, sock, ip_port, proxy_info))) {
+ kill_sock(ns, sock);
return nullptr;
}
TCP_Client_Connection *temp = (TCP_Client_Connection *)calloc(1, sizeof(TCP_Client_Connection));
if (temp == nullptr) {
- kill_sock(sock);
+ kill_sock(ns, sock);
return nullptr;
}
+ temp->con.ns = ns;
+ temp->con.rng = rng;
temp->con.sock = sock;
temp->con.ip_port = *ip_port;
memcpy(temp->public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
@@ -578,7 +636,7 @@ TCP_Client_Connection *new_TCP_connection(const Logger *logger, const Mono_Time
temp->status = TCP_CLIENT_CONNECTING;
if (generate_handshake(temp) == -1) {
- kill_sock(sock);
+ kill_sock(ns, sock);
free(temp);
return nullptr;
}
@@ -592,143 +650,184 @@ TCP_Client_Connection *new_TCP_connection(const Logger *logger, const Mono_Time
return temp;
}
-/** return 0 on success
- * return -1 on failure
- */
-static int handle_TCP_client_packet(const Logger *logger, TCP_Client_Connection *conn, const uint8_t *data,
- uint16_t length, void *userdata)
+non_null()
+static int handle_TCP_client_routing_response(TCP_Client_Connection *conn, const uint8_t *data, uint16_t length)
{
- if (length <= 1) {
+ if (length != 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE) {
return -1;
}
- switch (data[0]) {
- case TCP_PACKET_ROUTING_RESPONSE: {
- if (length != 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE) {
- return -1;
- }
+ if (data[1] < NUM_RESERVED_PORTS) {
+ return 0;
+ }
- if (data[1] < NUM_RESERVED_PORTS) {
- return 0;
- }
+ const uint8_t con_id = data[1] - NUM_RESERVED_PORTS;
- uint8_t con_id = data[1] - NUM_RESERVED_PORTS;
+ if (conn->connections[con_id].status != 0) {
+ return 0;
+ }
- if (conn->connections[con_id].status != 0) {
- return 0;
- }
+ conn->connections[con_id].status = 1;
+ conn->connections[con_id].number = -1;
+ memcpy(conn->connections[con_id].public_key, data + 2, CRYPTO_PUBLIC_KEY_SIZE);
- conn->connections[con_id].status = 1;
- conn->connections[con_id].number = -1;
- memcpy(conn->connections[con_id].public_key, data + 2, CRYPTO_PUBLIC_KEY_SIZE);
+ if (conn->response_callback != nullptr) {
+ conn->response_callback(conn->response_callback_object, con_id, conn->connections[con_id].public_key);
+ }
- if (conn->response_callback) {
- conn->response_callback(conn->response_callback_object, con_id, conn->connections[con_id].public_key);
- }
+ return 0;
+}
- return 0;
- }
+non_null()
+static int handle_TCP_client_connection_notification(TCP_Client_Connection *conn, const uint8_t *data, uint16_t length)
+{
+ if (length != 1 + 1) {
+ return -1;
+ }
- case TCP_PACKET_CONNECTION_NOTIFICATION: {
- if (length != 1 + 1) {
- return -1;
- }
+ if (data[1] < NUM_RESERVED_PORTS) {
+ return -1;
+ }
- if (data[1] < NUM_RESERVED_PORTS) {
- return -1;
- }
+ const uint8_t con_id = data[1] - NUM_RESERVED_PORTS;
- uint8_t con_id = data[1] - NUM_RESERVED_PORTS;
+ if (conn->connections[con_id].status != 1) {
+ return 0;
+ }
- if (conn->connections[con_id].status != 1) {
- return 0;
- }
+ conn->connections[con_id].status = 2;
- conn->connections[con_id].status = 2;
+ if (conn->status_callback != nullptr) {
+ conn->status_callback(conn->status_callback_object, conn->connections[con_id].number, con_id,
+ conn->connections[con_id].status);
+ }
- if (conn->status_callback) {
- conn->status_callback(conn->status_callback_object, conn->connections[con_id].number, con_id,
- conn->connections[con_id].status);
- }
+ return 0;
+}
- return 0;
- }
+non_null()
+static int handle_TCP_client_disconnect_notification(TCP_Client_Connection *conn, const uint8_t *data, uint16_t length)
+{
+ if (length != 1 + 1) {
+ return -1;
+ }
- case TCP_PACKET_DISCONNECT_NOTIFICATION: {
- if (length != 1 + 1) {
- return -1;
- }
+ if (data[1] < NUM_RESERVED_PORTS) {
+ return -1;
+ }
- if (data[1] < NUM_RESERVED_PORTS) {
- return -1;
- }
+ const uint8_t con_id = data[1] - NUM_RESERVED_PORTS;
- uint8_t con_id = data[1] - NUM_RESERVED_PORTS;
+ if (conn->connections[con_id].status == 0) {
+ return 0;
+ }
- if (conn->connections[con_id].status == 0) {
- return 0;
- }
+ if (conn->connections[con_id].status != 2) {
+ return 0;
+ }
- if (conn->connections[con_id].status != 2) {
- return 0;
- }
+ conn->connections[con_id].status = 1;
- conn->connections[con_id].status = 1;
+ if (conn->status_callback != nullptr) {
+ conn->status_callback(conn->status_callback_object, conn->connections[con_id].number, con_id,
+ conn->connections[con_id].status);
+ }
- if (conn->status_callback) {
- conn->status_callback(conn->status_callback_object, conn->connections[con_id].number, con_id,
- conn->connections[con_id].status);
- }
+ return 0;
+}
- return 0;
- }
+non_null()
+static int handle_TCP_client_ping(const Logger *logger, TCP_Client_Connection *conn, const uint8_t *data, uint16_t length)
+{
+ if (length != 1 + sizeof(uint64_t)) {
+ return -1;
+ }
- case TCP_PACKET_PING: {
- if (length != 1 + sizeof(uint64_t)) {
- return -1;
- }
+ uint64_t ping_id;
+ memcpy(&ping_id, data + 1, sizeof(uint64_t));
+ conn->ping_response_id = ping_id;
+ tcp_send_ping_response(logger, conn);
+ return 0;
+}
- uint64_t ping_id;
- memcpy(&ping_id, data + 1, sizeof(uint64_t));
- conn->ping_response_id = ping_id;
- tcp_send_ping_response(logger, conn);
- return 0;
+non_null()
+static int handle_TCP_client_pong(TCP_Client_Connection *conn, const uint8_t *data, uint16_t length)
+{
+ if (length != 1 + sizeof(uint64_t)) {
+ return -1;
+ }
+
+ uint64_t ping_id;
+ memcpy(&ping_id, data + 1, sizeof(uint64_t));
+
+ if (ping_id != 0) {
+ if (ping_id == conn->ping_id) {
+ conn->ping_id = 0;
}
- case TCP_PACKET_PONG: {
- if (length != 1 + sizeof(uint64_t)) {
- return -1;
- }
+ return 0;
+ }
- uint64_t ping_id;
- memcpy(&ping_id, data + 1, sizeof(uint64_t));
+ return -1;
+}
- if (ping_id) {
- if (ping_id == conn->ping_id) {
- conn->ping_id = 0;
- }
+non_null(1, 2) nullable(4)
+static int handle_TCP_client_oob_recv(TCP_Client_Connection *conn, const uint8_t *data, uint16_t length, void *userdata)
+{
+ if (length <= 1 + CRYPTO_PUBLIC_KEY_SIZE) {
+ return -1;
+ }
- return 0;
- }
+ if (conn->oob_data_callback != nullptr) {
+ conn->oob_data_callback(conn->oob_data_callback_object, data + 1, data + 1 + CRYPTO_PUBLIC_KEY_SIZE,
+ length - (1 + CRYPTO_PUBLIC_KEY_SIZE), userdata);
+ }
- return -1;
- }
+ return 0;
+}
- case TCP_PACKET_OOB_RECV: {
- if (length <= 1 + CRYPTO_PUBLIC_KEY_SIZE) {
- return -1;
- }
+/**
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+non_null(1, 2, 3) nullable(5)
+static int handle_TCP_client_packet(const Logger *logger, TCP_Client_Connection *conn, const uint8_t *data,
+ uint16_t length, void *userdata)
+{
+ if (length <= 1) {
+ return -1;
+ }
- if (conn->oob_data_callback) {
- conn->oob_data_callback(conn->oob_data_callback_object, data + 1, data + 1 + CRYPTO_PUBLIC_KEY_SIZE,
- length - (1 + CRYPTO_PUBLIC_KEY_SIZE), userdata);
- }
+ switch (data[0]) {
+ case TCP_PACKET_ROUTING_RESPONSE:
+ return handle_TCP_client_routing_response(conn, data, length);
+
+ case TCP_PACKET_CONNECTION_NOTIFICATION:
+ return handle_TCP_client_connection_notification(conn, data, length);
+
+ case TCP_PACKET_DISCONNECT_NOTIFICATION:
+ return handle_TCP_client_disconnect_notification(conn, data, length);
+ case TCP_PACKET_PING:
+ return handle_TCP_client_ping(logger, conn, data, length);
+
+ case TCP_PACKET_PONG:
+ return handle_TCP_client_pong(conn, data, length);
+
+ case TCP_PACKET_OOB_RECV:
+ return handle_TCP_client_oob_recv(conn, data, length, userdata);
+
+ case TCP_PACKET_ONION_RESPONSE: {
+ if (conn->onion_callback != nullptr) {
+ conn->onion_callback(conn->onion_callback_object, data + 1, length - 1, userdata);
+ }
return 0;
}
- case TCP_PACKET_ONION_RESPONSE: {
- conn->onion_callback(conn->onion_callback_object, data + 1, length - 1, userdata);
+ case TCP_PACKET_FORWARDING: {
+ if (conn->forwarded_response_callback != nullptr) {
+ conn->forwarded_response_callback(conn->forwarded_response_callback_object, data + 1, length - 1, userdata);
+ }
return 0;
}
@@ -737,9 +836,9 @@ static int handle_TCP_client_packet(const Logger *logger, TCP_Client_Connection
return -1;
}
- uint8_t con_id = data[0] - NUM_RESERVED_PORTS;
+ const uint8_t con_id = data[0] - NUM_RESERVED_PORTS;
- if (conn->data_callback) {
+ if (conn->data_callback != nullptr) {
conn->data_callback(conn->data_callback_object, conn->connections[con_id].number, con_id, data + 1, length - 1,
userdata);
}
@@ -749,11 +848,11 @@ static int handle_TCP_client_packet(const Logger *logger, TCP_Client_Connection
return 0;
}
+non_null(1, 2) nullable(3)
static bool tcp_process_packet(const Logger *logger, TCP_Client_Connection *conn, void *userdata)
{
uint8_t packet[MAX_PACKET_SIZE];
- const int len = read_packet_TCP_secure_connection(logger, conn->con.sock, &conn->next_packet_length,
- conn->con.shared_key, conn->recv_nonce, packet, sizeof(packet), &conn->ip_port);
+ const int len = read_packet_TCP_secure_connection(logger, conn->con.ns, conn->con.sock, &conn->next_packet_length, conn->con.shared_key, conn->recv_nonce, packet, sizeof(packet), &conn->ip_port);
if (len == 0) {
return false;
@@ -772,6 +871,7 @@ static bool tcp_process_packet(const Logger *logger, TCP_Client_Connection *conn
return true;
}
+non_null(1, 2, 3) nullable(4)
static int do_confirmed_TCP(const Logger *logger, TCP_Client_Connection *conn, const Mono_Time *mono_time,
void *userdata)
{
@@ -780,9 +880,9 @@ static int do_confirmed_TCP(const Logger *logger, TCP_Client_Connection *conn, c
tcp_send_ping_request(logger, conn);
if (mono_time_is_timeout(mono_time, conn->last_pinged, TCP_PING_FREQUENCY)) {
- uint64_t ping_id = random_u64();
+ uint64_t ping_id = random_u64(conn->con.rng);
- if (!ping_id) {
+ if (ping_id == 0) {
++ping_id;
}
@@ -792,7 +892,7 @@ static int do_confirmed_TCP(const Logger *logger, TCP_Client_Connection *conn, c
conn->last_pinged = mono_time_get(mono_time);
}
- if (conn->ping_id && mono_time_is_timeout(mono_time, conn->last_pinged, TCP_PING_TIMEOUT)) {
+ if (conn->ping_id != 0 && mono_time_is_timeout(mono_time, conn->last_pinged, TCP_PING_TIMEOUT)) {
conn->status = TCP_CLIENT_DISCONNECTED;
return 0;
}
@@ -805,8 +905,7 @@ static int do_confirmed_TCP(const Logger *logger, TCP_Client_Connection *conn, c
return 0;
}
-/** Run the TCP connection
- */
+/** Run the TCP connection */
void do_TCP_connection(const Logger *logger, const Mono_Time *mono_time,
TCP_Client_Connection *tcp_connection, void *userdata)
{
@@ -816,7 +915,7 @@ void do_TCP_connection(const Logger *logger, const Mono_Time *mono_time,
if (tcp_connection->status == TCP_CLIENT_PROXY_HTTP_CONNECTING) {
if (send_pending_data(logger, &tcp_connection->con) == 0) {
- int ret = proxy_http_read_connection_response(logger, tcp_connection);
+ const int ret = proxy_http_read_connection_response(logger, tcp_connection);
if (ret == -1) {
tcp_connection->kill_at = 0;
@@ -870,11 +969,11 @@ void do_TCP_connection(const Logger *logger, const Mono_Time *mono_time,
if (tcp_connection->status == TCP_CLIENT_UNCONFIRMED) {
uint8_t data[TCP_SERVER_HANDSHAKE_SIZE];
- int len = read_TCP_packet(logger, tcp_connection->con.sock, data, sizeof(data), &tcp_connection->con.ip_port);
+ const int len = read_TCP_packet(logger, tcp_connection->con.ns, tcp_connection->con.sock, data, sizeof(data), &tcp_connection->con.ip_port);
if (sizeof(data) == len) {
if (handle_handshake(tcp_connection, data) == 0) {
- tcp_connection->kill_at = -1;
+ tcp_connection->kill_at = UINT64_MAX;
tcp_connection->status = TCP_CLIENT_CONFIRMED;
} else {
tcp_connection->kill_at = 0;
@@ -892,8 +991,7 @@ void do_TCP_connection(const Logger *logger, const Mono_Time *mono_time,
}
}
-/** Kill the TCP connection
- */
+/** Kill the TCP connection */
void kill_TCP_connection(TCP_Client_Connection *tcp_connection)
{
if (tcp_connection == nullptr) {
@@ -901,7 +999,7 @@ void kill_TCP_connection(TCP_Client_Connection *tcp_connection)
}
wipe_priority_list(tcp_connection->con.priority_queue_start);
- kill_sock(tcp_connection->con.sock);
+ kill_sock(tcp_connection->con.ns, tcp_connection->con.sock);
crypto_memzero(tcp_connection, sizeof(TCP_Client_Connection));
free(tcp_connection);
}
diff --git a/protocols/Tox/libtox/src/toxcore/TCP_client.h b/protocols/Tox/libtox/src/toxcore/TCP_client.h
index dd170d7849..fdc91de70d 100644
--- a/protocols/Tox/libtox/src/toxcore/TCP_client.h
+++ b/protocols/Tox/libtox/src/toxcore/TCP_client.h
@@ -9,6 +9,8 @@
#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"
@@ -38,84 +40,114 @@ typedef enum 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
- */
-TCP_Client_Connection *new_TCP_connection(const Logger *logger, const Mono_Time *mono_time, const IP_Port *ip_port,
+/** 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
- */
+/** 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
- */
+/** 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);
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @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);
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @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);
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @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);
-/** Set the number that will be used as an argument in the callbacks related to 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);
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure.
+/**
+ * @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);
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure.
+/**
+ * @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);
diff --git a/protocols/Tox/libtox/src/toxcore/TCP_common.c b/protocols/Tox/libtox/src/toxcore/TCP_common.c
index 237df20f89..349e35fda7 100644
--- a/protocols/Tox/libtox/src/toxcore/TCP_common.c
+++ b/protocols/Tox/libtox/src/toxcore/TCP_common.c
@@ -8,9 +8,11 @@
#include <stdlib.h>
#include <string.h>
+#include "ccompat.h"
+
void wipe_priority_list(TCP_Priority_List *p)
{
- while (p) {
+ while (p != nullptr) {
TCP_Priority_List *pp = p;
p = p->next;
free(pp->data);
@@ -18,8 +20,9 @@ void wipe_priority_list(TCP_Priority_List *p)
}
}
-/** return 0 if pending data was sent completely
- * return -1 if it wasn't
+/**
+ * @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)
{
@@ -28,7 +31,7 @@ int send_pending_data_nonpriority(const Logger *logger, TCP_Connection *con)
}
const uint16_t left = con->last_packet_length - con->last_packet_sent;
- const int len = net_send(logger, con->sock, con->last_packet + con->last_packet_sent, left, &con->ip_port);
+ 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;
@@ -44,8 +47,9 @@ int send_pending_data_nonpriority(const Logger *logger, TCP_Connection *con)
return -1;
}
-/** return 0 if pending data was sent completely
- * return -1 if it wasn't
+/**
+ * @retval 0 if pending data was sent completely
+ * @retval -1 if it wasn't
*/
int send_pending_data(const Logger *logger, TCP_Connection *con)
{
@@ -56,9 +60,9 @@ int send_pending_data(const Logger *logger, TCP_Connection *con)
TCP_Priority_List *p = con->priority_queue_start;
- while (p) {
+ while (p != nullptr) {
const uint16_t left = p->size - p->sent;
- const int len = net_send(logger, con->sock, p->data + p->sent, left, &con->ip_port);
+ const int len = net_send(con->ns, logger, con->sock, p->data + p->sent, left, &con->ip_port);
if (len != left) {
if (len > 0) {
@@ -76,7 +80,7 @@ int send_pending_data(const Logger *logger, TCP_Connection *con)
con->priority_queue_start = p;
- if (!p) {
+ if (p == nullptr) {
con->priority_queue_end = nullptr;
return 0;
}
@@ -84,10 +88,12 @@ int send_pending_data(const Logger *logger, TCP_Connection *con)
return -1;
}
-/** return 0 on failure (only if calloc fails)
- * return 1 on success
+/**
+ * @retval false on failure (only if calloc fails)
+ * @retval true on success
*/
-bool add_priority(TCP_Connection *con, const uint8_t *packet, uint16_t size, uint16_t sent)
+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));
@@ -108,7 +114,7 @@ bool add_priority(TCP_Connection *con, const uint8_t *packet, uint16_t size, uin
memcpy(new_list->data, packet, size);
- if (p) {
+ if (p != nullptr) {
p->next = new_list;
} else {
con->priority_queue_start = new_list;
@@ -118,9 +124,10 @@ bool add_priority(TCP_Connection *con, const uint8_t *packet, uint16_t size, uin
return true;
}
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @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)
@@ -129,11 +136,11 @@ int write_packet_TCP_secure_connection(const Logger *logger, TCP_Connection *con
return -1;
}
- bool sendpriority = 1;
+ bool sendpriority = true;
if (send_pending_data(logger, con) == -1) {
if (priority) {
- sendpriority = 0;
+ sendpriority = false;
} else {
return 0;
}
@@ -150,7 +157,7 @@ int write_packet_TCP_secure_connection(const Logger *logger, TCP_Connection *con
}
if (priority) {
- len = sendpriority ? net_send(logger, con->sock, packet, SIZEOF_VLA(packet), &con->ip_port) : 0;
+ len = sendpriority ? net_send(con->ns, logger, con->sock, packet, SIZEOF_VLA(packet), &con->ip_port) : 0;
if (len <= 0) {
len = 0;
@@ -162,10 +169,10 @@ int write_packet_TCP_secure_connection(const Logger *logger, TCP_Connection *con
return 1;
}
- return add_priority(con, packet, SIZEOF_VLA(packet), len);
+ return add_priority(con, packet, SIZEOF_VLA(packet), len) ? 1 : 0;
}
- len = net_send(logger, con->sock, packet, SIZEOF_VLA(packet), &con->ip_port);
+ len = net_send(con->ns, logger, con->sock, packet, SIZEOF_VLA(packet), &con->ip_port);
if (len <= 0) {
return 0;
@@ -183,21 +190,21 @@ int write_packet_TCP_secure_connection(const Logger *logger, TCP_Connection *con
return 1;
}
-/** Read length bytes from socket.
+/** @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, Socket sock, uint8_t *data, uint16_t length, const IP_Port *ip_port)
+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(sock);
+ 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(logger, sock, data, length, ip_port);
+ const int len = net_recv(ns, logger, sock, data, length, ip_port);
if (len != length) {
LOGGER_ERROR(logger, "FAIL recv packet");
@@ -207,20 +214,21 @@ int read_TCP_packet(const Logger *logger, Socket sock, uint8_t *data, uint16_t l
return len;
}
-/** Read the next two bytes in TCP stream then convert them to
+/** @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.
*/
-static uint16_t read_TCP_length(const Logger *logger, Socket sock, const IP_Port *ip_port)
+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(sock);
+ 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(logger, sock, length_buf, sizeof(length_buf), ip_port);
+ 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");
@@ -231,7 +239,7 @@ static uint16_t read_TCP_length(const Logger *logger, Socket sock, const IP_Port
net_unpack_u16(length_buf, &length);
if (length > MAX_PACKET_SIZE) {
- LOGGER_WARNING(logger, "TCP packet too large: %d > %d", length, MAX_PACKET_SIZE);
+ LOGGER_ERROR(logger, "TCP packet too large: %d > %d", length, MAX_PACKET_SIZE);
return -1;
}
@@ -241,16 +249,18 @@ static uint16_t read_TCP_length(const Logger *logger, Socket sock, const IP_Port
return 0;
}
-/** return length of received packet on success.
- * return 0 if could not read any packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @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, 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)
+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, sock, ip_port);
+ const uint16_t len = read_TCP_length(logger, ns, sock, ip_port);
if (len == (uint16_t) -1) {
return -1;
@@ -269,10 +279,14 @@ int read_packet_TCP_secure_connection(const Logger *logger, Socket sock, uint16_
}
VLA(uint8_t, data_encrypted, *next_packet_length);
- const int len_packet = read_TCP_packet(logger, sock, data_encrypted, *next_packet_length, ip_port);
+ 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);
+ LOGGER_ERROR(logger, "invalid packet length: %d, expected %d", len_packet, *next_packet_length);
return 0;
}
@@ -281,7 +295,7 @@ int read_packet_TCP_secure_connection(const Logger *logger, Socket sock, uint16_
const int len = decrypt_data_symmetric(shared_key, recv_nonce, data_encrypted, len_packet, data);
if (len + CRYPTO_MAC_SIZE != len_packet) {
- LOGGER_WARNING(logger, "decrypted length %d does not match expected length %d", 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;
}
diff --git a/protocols/Tox/libtox/src/toxcore/TCP_common.h b/protocols/Tox/libtox/src/toxcore/TCP_common.h
index 92c0a9a3d9..88c0cb665a 100644
--- a/protocols/Tox/libtox/src/toxcore/TCP_common.h
+++ b/protocols/Tox/libtox/src/toxcore/TCP_common.h
@@ -17,6 +17,7 @@ struct TCP_Priority_List {
uint8_t *data;
};
+nullable(1)
void wipe_priority_list(TCP_Priority_List *p);
#define NUM_RESERVED_PORTS 16
@@ -32,6 +33,8 @@ void wipe_priority_list(TCP_Priority_List *p);
#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
#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)
@@ -45,6 +48,8 @@ void wipe_priority_list(TCP_Priority_List *p);
#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. */
@@ -57,41 +62,48 @@ typedef struct TCP_Connection {
TCP_Priority_List *priority_queue_end;
} TCP_Connection;
-/** return 0 if pending data was sent completely
- * return -1 if it wasn't
+/**
+ * @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);
-/** return 0 if pending data was sent completely
- * return -1 if it wasn't
+/**
+ * @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);
-/** return 0 on failure (only if calloc fails)
- * return 1 on success
+/**
+ * @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure (connection must be killed).
*/
-bool add_priority(TCP_Connection *con, const uint8_t *packet, uint16_t size, uint16_t sent);
+non_null()
+int write_packet_TCP_secure_connection(
+ const Logger *logger, TCP_Connection *con, const uint8_t *data, uint16_t length,
+ bool priority);
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -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);
-
-/** Read length bytes from socket.
+/** @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, Socket sock, uint8_t *data, uint16_t length, const IP_Port *ip_port);
-
-/** return length of received packet on success.
- * return 0 if could not read any packet.
- * return -1 on failure (connection must be killed).
+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).
*/
-int read_packet_TCP_secure_connection(const Logger *logger, 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);
+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
diff --git a/protocols/Tox/libtox/src/toxcore/TCP_connection.c b/protocols/Tox/libtox/src/toxcore/TCP_connection.c
index 4b406097c2..c67df1b2eb 100644
--- a/protocols/Tox/libtox/src/toxcore/TCP_connection.c
+++ b/protocols/Tox/libtox/src/toxcore/TCP_connection.c
@@ -13,12 +13,15 @@
#include <string.h>
#include "TCP_client.h"
+#include "ccompat.h"
#include "mono_time.h"
#include "util.h"
struct TCP_Connections {
const Logger *logger;
+ const Random *rng;
Mono_Time *mono_time;
+ const Network *ns;
DHT *dht;
uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE];
@@ -39,6 +42,9 @@ struct TCP_Connections {
tcp_onion_cb *tcp_onion_callback;
void *tcp_onion_callback_object;
+ forwarded_response_cb *tcp_forwarded_response_callback;
+ void *tcp_forwarded_response_callback_object;
+
TCP_Proxy_Info proxy_info;
bool onion_status;
@@ -46,6 +52,10 @@ struct TCP_Connections {
};
+static const TCP_Connection_to empty_tcp_connection_to = {0};
+static const TCP_con empty_tcp_con = {0};
+
+
const uint8_t *tcp_connections_public_key(const TCP_Connections *tcp_c)
{
return tcp_c->self_public_key;
@@ -57,14 +67,15 @@ uint32_t tcp_connections_count(const TCP_Connections *tcp_c)
return tcp_c->tcp_connections_length;
}
-/** Set the size of the array to num.
+/** @brief Set the size of the array to num.
*
- * return -1 if realloc fails.
- * return 0 if it succeeds.
+ * @retval -1 if realloc fails.
+ * @retval 0 if it succeeds.
*/
+non_null()
static int realloc_TCP_Connection_to(TCP_Connection_to **array, size_t num)
{
- if (!num) {
+ if (num == 0) {
free(*array);
*array = nullptr;
return 0;
@@ -73,7 +84,7 @@ static int realloc_TCP_Connection_to(TCP_Connection_to **array, size_t num)
TCP_Connection_to *temp_pointer =
(TCP_Connection_to *)realloc(*array, num * sizeof(TCP_Connection_to));
- if (!temp_pointer) {
+ if (temp_pointer == nullptr) {
return -1;
}
@@ -82,9 +93,10 @@ static int realloc_TCP_Connection_to(TCP_Connection_to **array, size_t num)
return 0;
}
+non_null()
static int realloc_TCP_con(TCP_con **array, size_t num)
{
- if (!num) {
+ if (num == 0) {
free(*array);
*array = nullptr;
return 0;
@@ -92,7 +104,7 @@ static int realloc_TCP_con(TCP_con **array, size_t num)
TCP_con *temp_pointer = (TCP_con *)realloc(*array, num * sizeof(TCP_con));
- if (!temp_pointer) {
+ if (temp_pointer == nullptr) {
return -1;
}
@@ -105,6 +117,7 @@ static int realloc_TCP_con(TCP_con **array, size_t num)
/**
* Return true if the connections_number is valid.
*/
+non_null()
static bool connections_number_is_valid(const TCP_Connections *tcp_c, int connections_number)
{
if ((unsigned int)connections_number >= tcp_c->connections_length) {
@@ -115,16 +128,13 @@ static bool connections_number_is_valid(const TCP_Connections *tcp_c, int connec
return false;
}
- if (tcp_c->connections[connections_number].status == TCP_CONN_NONE) {
- return false;
- }
-
- return true;
+ return tcp_c->connections[connections_number].status != TCP_CONN_NONE;
}
/**
* Return true if the tcp_connections_number is valid.
*/
+non_null()
static bool tcp_connections_number_is_valid(const TCP_Connections *tcp_c, int tcp_connections_number)
{
if ((uint32_t)tcp_connections_number >= tcp_c->tcp_connections_length) {
@@ -135,18 +145,15 @@ static bool tcp_connections_number_is_valid(const TCP_Connections *tcp_c, int tc
return false;
}
- if (tcp_c->tcp_connections[tcp_connections_number].status == TCP_CONN_NONE) {
- return false;
- }
-
- return true;
+ return tcp_c->tcp_connections[tcp_connections_number].status != TCP_CONN_NONE;
}
-/** Create a new empty connection.
+/** @brief Create a new empty connection.
*
* return -1 on failure.
* return connections_number on success.
*/
+non_null()
static int create_connection(TCP_Connections *tcp_c)
{
for (uint32_t i = 0; i < tcp_c->connections_length; ++i) {
@@ -160,17 +167,18 @@ static int create_connection(TCP_Connections *tcp_c)
if (realloc_TCP_Connection_to(&tcp_c->connections, tcp_c->connections_length + 1) == 0) {
id = tcp_c->connections_length;
++tcp_c->connections_length;
- memset(&tcp_c->connections[id], 0, sizeof(TCP_Connection_to));
+ tcp_c->connections[id] = empty_tcp_connection_to;
}
return id;
}
-/** Create a new empty tcp connection.
+/** @brief Create a new empty tcp connection.
*
* return -1 on failure.
* return tcp_connections_number on success.
*/
+non_null()
static int create_tcp_connection(TCP_Connections *tcp_c)
{
for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) {
@@ -184,17 +192,18 @@ static int create_tcp_connection(TCP_Connections *tcp_c)
if (realloc_TCP_con(&tcp_c->tcp_connections, tcp_c->tcp_connections_length + 1) == 0) {
id = tcp_c->tcp_connections_length;
++tcp_c->tcp_connections_length;
- memset(&tcp_c->tcp_connections[id], 0, sizeof(TCP_con));
+ tcp_c->tcp_connections[id] = empty_tcp_con;
}
return id;
}
-/** Wipe a connection.
+/** @brief Wipe a connection.
*
* return -1 on failure.
* return 0 on success.
*/
+non_null()
static int wipe_connection(TCP_Connections *tcp_c, int connections_number)
{
if (!connections_number_is_valid(tcp_c, connections_number)) {
@@ -202,7 +211,7 @@ static int wipe_connection(TCP_Connections *tcp_c, int connections_number)
}
uint32_t i;
- memset(&tcp_c->connections[connections_number], 0, sizeof(TCP_Connection_to));
+ tcp_c->connections[connections_number] = empty_tcp_connection_to;
for (i = tcp_c->connections_length; i != 0; --i) {
if (tcp_c->connections[i - 1].status != TCP_CONN_NONE) {
@@ -218,18 +227,19 @@ static int wipe_connection(TCP_Connections *tcp_c, int connections_number)
return 0;
}
-/** Wipe a connection.
+/** @brief Wipe a connection.
*
* return -1 on failure.
* return 0 on success.
*/
+non_null()
static int wipe_tcp_connection(TCP_Connections *tcp_c, int tcp_connections_number)
{
if (!tcp_connections_number_is_valid(tcp_c, tcp_connections_number)) {
return -1;
}
- memset(&tcp_c->tcp_connections[tcp_connections_number], 0, sizeof(TCP_con));
+ tcp_c->tcp_connections[tcp_connections_number] = empty_tcp_con;
uint32_t i;
@@ -247,6 +257,7 @@ static int wipe_tcp_connection(TCP_Connections *tcp_c, int tcp_connections_numbe
return 0;
}
+non_null()
static TCP_Connection_to *get_connection(const TCP_Connections *tcp_c, int connections_number)
{
if (!connections_number_is_valid(tcp_c, connections_number)) {
@@ -256,6 +267,7 @@ static TCP_Connection_to *get_connection(const TCP_Connections *tcp_c, int conne
return &tcp_c->connections[connections_number];
}
+non_null()
static TCP_con *get_tcp_connection(const TCP_Connections *tcp_c, int tcp_connections_number)
{
if (!tcp_connections_number_is_valid(tcp_c, tcp_connections_number)) {
@@ -285,7 +297,7 @@ uint32_t tcp_connected_relays_count(const TCP_Connections *tcp_c)
return count;
}
-/** Send a packet to the TCP connection.
+/** @brief Send a packet to the TCP connection.
*
* return -1 on failure.
* return 0 on success.
@@ -295,7 +307,7 @@ int send_packet_tcp_connection(const TCP_Connections *tcp_c, int connections_num
{
const TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
- if (!con_to) {
+ if (con_to == nullptr) {
return -1;
}
@@ -303,25 +315,25 @@ int send_packet_tcp_connection(const TCP_Connections *tcp_c, int connections_num
// TODO(irungentoo): thread safety?
int ret = -1;
- bool limit_reached = 0;
+ bool limit_reached = false;
- for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
uint32_t tcp_con_num = con_to->connections[i].tcp_connection;
- uint8_t status = con_to->connections[i].status;
- uint8_t connection_id = con_to->connections[i].connection_id;
+ const uint8_t status = con_to->connections[i].status;
+ const uint8_t connection_id = con_to->connections[i].connection_id;
- if (tcp_con_num && status == TCP_CONNECTIONS_STATUS_ONLINE) {
+ if (tcp_con_num > 0 && status == TCP_CONNECTIONS_STATUS_ONLINE) {
tcp_con_num -= 1;
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_con_num);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
continue;
}
ret = send_data(tcp_c->logger, tcp_con->connection, connection_id, packet, length);
if (ret == 0) {
- limit_reached = 1;
+ limit_reached = true;
}
if (ret == 1) {
@@ -334,39 +346,35 @@ int send_packet_tcp_connection(const TCP_Connections *tcp_c, int connections_num
return 0;
}
- if (!limit_reached) {
- ret = 0;
+ if (limit_reached) {
+ return -1;
+ }
- /* Send oob packets to all relays tied to the connection. */
- for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
- uint32_t tcp_con_num = con_to->connections[i].tcp_connection;
- uint8_t status = con_to->connections[i].status;
+ bool sent_any = false;
- if (tcp_con_num && status == TCP_CONNECTIONS_STATUS_REGISTERED) {
- tcp_con_num -= 1;
- TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_con_num);
+ /* Send oob packets to all relays tied to the connection. */
+ for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ uint32_t tcp_con_num = con_to->connections[i].tcp_connection;
+ const uint8_t status = con_to->connections[i].status;
- if (!tcp_con) {
- continue;
- }
+ if (tcp_con_num > 0 && status == TCP_CONNECTIONS_STATUS_REGISTERED) {
+ tcp_con_num -= 1;
+ TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_con_num);
- if (send_oob_packet(tcp_c->logger, tcp_con->connection, con_to->public_key, packet, length) == 1) {
- ret += 1;
- }
+ if (tcp_con == nullptr) {
+ continue;
}
- }
- if (ret >= 1) {
- return 0;
+ if (send_oob_packet(tcp_c->logger, tcp_con->connection, con_to->public_key, packet, length) == 1) {
+ sent_any = true;
+ }
}
-
- return -1;
}
- return -1;
+ return sent_any ? 0 : -1;
}
-/** Return a random TCP connection number for use in send_tcp_onion_request.
+/** @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.
@@ -376,10 +384,10 @@ int send_packet_tcp_connection(const TCP_Connections *tcp_c, int connections_num
*/
int get_random_tcp_onion_conn_number(const TCP_Connections *tcp_c)
{
- const uint32_t r = random_u32();
+ const uint32_t r = random_u32(tcp_c->rng);
for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) {
- uint32_t index = (i + r) % tcp_c->tcp_connections_length;
+ const uint32_t index = (i + r) % tcp_c->tcp_connections_length;
if (tcp_c->tcp_connections[index].onion && tcp_c->tcp_connections[index].status == TCP_CONN_CONNECTED) {
return index;
@@ -389,7 +397,44 @@ int get_random_tcp_onion_conn_number(const TCP_Connections *tcp_c)
return -1;
}
-/** Send an onion packet via the TCP relay corresponding to tcp_connections_number.
+/** @brief Return TCP connection number of active TCP connection with ip_port.
+ *
+ * return TCP connection number on success.
+ * return -1 on failure.
+ */
+non_null()
+static int get_conn_number_by_ip_port(TCP_Connections *tcp_c, const IP_Port *ip_port)
+{
+ for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) {
+ const IP_Port conn_ip_port = tcp_con_ip_port(tcp_c->tcp_connections[i].connection);
+
+ if (ipport_equal(ip_port, &conn_ip_port) &&
+ tcp_c->tcp_connections[i].status == TCP_CONN_CONNECTED) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/** @brief Put IP_Port of a random onion TCP connection in ip_port.
+ *
+ * return true on success.
+ * return false on failure.
+ */
+bool tcp_get_random_conn_ip_port(const TCP_Connections *tcp_c, IP_Port *ip_port)
+{
+ const int index = get_random_tcp_onion_conn_number(tcp_c);
+
+ if (index == -1) {
+ return false;
+ }
+
+ *ip_port = tcp_con_ip_port(tcp_c->tcp_connections[index].connection);
+ return true;
+}
+
+/** @brief Send an onion packet via the TCP relay corresponding to tcp_connections_number.
*
* return 0 on success.
* return -1 on failure.
@@ -413,7 +458,37 @@ int tcp_send_onion_request(TCP_Connections *tcp_c, uint32_t tcp_connections_numb
return -1;
}
-/** Send an oob packet via the TCP relay corresponding to tcp_connections_number.
+/* 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.
+ */
+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)
+{
+ const int index = get_conn_number_by_ip_port(tcp_c, tcp_forwarder);
+
+ if (index == -1) {
+ return -1;
+ }
+
+ if (chain_length == 0) {
+ return send_forward_request_tcp(logger, tcp_c->tcp_connections[index].connection, dht_node, data,
+ data_length) == 1 ? 0 : -1;
+ }
+
+ 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)
+ && send_forward_request_tcp(logger, tcp_c->tcp_connections[index].connection, dht_node, packet, len) == 1 ? 0 : -1;
+}
+
+/** @brief Send an oob packet via the TCP relay corresponding to tcp_connections_number.
*
* return 0 on success.
* return -1 on failure.
@@ -423,7 +498,7 @@ int tcp_send_oob_packet(const TCP_Connections *tcp_c, unsigned int tcp_connectio
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
@@ -440,9 +515,10 @@ int tcp_send_oob_packet(const TCP_Connections *tcp_c, unsigned int tcp_connectio
return -1;
}
+non_null()
static int find_tcp_connection_relay(const TCP_Connections *tcp_c, const uint8_t *relay_pk);
-/** Send an oob packet via the TCP relay corresponding to relay_pk.
+/** @brief Send an oob packet via the TCP relay corresponding to relay_pk.
*
* return 0 on success.
* return -1 on failure.
@@ -450,7 +526,7 @@ static int find_tcp_connection_relay(const TCP_Connections *tcp_c, const uint8_t
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)
{
- int tcp_con_number = find_tcp_connection_relay(tcp_c, relay_pk);
+ const int tcp_con_number = find_tcp_connection_relay(tcp_c, relay_pk);
if (tcp_con_number < 0) {
return -1;
@@ -459,42 +535,72 @@ int tcp_send_oob_packet_using_relay(const TCP_Connections *tcp_c, const uint8_t
return tcp_send_oob_packet(tcp_c, tcp_con_number, public_key, packet, length);
}
-/** Set the callback for TCP data packets.
- */
+/** @brief Set the callback for TCP data packets. */
void set_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_data_cb *tcp_data_callback, void *object)
{
tcp_c->tcp_data_callback = tcp_data_callback;
tcp_c->tcp_data_callback_object = object;
}
-/** Set the callback for TCP oob data packets.
- */
+/** @brief Set the callback for TCP oob data packets. */
void set_oob_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_oob_cb *tcp_oob_callback, void *object)
{
tcp_c->tcp_oob_callback = tcp_oob_callback;
tcp_c->tcp_oob_callback_object = object;
}
-/** Set the callback for TCP onion packets.
- */
+/** @brief Set the callback for TCP onion packets. */
void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_onion_cb *tcp_onion_callback, void *object)
{
tcp_c->tcp_onion_callback = tcp_onion_callback;
tcp_c->tcp_onion_callback_object = object;
}
-/** Find the TCP connection with public_key.
+/** @brief Set the callback for TCP forwarding packets. */
+void set_forwarding_packet_tcp_connection_callback(TCP_Connections *tcp_c,
+ forwarded_response_cb *tcp_forwarded_response_callback,
+ void *object)
+{
+ tcp_c->tcp_forwarded_response_callback = tcp_forwarded_response_callback;
+ tcp_c->tcp_forwarded_response_callback_object = 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)
+{
+ IP_Port ip_port = {{{0}}};
+ ip_port.ip.family = net_family_tcp_server();
+ ip_port.ip.ip.v6.uint32[0] = tcp_connections_number;
+ return ip_port;
+}
+
+/** @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.
+ */
+bool ip_port_to_tcp_connections_number(const IP_Port *ip_port, unsigned int *tcp_connections_number)
+{
+ *tcp_connections_number = ip_port->ip.ip.v6.uint32[0];
+ return net_family_is_tcp_server(ip_port->ip.family);
+}
+
+/** @brief Find the TCP connection with public_key.
*
* return connections_number on success.
* return -1 on failure.
*/
+non_null()
static int find_tcp_connection_to(const TCP_Connections *tcp_c, const uint8_t *public_key)
{
- for (unsigned int i = 0; i < tcp_c->connections_length; ++i) {
+ for (uint32_t i = 0; i < tcp_c->connections_length; ++i) {
const TCP_Connection_to *con_to = get_connection(tcp_c, i);
- if (con_to) {
- if (public_key_cmp(con_to->public_key, public_key) == 0) {
+ if (con_to != nullptr) {
+ if (pk_equal(con_to->public_key, public_key)) {
return i;
}
}
@@ -503,7 +609,7 @@ static int find_tcp_connection_to(const TCP_Connections *tcp_c, const uint8_t *p
return -1;
}
-/** Find the TCP connection to a relay with relay_pk.
+/** @brief Find the TCP connection to a relay with relay_pk.
*
* return connections_number on success.
* return -1 on failure.
@@ -513,13 +619,13 @@ static int find_tcp_connection_relay(const TCP_Connections *tcp_c, const uint8_t
for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) {
const TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
- if (tcp_con) {
+ if (tcp_con != nullptr) {
if (tcp_con->status == TCP_CONN_SLEEPING) {
- if (public_key_cmp(tcp_con->relay_pk, relay_pk) == 0) {
+ if (pk_equal(tcp_con->relay_pk, relay_pk)) {
return i;
}
} else {
- if (public_key_cmp(tcp_con_public_key(tcp_con->connection), relay_pk) == 0) {
+ if (pk_equal(tcp_con_public_key(tcp_con->connection), relay_pk)) {
return i;
}
}
@@ -529,9 +635,9 @@ static int find_tcp_connection_relay(const TCP_Connections *tcp_c, const uint8_t
return -1;
}
-/** Create a new TCP connection to public_key.
+/** @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().
+ * 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.
*
@@ -544,7 +650,7 @@ int new_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key, int
return -1;
}
- int connections_number = create_connection(tcp_c);
+ const int connections_number = create_connection(tcp_c);
if (connections_number == -1) {
return -1;
@@ -559,23 +665,24 @@ int new_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key, int
return connections_number;
}
-/** return 0 on success.
- * return -1 on failure.
+/**
+ * @retval 0 on success.
+ * @retval -1 on failure.
*/
int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number)
{
const TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
- if (!con_to) {
+ if (con_to == nullptr) {
return -1;
}
- for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
- if (con_to->connections[i].tcp_connection) {
- unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1;
+ for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ if (con_to->connections[i].tcp_connection > 0) {
+ const unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1;
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
continue;
}
@@ -596,7 +703,7 @@ int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number)
return wipe_connection(tcp_c, connections_number);
}
-/** Set connection status.
+/** @brief Set connection status.
*
* status of 1 means we are using the connection.
* status of 0 means we are not using it.
@@ -610,7 +717,7 @@ int set_tcp_connection_to_status(const TCP_Connections *tcp_c, int connections_n
{
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
- if (!con_to) {
+ if (con_to == nullptr) {
return -1;
}
@@ -620,12 +727,12 @@ int set_tcp_connection_to_status(const TCP_Connections *tcp_c, int connections_n
return -1;
}
- for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
- if (con_to->connections[i].tcp_connection) {
- unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1;
+ for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ if (con_to->connections[i].tcp_connection > 0) {
+ const unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1;
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
continue;
}
@@ -644,12 +751,12 @@ int set_tcp_connection_to_status(const TCP_Connections *tcp_c, int connections_n
return -1;
}
- for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
- if (con_to->connections[i].tcp_connection) {
+ for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ if (con_to->connections[i].tcp_connection > 0) {
unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1;
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
continue;
}
@@ -663,27 +770,30 @@ int set_tcp_connection_to_status(const TCP_Connections *tcp_c, int connections_n
return 0;
}
+non_null()
static bool tcp_connection_in_conn(const TCP_Connection_to *con_to, unsigned int tcp_connections_number)
{
- for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
-/** return index on success.
- * return -1 on failure.
+/**
+ * @return index on success.
+ * @retval -1 on failure.
*/
+non_null()
static int add_tcp_connection_to_conn(TCP_Connection_to *con_to, unsigned int tcp_connections_number)
{
if (tcp_connection_in_conn(con_to, tcp_connections_number)) {
return -1;
}
- for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
if (con_to->connections[i].tcp_connection == 0) {
con_to->connections[i].tcp_connection = tcp_connections_number + 1;
con_to->connections[i].status = TCP_CONNECTIONS_STATUS_NONE;
@@ -695,12 +805,14 @@ static int add_tcp_connection_to_conn(TCP_Connection_to *con_to, unsigned int tc
return -1;
}
-/** return index on success.
- * return -1 on failure.
+/**
+ * @return index on success.
+ * @retval -1 on failure.
*/
+non_null()
static int rm_tcp_connection_from_conn(TCP_Connection_to *con_to, unsigned int tcp_connections_number)
{
- for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) {
con_to->connections[i].tcp_connection = 0;
con_to->connections[i].status = TCP_CONNECTIONS_STATUS_NONE;
@@ -712,15 +824,17 @@ static int rm_tcp_connection_from_conn(TCP_Connection_to *con_to, unsigned int t
return -1;
}
-/** return number of online connections on success.
- * return -1 on failure.
+/**
+ * @return number of online connections on success.
+ * @retval -1 on failure.
*/
-static unsigned int online_tcp_connection_from_conn(const TCP_Connection_to *con_to)
+non_null()
+static uint32_t online_tcp_connection_from_conn(const TCP_Connection_to *con_to)
{
- unsigned int count = 0;
+ uint32_t count = 0;
- for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
- if (con_to->connections[i].tcp_connection) {
+ for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ if (con_to->connections[i].tcp_connection > 0) {
if (con_to->connections[i].status == TCP_CONNECTIONS_STATUS_ONLINE) {
++count;
}
@@ -730,14 +844,16 @@ static unsigned int online_tcp_connection_from_conn(const TCP_Connection_to *con
return count;
}
-/** return index on success.
- * return -1 on failure.
+/**
+ * @return index on success.
+ * @retval -1 on failure.
*/
+non_null()
static int set_tcp_connection_status(TCP_Connection_to *con_to, unsigned int tcp_connections_number,
uint8_t status,
uint8_t connection_id)
{
- for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
+ for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) {
if (con_to->connections[i].status == status) {
@@ -753,7 +869,7 @@ static int set_tcp_connection_status(TCP_Connection_to *con_to, unsigned int tcp
return -1;
}
-/** Kill a TCP relay connection.
+/** @brief Kill a TCP relay connection.
*
* return 0 on success.
* return -1 on failure.
@@ -762,14 +878,14 @@ int kill_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
- for (unsigned int i = 0; i < tcp_c->connections_length; ++i) {
+ for (uint32_t i = 0; i < tcp_c->connections_length; ++i) {
TCP_Connection_to *con_to = get_connection(tcp_c, i);
- if (con_to) {
+ if (con_to != nullptr) {
rm_tcp_connection_from_conn(con_to, tcp_connections_number);
}
}
@@ -783,11 +899,12 @@ int kill_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number
return wipe_tcp_connection(tcp_c, tcp_connections_number);
}
+non_null()
static int reconnect_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number)
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
@@ -799,18 +916,17 @@ static int reconnect_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connec
uint8_t relay_pk[CRYPTO_PUBLIC_KEY_SIZE];
memcpy(relay_pk, tcp_con_public_key(tcp_con->connection), CRYPTO_PUBLIC_KEY_SIZE);
kill_TCP_connection(tcp_con->connection);
- tcp_con->connection = new_TCP_connection(tcp_c->logger, tcp_c->mono_time, &ip_port, relay_pk, tcp_c->self_public_key,
- tcp_c->self_secret_key, &tcp_c->proxy_info);
+ tcp_con->connection = new_TCP_connection(tcp_c->logger, tcp_c->mono_time, tcp_c->rng, tcp_c->ns, &ip_port, relay_pk, tcp_c->self_public_key, tcp_c->self_secret_key, &tcp_c->proxy_info);
- if (!tcp_con->connection) {
+ if (tcp_con->connection == nullptr) {
kill_tcp_relay_connection(tcp_c, tcp_connections_number);
return -1;
}
- for (unsigned int i = 0; i < tcp_c->connections_length; ++i) {
+ for (uint32_t i = 0; i < tcp_c->connections_length; ++i) {
TCP_Connection_to *con_to = get_connection(tcp_c, i);
- if (con_to) {
+ if (con_to != nullptr) {
set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_NONE, 0);
}
}
@@ -829,11 +945,12 @@ static int reconnect_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connec
return 0;
}
+non_null()
static int sleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number)
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
@@ -851,10 +968,10 @@ static int sleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connection
kill_TCP_connection(tcp_con->connection);
tcp_con->connection = nullptr;
- for (unsigned int i = 0; i < tcp_c->connections_length; ++i) {
+ for (uint32_t i = 0; i < tcp_c->connections_length; ++i) {
TCP_Connection_to *con_to = get_connection(tcp_c, i);
- if (con_to) {
+ if (con_to != nullptr) {
set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_NONE, 0);
}
}
@@ -873,11 +990,12 @@ static int sleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connection
return 0;
}
+non_null()
static int unsleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number)
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
@@ -885,10 +1003,11 @@ static int unsleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connecti
return -1;
}
- tcp_con->connection = new_TCP_connection(tcp_c->logger, tcp_c->mono_time, &tcp_con->ip_port, tcp_con->relay_pk,
- tcp_c->self_public_key, tcp_c->self_secret_key, &tcp_c->proxy_info);
+ tcp_con->connection = new_TCP_connection(
+ tcp_c->logger, tcp_c->mono_time, tcp_c->rng, tcp_c->ns, &tcp_con->ip_port,
+ tcp_con->relay_pk, tcp_c->self_public_key, tcp_c->self_secret_key, &tcp_c->proxy_info);
- if (!tcp_con->connection) {
+ if (tcp_con->connection == nullptr) {
kill_tcp_relay_connection(tcp_c, tcp_connections_number);
return -1;
}
@@ -898,20 +1017,22 @@ static int unsleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connecti
tcp_con->connected_time = 0;
tcp_con->status = TCP_CONN_VALID;
tcp_con->unsleep = 0;
+
return 0;
}
-/** Send a TCP routing request.
+/** @brief Send a TCP routing request.
*
* return 0 on success.
* return -1 on failure.
*/
+non_null()
static int send_tcp_relay_routing_request(const TCP_Connections *tcp_c, int tcp_connections_number,
const uint8_t *public_key)
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
@@ -926,19 +1047,20 @@ static int send_tcp_relay_routing_request(const TCP_Connections *tcp_c, int tcp_
return 0;
}
+non_null()
static int tcp_response_callback(void *object, uint8_t connection_id, const uint8_t *public_key)
{
TCP_Client_Connection *tcp_client_con = (TCP_Client_Connection *)object;
const TCP_Connections *tcp_c = (const TCP_Connections *)tcp_con_custom_object(tcp_client_con);
- unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con);
+ const unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con);
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
- int connections_number = find_tcp_connection_to(tcp_c, public_key);
+ const int connections_number = find_tcp_connection_to(tcp_c, public_key);
if (connections_number == -1) {
return -1;
@@ -959,16 +1081,17 @@ static int tcp_response_callback(void *object, uint8_t connection_id, const uint
return 0;
}
+non_null()
static int tcp_status_callback(void *object, uint32_t number, uint8_t connection_id, uint8_t status)
{
const TCP_Client_Connection *tcp_client_con = (const TCP_Client_Connection *)object;
const TCP_Connections *tcp_c = (const TCP_Connections *)tcp_con_custom_object(tcp_client_con);
- unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con);
+ const unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con);
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
TCP_Connection_to *con_to = get_connection(tcp_c, number);
- if (!con_to || !tcp_con) {
+ if (con_to == nullptr || tcp_con == nullptr) {
return -1;
}
@@ -997,6 +1120,7 @@ static int tcp_status_callback(void *object, uint32_t number, uint8_t connection
return 0;
}
+non_null(1, 4) nullable(6)
static int tcp_conn_data_callback(void *object, uint32_t number, uint8_t connection_id, const uint8_t *data,
uint16_t length, void *userdata)
{
@@ -1007,26 +1131,27 @@ static int tcp_conn_data_callback(void *object, uint32_t number, uint8_t connect
const TCP_Client_Connection *tcp_client_con = (TCP_Client_Connection *)object;
TCP_Connections *tcp_c = (TCP_Connections *)tcp_con_custom_object(tcp_client_con);
- unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con);
+ const unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con);
const TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
const TCP_Connection_to *con_to = get_connection(tcp_c, number);
- if (!con_to) {
+ if (con_to == nullptr) {
return -1;
}
- if (tcp_c->tcp_data_callback) {
+ if (tcp_c->tcp_data_callback != nullptr) {
tcp_c->tcp_data_callback(tcp_c->tcp_data_callback_object, con_to->id, data, length, userdata);
}
return 0;
}
+non_null()
static int tcp_conn_oob_callback(void *object, const uint8_t *public_key, const uint8_t *data, uint16_t length,
void *userdata)
{
@@ -1037,50 +1162,62 @@ static int tcp_conn_oob_callback(void *object, const uint8_t *public_key, const
const TCP_Client_Connection *tcp_client_con = (const TCP_Client_Connection *)object;
TCP_Connections *tcp_c = (TCP_Connections *)tcp_con_custom_object(tcp_client_con);
- unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con);
+ const unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con);
const TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
/* TODO(irungentoo): optimize */
- int connections_number = find_tcp_connection_to(tcp_c, public_key);
+ const int connections_number = find_tcp_connection_to(tcp_c, public_key);
const TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
- if (con_to && tcp_connection_in_conn(con_to, tcp_connections_number)) {
+ if (con_to != nullptr && tcp_connection_in_conn(con_to, tcp_connections_number)) {
return tcp_conn_data_callback(object, connections_number, 0, data, length, userdata);
}
- if (tcp_c->tcp_oob_callback) {
+ if (tcp_c->tcp_oob_callback != nullptr) {
tcp_c->tcp_oob_callback(tcp_c->tcp_oob_callback_object, public_key, tcp_connections_number, data, length, userdata);
}
return 0;
}
+non_null()
static int tcp_onion_callback(void *object, const uint8_t *data, uint16_t length, void *userdata)
{
TCP_Connections *tcp_c = (TCP_Connections *)object;
- if (tcp_c->tcp_onion_callback) {
+ if (tcp_c->tcp_onion_callback != nullptr) {
tcp_c->tcp_onion_callback(tcp_c->tcp_onion_callback_object, data, length, userdata);
}
return 0;
}
-/** Set callbacks for the TCP relay connection.
+non_null()
+static void tcp_forwarding_callback(void *object, const uint8_t *data, uint16_t length, void *userdata)
+{
+ TCP_Connections *tcp_c = (TCP_Connections *)object;
+
+ if (tcp_c->tcp_forwarded_response_callback != nullptr) {
+ tcp_c->tcp_forwarded_response_callback(tcp_c->tcp_forwarded_response_callback_object, data, length, userdata);
+ }
+}
+
+/** @brief Set callbacks for the TCP relay connection.
*
* return 0 on success.
* return -1 on failure.
*/
+non_null()
static int tcp_relay_set_callbacks(TCP_Connections *tcp_c, int tcp_connections_number)
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
@@ -1089,6 +1226,7 @@ static int tcp_relay_set_callbacks(TCP_Connections *tcp_c, int tcp_connections_n
tcp_con_set_custom_object(con, tcp_c);
tcp_con_set_custom_uint(con, tcp_connections_number);
onion_response_handler(con, &tcp_onion_callback, tcp_c);
+ forwarding_handler(con, &tcp_forwarding_callback, tcp_c);
routing_response_handler(con, &tcp_response_callback, con);
routing_status_handler(con, &tcp_status_callback, con);
routing_data_handler(con, &tcp_conn_data_callback, con);
@@ -1097,23 +1235,24 @@ static int tcp_relay_set_callbacks(TCP_Connections *tcp_c, int tcp_connections_n
return 0;
}
+non_null()
static int tcp_relay_on_online(TCP_Connections *tcp_c, int tcp_connections_number)
{
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
- unsigned int sent = 0;
+ bool sent_any = false;
- for (unsigned int i = 0; i < tcp_c->connections_length; ++i) {
+ for (uint32_t i = 0; i < tcp_c->connections_length; ++i) {
TCP_Connection_to *con_to = get_connection(tcp_c, i);
- if (con_to) {
+ if (con_to != nullptr) {
if (tcp_connection_in_conn(con_to, tcp_connections_number)) {
if (send_tcp_relay_routing_request(tcp_c, tcp_connections_number, con_to->public_key) == 0) {
- ++sent;
+ sent_any = true;
}
}
}
@@ -1123,7 +1262,7 @@ static int tcp_relay_on_online(TCP_Connections *tcp_c, int tcp_connections_numbe
tcp_con->status = TCP_CONN_CONNECTED;
/* If this connection isn't used by any connection, we don't need to wait for them to come online. */
- if (sent) {
+ if (sent_any) {
tcp_con->connected_time = mono_time_get(tcp_c->mono_time);
} else {
tcp_con->connected_time = 0;
@@ -1137,21 +1276,22 @@ static int tcp_relay_on_online(TCP_Connections *tcp_c, int tcp_connections_numbe
return 0;
}
+non_null()
static int add_tcp_relay_instance(TCP_Connections *tcp_c, const IP_Port *ip_port, const uint8_t *relay_pk)
{
IP_Port ipp_copy = *ip_port;
if (net_family_is_tcp_ipv4(ipp_copy.ip.family)) {
- ipp_copy.ip.family = net_family_ipv4;
+ ipp_copy.ip.family = net_family_ipv4();
} else if (net_family_is_tcp_ipv6(ipp_copy.ip.family)) {
- ipp_copy.ip.family = net_family_ipv6;
+ ipp_copy.ip.family = net_family_ipv6();
}
if (!net_family_is_ipv4(ipp_copy.ip.family) && !net_family_is_ipv6(ipp_copy.ip.family)) {
return -1;
}
- int tcp_connections_number = create_tcp_connection(tcp_c);
+ const int tcp_connections_number = create_tcp_connection(tcp_c);
if (tcp_connections_number == -1) {
return -1;
@@ -1159,10 +1299,11 @@ static int add_tcp_relay_instance(TCP_Connections *tcp_c, const IP_Port *ip_port
TCP_con *tcp_con = &tcp_c->tcp_connections[tcp_connections_number];
- tcp_con->connection = new_TCP_connection(tcp_c->logger, tcp_c->mono_time, &ipp_copy, relay_pk, tcp_c->self_public_key,
- tcp_c->self_secret_key, &tcp_c->proxy_info);
+ tcp_con->connection = new_TCP_connection(
+ tcp_c->logger, tcp_c->mono_time, tcp_c->rng, tcp_c->ns, &ipp_copy,
+ relay_pk, tcp_c->self_public_key, tcp_c->self_secret_key, &tcp_c->proxy_info);
- if (!tcp_con->connection) {
+ if (tcp_con->connection == nullptr) {
return -1;
}
@@ -1171,14 +1312,14 @@ static int add_tcp_relay_instance(TCP_Connections *tcp_c, const IP_Port *ip_port
return tcp_connections_number;
}
-/** Add a TCP relay to the TCP_Connections instance.
+/** @brief Add a TCP relay to the TCP_Connections instance.
*
* return 0 on success.
* return -1 on failure.
*/
int add_tcp_relay_global(TCP_Connections *tcp_c, const IP_Port *ip_port, const uint8_t *relay_pk)
{
- int tcp_connections_number = find_tcp_connection_relay(tcp_c, relay_pk);
+ const int tcp_connections_number = find_tcp_connection_relay(tcp_c, relay_pk);
if (tcp_connections_number != -1) {
return -1;
@@ -1191,7 +1332,7 @@ int add_tcp_relay_global(TCP_Connections *tcp_c, const IP_Port *ip_port, const u
return 0;
}
-/** Add a TCP relay tied to a connection.
+/** @brief Add a TCP relay tied to a connection.
*
* NOTE: This can only be used during the tcp_oob_callback.
*
@@ -1203,13 +1344,13 @@ int add_tcp_number_relay_connection(const TCP_Connections *tcp_c, int connection
{
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
- if (!con_to) {
+ if (con_to == nullptr) {
return -1;
}
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
@@ -1230,7 +1371,7 @@ int add_tcp_number_relay_connection(const TCP_Connections *tcp_c, int connection
return 0;
}
-/** Add a TCP relay tied to a connection.
+/** @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.
*
@@ -1242,7 +1383,7 @@ int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, con
{
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
- if (!con_to) {
+ if (con_to == nullptr) {
return -1;
}
@@ -1260,7 +1401,7 @@ int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, con
const TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
- if (!tcp_con) {
+ if (tcp_con == nullptr) {
return -1;
}
@@ -1271,21 +1412,55 @@ int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, con
return 0;
}
-/** return number of online tcp relays tied to the connection on success.
- * return 0 on failure.
+/**
+ * @return number of online tcp relays tied to the connection on success.
+ * @retval 0 on failure.
*/
-unsigned int tcp_connection_to_online_tcp_relays(const TCP_Connections *tcp_c, int connections_number)
+uint32_t tcp_connection_to_online_tcp_relays(const TCP_Connections *tcp_c, int connections_number)
{
const TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
- if (!con_to) {
+ if (con_to == nullptr) {
return 0;
}
return online_tcp_connection_from_conn(con_to);
}
-/** Copy a maximum of max_num TCP relays we are connected to to tcp_relays.
+/** @brief Copies the tcp relay from tcp connections designated by `idx` to `tcp_relay`.
+ *
+ * Returns true if the relay was successfully copied.
+ * Returns false if the connection index is invalid, or if the relay is not connected.
+ */
+non_null()
+static bool copy_tcp_relay_conn(const TCP_Connections *tcp_c, Node_format *tcp_relay, uint16_t idx)
+{
+ const TCP_con *tcp_con = get_tcp_connection(tcp_c, idx);
+
+ if (tcp_con == nullptr) {
+ return false;
+ }
+
+ if (tcp_con->status != TCP_CONN_CONNECTED) {
+ return false;
+ }
+
+ memcpy(tcp_relay->public_key, tcp_con_public_key(tcp_con->connection), CRYPTO_PUBLIC_KEY_SIZE);
+ tcp_relay->ip_port = tcp_con_ip_port(tcp_con->connection);
+
+ Family *const family = &tcp_relay->ip_port.ip.family;
+
+ if (net_family_is_ipv4(*family)) {
+ *family = net_family_tcp_ipv4();
+ } else if (net_family_is_ipv6(*family)) {
+ *family = net_family_tcp_ipv6();
+ }
+
+ return true;
+}
+
+/** @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.
@@ -1293,28 +1468,34 @@ unsigned int tcp_connection_to_online_tcp_relays(const TCP_Connections *tcp_c, i
*/
uint32_t tcp_copy_connected_relays(const TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num)
{
- const uint32_t r = random_u32();
+ const uint32_t r = random_u32(tcp_c->rng);
uint32_t copied = 0;
for (uint32_t i = 0; (i < tcp_c->tcp_connections_length) && (copied < max_num); ++i) {
- const TCP_con *tcp_con = get_tcp_connection(tcp_c, (i + r) % tcp_c->tcp_connections_length);
+ const uint16_t idx = (i + r) % tcp_c->tcp_connections_length;
- if (!tcp_con) {
- continue;
+ if (copy_tcp_relay_conn(tcp_c, &tcp_relays[copied], idx)) {
+ ++copied;
}
+ }
- if (tcp_con->status == TCP_CONN_CONNECTED) {
- memcpy(tcp_relays[copied].public_key, tcp_con_public_key(tcp_con->connection), CRYPTO_PUBLIC_KEY_SIZE);
- tcp_relays[copied].ip_port = tcp_con_ip_port(tcp_con->connection);
+ return copied;
+}
- Family *const family = &tcp_relays[copied].ip_port.ip.family;
+uint32_t tcp_copy_connected_relays_index(const TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num,
+ uint32_t idx)
+{
+ if (tcp_c->tcp_connections_length == 0) {
+ return 0;
+ }
- if (net_family_is_ipv4(*family)) {
- *family = net_family_tcp_ipv4;
- } else if (net_family_is_ipv6(*family)) {
- *family = net_family_tcp_ipv6;
- }
+ uint32_t copied = 0;
+ const uint16_t num_to_copy = min_u16(max_num, tcp_c->tcp_connections_length);
+ const uint16_t start = idx % tcp_c->tcp_connections_length;
+ const uint16_t end = (start + num_to_copy) % tcp_c->tcp_connections_length;
+ for (uint16_t i = start; i != end; i = (i + 1) % tcp_c->tcp_connections_length) {
+ if (copy_tcp_relay_conn(tcp_c, &tcp_relays[copied], i)) {
++copied;
}
}
@@ -1322,7 +1503,7 @@ uint32_t tcp_copy_connected_relays(const TCP_Connections *tcp_c, Node_format *tc
return copied;
}
-/** Set if we want TCP_connection to allocate some connection for onion use.
+/** @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.
*
@@ -1339,7 +1520,7 @@ int set_tcp_onion_status(TCP_Connections *tcp_c, bool status)
for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) {
TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
- if (tcp_con) {
+ if (tcp_con != nullptr) {
if (tcp_con->status == TCP_CONN_CONNECTED && !tcp_con->onion) {
++tcp_c->onion_num_conns;
tcp_con->onion = 1;
@@ -1352,18 +1533,18 @@ int set_tcp_onion_status(TCP_Connections *tcp_c, bool status)
}
if (tcp_c->onion_num_conns < NUM_ONION_TCP_CONNECTIONS) {
- unsigned int wakeup = NUM_ONION_TCP_CONNECTIONS - tcp_c->onion_num_conns;
+ const unsigned int wakeup = NUM_ONION_TCP_CONNECTIONS - tcp_c->onion_num_conns;
for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) {
TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
- if (tcp_con) {
+ if (tcp_con != nullptr) {
if (tcp_con->status == TCP_CONN_SLEEPING) {
tcp_con->unsleep = 1;
}
}
- if (!wakeup) {
+ if (wakeup == 0) {
break;
}
}
@@ -1374,7 +1555,7 @@ int set_tcp_onion_status(TCP_Connections *tcp_c, bool status)
for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) {
TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
- if (tcp_con) {
+ if (tcp_con != nullptr) {
if (tcp_con->onion) {
--tcp_c->onion_num_conns;
tcp_con->onion = 0;
@@ -1388,15 +1569,16 @@ int set_tcp_onion_status(TCP_Connections *tcp_c, bool status)
return 0;
}
-/** Returns a new TCP_Connections object associated with the secret_key.
+/** @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
+ * 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.
*/
-TCP_Connections *new_tcp_connections(const Logger *logger, Mono_Time *mono_time, const uint8_t *secret_key,
- const TCP_Proxy_Info *proxy_info)
+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)
{
if (secret_key == nullptr) {
return nullptr;
@@ -1409,7 +1591,9 @@ TCP_Connections *new_tcp_connections(const Logger *logger, Mono_Time *mono_time,
}
temp->logger = logger;
+ temp->rng = rng;
temp->mono_time = mono_time;
+ temp->ns = ns;
memcpy(temp->self_secret_key, secret_key, CRYPTO_SECRET_KEY_SIZE);
crypto_derive_public_key(temp->self_public_key, temp->self_secret_key);
@@ -1418,6 +1602,7 @@ TCP_Connections *new_tcp_connections(const Logger *logger, Mono_Time *mono_time,
return temp;
}
+non_null(1, 2) nullable(3)
static void do_tcp_conns(const Logger *logger, TCP_Connections *tcp_c, void *userdata)
{
for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) {
@@ -1451,7 +1636,7 @@ static void do_tcp_conns(const Logger *logger, TCP_Connections *tcp_c, void *use
}
if (tcp_con->status == TCP_CONN_CONNECTED
- && !tcp_con->onion && tcp_con->lock_count
+ && !tcp_con->onion && tcp_con->lock_count > 0
&& tcp_con->lock_count == tcp_con->sleep_count
&& mono_time_is_timeout(tcp_c->mono_time, tcp_con->connected_time, TCP_CONNECTION_ANNOUNCE_TIMEOUT)) {
sleep_tcp_relay_connection(tcp_c, i);
@@ -1464,6 +1649,7 @@ static void do_tcp_conns(const Logger *logger, TCP_Connections *tcp_c, void *use
}
}
+non_null()
static void kill_nonused_tcp(TCP_Connections *tcp_c)
{
if (tcp_c->tcp_connections_length <= RECOMMENDED_FRIEND_TCP_CONNECTIONS) {
@@ -1487,7 +1673,7 @@ static void kill_nonused_tcp(TCP_Connections *tcp_c)
}
if (tcp_con->status == TCP_CONN_CONNECTED) {
- if (tcp_con->onion || tcp_con->lock_count) { // connection is in use so we skip it
+ if (tcp_con->onion || tcp_con->lock_count > 0) { // connection is in use so we skip it
continue;
}
@@ -1507,6 +1693,10 @@ void do_tcp_connections(const Logger *logger, TCP_Connections *tcp_c, void *user
void kill_tcp_connections(TCP_Connections *tcp_c)
{
+ if (tcp_c == nullptr) {
+ return;
+ }
+
for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) {
kill_TCP_connection(tcp_c->tcp_connections[i].connection);
}
diff --git a/protocols/Tox/libtox/src/toxcore/TCP_connection.h b/protocols/Tox/libtox/src/toxcore/TCP_connection.h
index 84890b98d7..65ead4d87f 100644
--- a/protocols/Tox/libtox/src/toxcore/TCP_connection.h
+++ b/protocols/Tox/libtox/src/toxcore/TCP_connection.h
@@ -9,12 +9,12 @@
#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"
-#include <stdbool.h>
-
#define TCP_CONN_NONE 0
#define TCP_CONN_VALID 1
@@ -33,8 +33,9 @@
/** Time until connection to friend gets killed (if it doesn't get locked within that time) */
#define TCP_CONNECTION_ANNOUNCE_TIMEOUT TCP_CONNECTION_TIMEOUT
-/** The amount of recommended connections for each friend
- * NOTE: Must be at most (MAX_FRIEND_TCP_CONNECTIONS / 2) */
+/** @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. */
@@ -71,22 +72,26 @@ typedef struct 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);
/** Returns the number of connected TCP relays */
+non_null()
uint32_t tcp_connected_relays_count(const TCP_Connections *tcp_c);
-/** Send a packet to the TCP connection.
+/** @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);
-/** Return a random TCP connection number for use in send_tcp_onion_request.
+/** @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.
@@ -94,72 +99,123 @@ int send_packet_tcp_connection(const TCP_Connections *tcp_c, int connections_num
* return TCP connection number on success.
* return -1 on failure.
*/
+non_null()
int get_random_tcp_onion_conn_number(const TCP_Connections *tcp_c);
-/** Send an onion packet via the TCP relay corresponding to tcp_connections_number.
+/** @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);
-/** Set if we want TCP_connection to allocate some connection for onion use.
+/** @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 an oob packet via the TCP relay corresponding to tcp_connections_number.
+/**
+ * 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);
-/** Set the callback for TCP data packets.
- */
+/** @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);
-/** Set the callback for TCP onion packets.
- */
+/** @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);
-/** Set the callback for TCP oob data packets.
- */
+/** @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);
-/** Create a new TCP connection to public_key.
+/** @brief Encode tcp_connections_number as a custom ip_port.
*
- * public_key must be the counterpart to the secret key that the other peer used with new_tcp_connections().
+ * 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);
-/** return 0 on success.
- * return -1 on failure.
+/**
+ * @retval 0 on success.
+ * @retval -1 on failure.
*/
+non_null()
int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number);
-/** Set connection status.
+/** @brief Set connection status.
*
* status of 1 means we are using the connection.
* status of 0 means we are not using it.
@@ -169,61 +225,86 @@ int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number);
* 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.
- * return 0 on failure.
+/**
+ * @return number of online tcp relays tied to the connection on success.
+ * @retval 0 on failure.
*/
-unsigned int tcp_connection_to_online_tcp_relays(const TCP_Connections *tcp_c, int connections_number);
+non_null()
+uint32_t tcp_connection_to_online_tcp_relays(const TCP_Connections *tcp_c, int connections_number);
-/** Add a TCP relay tied to a connection.
+/** @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);
-/** Add a TCP relay tied to a connection.
+/** @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);
-/** Add a TCP relay to the TCP_Connections instance.
+/** @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);
-/** Copy a maximum of max_num TCP relays we are connected to to tcp_relays.
+/** @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);
-/** Returns a new TCP_Connections object associated with the secret_key.
+/** @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
+ * 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.
*/
-TCP_Connections *new_tcp_connections(const Logger *logger, Mono_Time *mono_time, const uint8_t *secret_key,
- const TCP_Proxy_Info *proxy_info);
+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
diff --git a/protocols/Tox/libtox/src/toxcore/TCP_server.c b/protocols/Tox/libtox/src/toxcore/TCP_server.c
index 4f7fe12121..f571a312d2 100644
--- a/protocols/Tox/libtox/src/toxcore/TCP_server.c
+++ b/protocols/Tox/libtox/src/toxcore/TCP_server.c
@@ -20,6 +20,7 @@
#endif
#include "TCP_common.h"
+#include "ccompat.h"
#include "list.h"
#include "mono_time.h"
#include "util.h"
@@ -57,7 +58,10 @@ typedef struct TCP_Secure_Connection {
struct TCP_Server {
const Logger *logger;
+ const Random *rng;
+ const Network *ns;
Onion *onion;
+ Forwarding *forwarding;
#ifdef TCP_SERVER_USE_EPOLL
int efd;
@@ -92,19 +96,19 @@ size_t tcp_server_listen_count(const TCP_Server *tcp_server)
return tcp_server->num_listening_socks;
}
-/** This is needed to compile on Android below API 21
- */
+/** This is needed to compile on Android below API 21 */
#ifdef TCP_SERVER_USE_EPOLL
#ifndef EPOLLRDHUP
#define EPOLLRDHUP 0x2000
#endif
#endif
-/** Increase the size of the connection list
+/** @brief Increase the size of the connection list
*
- * return -1 on failure
- * return 0 on success.
+ * @retval -1 on failure
+ * @retval 0 on success.
*/
+non_null()
static int alloc_new_connections(TCP_Server *tcp_server, uint32_t num)
{
const uint32_t new_size = tcp_server->size_accepted_connections + num;
@@ -130,20 +134,23 @@ static int alloc_new_connections(TCP_Server *tcp_server, uint32_t num)
return 0;
}
+non_null()
static void wipe_secure_connection(TCP_Secure_Connection *con)
{
- if (con->status) {
+ if (con->status != 0) {
wipe_priority_list(con->con.priority_queue_start);
crypto_memzero(con, sizeof(TCP_Secure_Connection));
}
}
+non_null()
static void move_secure_connection(TCP_Secure_Connection *con_new, TCP_Secure_Connection *con_old)
{
*con_new = *con_old;
crypto_memzero(con_old, sizeof(TCP_Secure_Connection));
}
+non_null()
static void free_accepted_connection_array(TCP_Server *tcp_server)
{
if (tcp_server->accepted_connection_array == nullptr) {
@@ -159,22 +166,26 @@ static void free_accepted_connection_array(TCP_Server *tcp_server)
tcp_server->size_accepted_connections = 0;
}
-/** return index corresponding to connection with peer on success
- * return -1 on failure.
+/**
+ * @return index corresponding to connection with peer on success
+ * @retval -1 on failure.
*/
+non_null()
static int get_TCP_connection_index(const TCP_Server *tcp_server, const uint8_t *public_key)
{
return bs_list_find(&tcp_server->accepted_key_list, public_key);
}
+non_null()
static int kill_accepted(TCP_Server *tcp_server, int index);
-/** Add accepted TCP connection to the list.
+/** @brief Add accepted TCP connection to the list.
*
- * return index on success
- * return -1 on failure
+ * @return index on success
+ * @retval -1 on failure
*/
+non_null()
static int add_accepted(TCP_Server *tcp_server, const Mono_Time *mono_time, TCP_Secure_Connection *con)
{
int index = get_TCP_connection_index(tcp_server, con->public_key);
@@ -219,11 +230,12 @@ static int add_accepted(TCP_Server *tcp_server, const Mono_Time *mono_time, TCP_
return index;
}
-/** Delete accepted connection from list.
+/** @brief Delete accepted connection from list.
*
- * return 0 on success
- * return -1 on failure
+ * @retval 0 on success
+ * @retval -1 on failure
*/
+non_null()
static int del_accepted(TCP_Server *tcp_server, int index)
{
if ((uint32_t)index >= tcp_server->size_accepted_connections) {
@@ -248,17 +260,18 @@ static int del_accepted(TCP_Server *tcp_server, int index)
return 0;
}
-/** Kill a TCP_Secure_Connection
- */
+/** Kill a TCP_Secure_Connection */
+non_null()
static void kill_TCP_secure_connection(TCP_Secure_Connection *con)
{
- kill_sock(con->con.sock);
+ kill_sock(con->con.ns, con->con.sock);
wipe_secure_connection(con);
}
+non_null()
static int rm_connection_index(TCP_Server *tcp_server, TCP_Secure_Connection *con, uint8_t con_number);
-/** Kill an accepted TCP_Secure_Connection
+/** @brief Kill an accepted TCP_Secure_Connection
*
* return -1 on failure.
* return 0 on success.
@@ -273,29 +286,31 @@ static int kill_accepted(TCP_Server *tcp_server, int index)
rm_connection_index(tcp_server, &tcp_server->accepted_connection_array[index], i);
}
- Socket sock = tcp_server->accepted_connection_array[index].con.sock;
+ const Socket sock = tcp_server->accepted_connection_array[index].con.sock;
if (del_accepted(tcp_server, index) != 0) {
return -1;
}
- kill_sock(sock);
+ kill_sock(tcp_server->ns, sock);
return 0;
}
-/** return 1 if everything went well.
- * return -1 if the connection must be killed.
+/**
+ * @retval 1 if everything went well.
+ * @retval -1 if the connection must be killed.
*/
+non_null()
static int handle_TCP_handshake(const Logger *logger, TCP_Secure_Connection *con, const uint8_t *data, uint16_t length,
const uint8_t *self_secret_key)
{
if (length != TCP_CLIENT_HANDSHAKE_SIZE) {
- LOGGER_WARNING(logger, "invalid handshake length: %d != %d", length, TCP_CLIENT_HANDSHAKE_SIZE);
+ LOGGER_ERROR(logger, "invalid handshake length: %d != %d", length, TCP_CLIENT_HANDSHAKE_SIZE);
return -1;
}
if (con->status != TCP_STATUS_CONNECTED) {
- LOGGER_WARNING(logger, "TCP connection %u not connected", (unsigned int)con->identifier);
+ LOGGER_ERROR(logger, "TCP connection %u not connected", (unsigned int)con->identifier);
return -1;
}
@@ -306,7 +321,7 @@ static int handle_TCP_handshake(const Logger *logger, TCP_Secure_Connection *con
data + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, TCP_HANDSHAKE_PLAIN_SIZE + CRYPTO_MAC_SIZE, plain);
if (len != TCP_HANDSHAKE_PLAIN_SIZE) {
- LOGGER_WARNING(logger, "invalid TCP handshake decrypted length: %d != %d", len, TCP_HANDSHAKE_PLAIN_SIZE);
+ LOGGER_ERROR(logger, "invalid TCP handshake decrypted length: %d != %d", len, TCP_HANDSHAKE_PLAIN_SIZE);
crypto_memzero(shared_key, sizeof(shared_key));
return -1;
}
@@ -314,13 +329,13 @@ static int handle_TCP_handshake(const Logger *logger, TCP_Secure_Connection *con
memcpy(con->public_key, data, CRYPTO_PUBLIC_KEY_SIZE);
uint8_t temp_secret_key[CRYPTO_SECRET_KEY_SIZE];
uint8_t resp_plain[TCP_HANDSHAKE_PLAIN_SIZE];
- crypto_new_keypair(resp_plain, temp_secret_key);
- random_nonce(con->con.sent_nonce);
+ crypto_new_keypair(con->con.rng, resp_plain, temp_secret_key);
+ random_nonce(con->con.rng, con->con.sent_nonce);
memcpy(resp_plain + CRYPTO_PUBLIC_KEY_SIZE, con->con.sent_nonce, CRYPTO_NONCE_SIZE);
memcpy(con->recv_nonce, plain + CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_NONCE_SIZE);
uint8_t response[TCP_SERVER_HANDSHAKE_SIZE];
- random_nonce(response);
+ random_nonce(con->con.rng, response);
len = encrypt_data_symmetric(shared_key, response, resp_plain, TCP_HANDSHAKE_PLAIN_SIZE,
response + CRYPTO_NONCE_SIZE);
@@ -330,9 +345,9 @@ static int handle_TCP_handshake(const Logger *logger, TCP_Secure_Connection *con
return -1;
}
- IP_Port ipp = {0};
+ IP_Port ipp = {{{0}}};
- if (TCP_SERVER_HANDSHAKE_SIZE != net_send(logger, con->con.sock, response, TCP_SERVER_HANDSHAKE_SIZE, &ipp)) {
+ if (TCP_SERVER_HANDSHAKE_SIZE != net_send(con->con.ns, logger, con->con.sock, response, TCP_SERVER_HANDSHAKE_SIZE, &ipp)) {
crypto_memzero(shared_key, sizeof(shared_key));
return -1;
}
@@ -345,14 +360,16 @@ static int handle_TCP_handshake(const Logger *logger, TCP_Secure_Connection *con
return 1;
}
-/** return 1 if connection handshake was handled correctly.
- * return 0 if we didn't get it yet.
- * return -1 if the connection must be killed.
+/**
+ * @retval 1 if connection handshake was handled correctly.
+ * @retval 0 if we didn't get it yet.
+ * @retval -1 if the connection must be killed.
*/
+non_null()
static int read_connection_handshake(const Logger *logger, TCP_Secure_Connection *con, const uint8_t *self_secret_key)
{
uint8_t data[TCP_CLIENT_HANDSHAKE_SIZE];
- const int len = read_TCP_packet(logger, con->con.sock, data, TCP_CLIENT_HANDSHAKE_SIZE, &con->con.ip_port);
+ const int len = read_TCP_packet(logger, con->con.ns, con->con.sock, data, TCP_CLIENT_HANDSHAKE_SIZE, &con->con.ip_port);
if (len == -1) {
LOGGER_TRACE(logger, "connection handshake is not ready yet");
@@ -362,10 +379,12 @@ static int read_connection_handshake(const Logger *logger, TCP_Secure_Connection
return handle_TCP_handshake(logger, con, data, len, self_secret_key);
}
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure (connection must be killed).
*/
+non_null()
static int send_routing_response(const Logger *logger, TCP_Secure_Connection *con, uint8_t rpid,
const uint8_t *public_key)
{
@@ -374,39 +393,45 @@ static int send_routing_response(const Logger *logger, TCP_Secure_Connection *co
data[1] = rpid;
memcpy(data + 2, public_key, CRYPTO_PUBLIC_KEY_SIZE);
- return write_packet_TCP_secure_connection(logger, &con->con, data, sizeof(data), 1);
+ return write_packet_TCP_secure_connection(logger, &con->con, data, sizeof(data), true);
}
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure (connection must be killed).
*/
+non_null()
static int send_connect_notification(const Logger *logger, TCP_Secure_Connection *con, uint8_t id)
{
uint8_t data[2] = {TCP_PACKET_CONNECTION_NOTIFICATION, (uint8_t)(id + NUM_RESERVED_PORTS)};
- return write_packet_TCP_secure_connection(logger, &con->con, data, sizeof(data), 1);
+ return write_packet_TCP_secure_connection(logger, &con->con, data, sizeof(data), true);
}
-/** return 1 on success.
- * return 0 if could not send packet.
- * return -1 on failure (connection must be killed).
+/**
+ * @retval 1 on success.
+ * @retval 0 if could not send packet.
+ * @retval -1 on failure (connection must be killed).
*/
+non_null()
static int send_disconnect_notification(const Logger *logger, TCP_Secure_Connection *con, uint8_t id)
{
uint8_t data[2] = {TCP_PACKET_DISCONNECT_NOTIFICATION, (uint8_t)(id + NUM_RESERVED_PORTS)};
- return write_packet_TCP_secure_connection(logger, &con->con, data, sizeof(data), 1);
+ return write_packet_TCP_secure_connection(logger, &con->con, data, sizeof(data), true);
}
-/** return 0 on success.
- * return -1 on failure (connection must be killed).
+/**
+ * @retval 0 on success.
+ * @retval -1 on failure (connection must be killed).
*/
+non_null()
static int handle_TCP_routing_req(TCP_Server *tcp_server, uint32_t con_id, const uint8_t *public_key)
{
uint32_t index = -1;
TCP_Secure_Connection *con = &tcp_server->accepted_connection_array[con_id];
/* If person tries to cennect to himself we deny the request*/
- if (public_key_cmp(con->public_key, public_key) == 0) {
+ if (pk_equal(con->public_key, public_key)) {
if (send_routing_response(tcp_server->logger, con, 0, public_key) == -1) {
return -1;
}
@@ -416,7 +441,7 @@ static int handle_TCP_routing_req(TCP_Server *tcp_server, uint32_t con_id, const
for (uint32_t i = 0; i < NUM_CLIENT_CONNECTIONS; ++i) {
if (con->connections[i].status != 0) {
- if (public_key_cmp(public_key, con->connections[i].public_key) == 0) {
+ if (pk_equal(public_key, con->connections[i].public_key)) {
if (send_routing_response(tcp_server->logger, con, i + NUM_RESERVED_PORTS, public_key) == -1) {
return -1;
}
@@ -436,7 +461,7 @@ static int handle_TCP_routing_req(TCP_Server *tcp_server, uint32_t con_id, const
return 0;
}
- int ret = send_routing_response(tcp_server->logger, con, index + NUM_RESERVED_PORTS, public_key);
+ const int ret = send_routing_response(tcp_server->logger, con, index + NUM_RESERVED_PORTS, public_key);
if (ret == 0) {
return 0;
@@ -448,7 +473,7 @@ static int handle_TCP_routing_req(TCP_Server *tcp_server, uint32_t con_id, const
con->connections[index].status = 1;
memcpy(con->connections[index].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
- int other_index = get_TCP_connection_index(tcp_server, public_key);
+ const int other_index = get_TCP_connection_index(tcp_server, public_key);
if (other_index != -1) {
uint32_t other_id = -1;
@@ -456,7 +481,7 @@ static int handle_TCP_routing_req(TCP_Server *tcp_server, uint32_t con_id, const
for (uint32_t i = 0; i < NUM_CLIENT_CONNECTIONS; ++i) {
if (other_conn->connections[i].status == 1
- && public_key_cmp(other_conn->connections[i].public_key, con->public_key) == 0) {
+ && pk_equal(other_conn->connections[i].public_key, con->public_key)) {
other_id = i;
break;
}
@@ -478,9 +503,11 @@ static int handle_TCP_routing_req(TCP_Server *tcp_server, uint32_t con_id, const
return 0;
}
-/** return 0 on success.
- * return -1 on failure (connection must be killed).
+/**
+ * @retval 0 on success.
+ * @retval -1 on failure (connection must be killed).
*/
+non_null()
static int handle_TCP_oob_send(TCP_Server *tcp_server, uint32_t con_id, const uint8_t *public_key, const uint8_t *data,
uint16_t length)
{
@@ -490,7 +517,7 @@ static int handle_TCP_oob_send(TCP_Server *tcp_server, uint32_t con_id, const ui
const TCP_Secure_Connection *con = &tcp_server->accepted_connection_array[con_id];
- int other_index = get_TCP_connection_index(tcp_server, public_key);
+ const int other_index = get_TCP_connection_index(tcp_server, public_key);
if (other_index != -1) {
VLA(uint8_t, resp_packet, 1 + CRYPTO_PUBLIC_KEY_SIZE + length);
@@ -498,13 +525,13 @@ static int handle_TCP_oob_send(TCP_Server *tcp_server, uint32_t con_id, const ui
memcpy(resp_packet + 1, con->public_key, CRYPTO_PUBLIC_KEY_SIZE);
memcpy(resp_packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, data, length);
write_packet_TCP_secure_connection(tcp_server->logger, &tcp_server->accepted_connection_array[other_index].con,
- resp_packet, SIZEOF_VLA(resp_packet), 0);
+ resp_packet, SIZEOF_VLA(resp_packet), false);
}
return 0;
}
-/** Remove connection with con_number from the connections array of con.
+/** @brief Remove connection with con_number from the connections array of con.
*
* return -1 on failure.
* return 0 on success.
@@ -515,7 +542,7 @@ static int rm_connection_index(TCP_Server *tcp_server, TCP_Secure_Connection *co
return -1;
}
- if (con->connections[con_number].status) {
+ if (con->connections[con_number].status != 0) {
if (con->connections[con_number].status == 2) {
const uint32_t index = con->connections[con_number].index;
const uint8_t other_id = con->connections[con_number].other_id;
@@ -540,35 +567,99 @@ static int rm_connection_index(TCP_Server *tcp_server, TCP_Secure_Connection *co
return -1;
}
+/** @brief Encode con_id and identifier as a custom IP_Port.
+ *
+ * @return ip_port.
+ */
+static IP_Port con_id_to_ip_port(uint32_t con_id, uint64_t identifier)
+{
+ IP_Port ip_port = {{{0}}};
+ ip_port.ip.family = net_family_tcp_client();
+ ip_port.ip.ip.v6.uint32[0] = con_id;
+ ip_port.ip.ip.v6.uint64[1] = identifier;
+ return ip_port;
+
+}
+
+/** @brief Decode ip_port created by con_id_to_ip_port to con_id.
+ *
+ * @retval true on success.
+ * @retval false if ip_port is invalid.
+ */
+non_null()
+static bool ip_port_to_con_id(const TCP_Server *tcp_server, const IP_Port *ip_port, uint32_t *con_id)
+{
+ *con_id = ip_port->ip.ip.v6.uint32[0];
+
+ return net_family_is_tcp_client(ip_port->ip.family) &&
+ *con_id < tcp_server->size_accepted_connections &&
+ tcp_server->accepted_connection_array[*con_id].identifier == ip_port->ip.ip.v6.uint64[1];
+}
+
+non_null()
static int handle_onion_recv_1(void *object, const IP_Port *dest, const uint8_t *data, uint16_t length)
{
TCP_Server *tcp_server = (TCP_Server *)object;
- uint32_t index = dest->ip.ip.v6.uint32[0];
+ uint32_t index;
- if (index >= tcp_server->size_accepted_connections) {
+ if (!ip_port_to_con_id(tcp_server, dest, &index)) {
return 1;
}
TCP_Secure_Connection *con = &tcp_server->accepted_connection_array[index];
- if (con->identifier != dest->ip.ip.v6.uint64[1]) {
- return 1;
- }
-
VLA(uint8_t, packet, 1 + length);
memcpy(packet + 1, data, length);
packet[0] = TCP_PACKET_ONION_RESPONSE;
- if (write_packet_TCP_secure_connection(tcp_server->logger, &con->con, packet, SIZEOF_VLA(packet), 0) != 1) {
+ if (write_packet_TCP_secure_connection(tcp_server->logger, &con->con, packet, SIZEOF_VLA(packet), false) != 1) {
return 1;
}
return 0;
}
-/** return 0 on success
- * return -1 on failure
+non_null()
+static bool handle_forward_reply_tcp(void *object, const uint8_t *sendback_data, uint16_t sendback_data_len,
+ const uint8_t *data, uint16_t length)
+{
+ TCP_Server *tcp_server = (TCP_Server *)object;
+
+ if (sendback_data_len != 1 + sizeof(uint32_t) + sizeof(uint64_t)) {
+ return false;
+ }
+
+ if (*sendback_data != SENDBACK_TCP) {
+ return false;
+ }
+
+ uint32_t con_id;
+ uint64_t identifier;
+ net_unpack_u32(sendback_data + 1, &con_id);
+ net_unpack_u64(sendback_data + 1 + sizeof(uint32_t), &identifier);
+
+ if (con_id >= tcp_server->size_accepted_connections) {
+ return false;
+ }
+
+ TCP_Secure_Connection *con = &tcp_server->accepted_connection_array[con_id];
+
+ if (con->identifier != identifier) {
+ return false;
+ }
+
+ VLA(uint8_t, packet, 1 + length);
+ memcpy(packet + 1, data, length);
+ packet[0] = TCP_PACKET_FORWARDING;
+
+ return write_packet_TCP_secure_connection(tcp_server->logger, &con->con, packet, SIZEOF_VLA(packet), false) == 1;
+}
+
+/**
+ * @retval 0 on success
+ * @retval -1 on failure
*/
+non_null()
static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint8_t *data, uint16_t length)
{
if (length == 0) {
@@ -615,7 +706,7 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint
uint8_t response[1 + sizeof(uint64_t)];
response[0] = TCP_PACKET_PONG;
memcpy(response + 1, data + 1, sizeof(uint64_t));
- write_packet_TCP_secure_connection(tcp_server->logger, &con->con, response, sizeof(response), 1);
+ write_packet_TCP_secure_connection(tcp_server->logger, &con->con, response, sizeof(response), true);
return 0;
}
@@ -629,7 +720,7 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint
uint64_t ping_id;
memcpy(&ping_id, data + 1, sizeof(uint64_t));
- if (ping_id) {
+ if (ping_id != 0) {
if (ping_id == con->ping_id) {
con->ping_id = 0;
}
@@ -654,17 +745,12 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint
case TCP_PACKET_ONION_REQUEST: {
LOGGER_TRACE(tcp_server->logger, "handling onion request for %d", con_id);
- if (tcp_server->onion) {
+ if (tcp_server->onion != nullptr) {
if (length <= 1 + CRYPTO_NONCE_SIZE + ONION_SEND_BASE * 2) {
return -1;
}
- IP_Port source;
- source.port = 0; // dummy initialise
- source.ip.family = net_family_tcp_onion;
- source.ip.ip.v6.uint32[0] = con_id;
- source.ip.ip.v6.uint32[1] = 0;
- source.ip.ip.v6.uint64[1] = con->identifier;
+ IP_Port source = con_id_to_ip_port(con_id, con->identifier);
onion_send_1(tcp_server->onion, data + 1 + CRYPTO_NONCE_SIZE, length - (1 + CRYPTO_NONCE_SIZE), &source,
data + 1);
}
@@ -677,6 +763,39 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint
return -1;
}
+ case TCP_PACKET_FORWARD_REQUEST: {
+ if (tcp_server->forwarding == nullptr) {
+ return -1;
+ }
+
+ const uint16_t sendback_data_len = 1 + sizeof(uint32_t) + sizeof(uint64_t);
+ uint8_t sendback_data[1 + sizeof(uint32_t) + sizeof(uint64_t)];
+ sendback_data[0] = SENDBACK_TCP;
+ net_pack_u32(sendback_data + 1, con_id);
+ net_pack_u64(sendback_data + 1 + sizeof(uint32_t), con->identifier);
+
+ IP_Port dest;
+ const int ipport_length = unpack_ip_port(&dest, data + 1, length - 1, false);
+
+ if (ipport_length == -1) {
+ return -1;
+ }
+
+ const uint8_t *const forward_data = data + (1 + ipport_length);
+ const uint16_t forward_data_len = length - (1 + ipport_length);
+
+ if (forward_data_len > MAX_FORWARD_DATA_SIZE) {
+ return -1;
+ }
+
+ send_forwarding(tcp_server->forwarding, &dest, sendback_data, sendback_data_len, forward_data, forward_data_len);
+ return 0;
+ }
+
+ case TCP_PACKET_FORWARDING: {
+ return -1;
+ }
+
default: {
if (data[0] < NUM_RESERVED_PORTS) {
return -1;
@@ -703,7 +822,7 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint
memcpy(new_data, data, length);
new_data[0] = other_c_id;
const int ret = write_packet_TCP_secure_connection(tcp_server->logger,
- &tcp_server->accepted_connection_array[index].con, new_data, length, 0);
+ &tcp_server->accepted_connection_array[index].con, new_data, length, false);
if (ret == -1) {
return -1;
@@ -717,6 +836,7 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint
}
+non_null()
static int confirm_TCP_connection(TCP_Server *tcp_server, const Mono_Time *mono_time, TCP_Secure_Connection *con,
const uint8_t *data, uint16_t length)
{
@@ -740,22 +860,24 @@ static int confirm_TCP_connection(TCP_Server *tcp_server, const Mono_Time *mono_
return index;
}
-/** return index on success
- * return -1 on failure
+/**
+ * @return index on success
+ * @retval -1 on failure
*/
+non_null()
static int accept_connection(TCP_Server *tcp_server, Socket sock)
{
if (!sock_valid(sock)) {
return -1;
}
- if (!set_socket_nonblock(sock)) {
- kill_sock(sock);
+ if (!set_socket_nonblock(tcp_server->ns, sock)) {
+ kill_sock(tcp_server->ns, sock);
return -1;
}
- if (!set_socket_nosigpipe(sock)) {
- kill_sock(sock);
+ if (!set_socket_nosigpipe(tcp_server->ns, sock)) {
+ kill_sock(tcp_server->ns, sock);
return -1;
}
@@ -769,6 +891,8 @@ static int accept_connection(TCP_Server *tcp_server, Socket sock)
}
conn->status = TCP_STATUS_CONNECTED;
+ conn->con.ns = tcp_server->ns;
+ conn->con.rng = tcp_server->rng;
conn->con.sock = sock;
conn->next_packet_length = 0;
@@ -776,56 +900,70 @@ static int accept_connection(TCP_Server *tcp_server, Socket sock)
return index;
}
-static Socket new_listening_TCP_socket(Family family, uint16_t port)
+non_null()
+static Socket new_listening_TCP_socket(const Logger *logger, const Network *ns, Family family, uint16_t port)
{
- Socket sock = net_socket(family, TOX_SOCK_STREAM, TOX_PROTO_TCP);
+ const Socket sock = net_socket(ns, family, TOX_SOCK_STREAM, TOX_PROTO_TCP);
if (!sock_valid(sock)) {
+ LOGGER_ERROR(logger, "TCP socket creation failed (family = %d)", family.value);
return net_invalid_socket;
}
- int ok = set_socket_nonblock(sock);
+ bool ok = set_socket_nonblock(ns, sock);
if (ok && net_family_is_ipv6(family)) {
- ok = set_socket_dualstack(sock);
+ ok = set_socket_dualstack(ns, sock);
}
if (ok) {
- ok = set_socket_reuseaddr(sock);
+ ok = set_socket_reuseaddr(ns, sock);
}
- ok = ok && bind_to_port(sock, family, port) && (net_listen(sock, TCP_MAX_BACKLOG) == 0);
+ ok = ok && bind_to_port(ns, sock, family, port) && (net_listen(ns, sock, TCP_MAX_BACKLOG) == 0);
if (!ok) {
- kill_sock(sock);
+ char *const error = net_new_strerror(net_error());
+ LOGGER_WARNING(logger, "could not bind to TCP port %d (family = %d): %s",
+ port, family.value, error != nullptr ? error : "(null)");
+ net_kill_strerror(error);
+ kill_sock(ns, sock);
return net_invalid_socket;
}
+ LOGGER_DEBUG(logger, "successfully bound to TCP port %d", port);
return sock;
}
-TCP_Server *new_TCP_server(const Logger *logger, uint8_t ipv6_enabled, uint16_t num_sockets, const uint16_t *ports,
- const uint8_t *secret_key, Onion *onion)
+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)
{
if (num_sockets == 0 || ports == nullptr) {
+ LOGGER_ERROR(logger, "no sockets");
return nullptr;
}
- if (networking_at_startup() != 0) {
+ if (ns == nullptr) {
+ LOGGER_ERROR(logger, "NULL network");
return nullptr;
}
TCP_Server *temp = (TCP_Server *)calloc(1, sizeof(TCP_Server));
if (temp == nullptr) {
+ LOGGER_ERROR(logger, "TCP server allocation failed");
return nullptr;
}
temp->logger = logger;
+ temp->ns = ns;
+ temp->rng = rng;
temp->socks_listening = (Socket *)calloc(num_sockets, sizeof(Socket));
if (temp->socks_listening == nullptr) {
+ LOGGER_ERROR(logger, "socket allocation failed");
free(temp);
return nullptr;
}
@@ -834,6 +972,7 @@ TCP_Server *new_TCP_server(const Logger *logger, uint8_t ipv6_enabled, uint16_t
temp->efd = epoll_create(8);
if (temp->efd == -1) {
+ LOGGER_ERROR(logger, "epoll initialisation failed");
free(temp->socks_listening);
free(temp);
return nullptr;
@@ -841,27 +980,29 @@ TCP_Server *new_TCP_server(const Logger *logger, uint8_t ipv6_enabled, uint16_t
#endif
- const Family family = ipv6_enabled ? net_family_ipv6 : net_family_ipv4;
+ const Family family = ipv6_enabled ? net_family_ipv6() : net_family_ipv4();
for (uint32_t i = 0; i < num_sockets; ++i) {
- Socket sock = new_listening_TCP_socket(family, ports[i]);
+ const Socket sock = new_listening_TCP_socket(logger, ns, family, ports[i]);
+
+ if (!sock_valid(sock)) {
+ continue;
+ }
- if (sock_valid(sock)) {
#ifdef TCP_SERVER_USE_EPOLL
- struct epoll_event ev;
+ struct epoll_event ev;
- ev.events = EPOLLIN | EPOLLET;
- ev.data.u64 = sock.socket | ((uint64_t)TCP_SOCKET_LISTENING << 32);
+ ev.events = EPOLLIN | EPOLLET;
+ ev.data.u64 = sock.sock | ((uint64_t)TCP_SOCKET_LISTENING << 32);
- if (epoll_ctl(temp->efd, EPOLL_CTL_ADD, sock.socket, &ev) == -1) {
- continue;
- }
+ if (epoll_ctl(temp->efd, EPOLL_CTL_ADD, sock.sock, &ev) == -1) {
+ continue;
+ }
#endif
- temp->socks_listening[temp->num_listening_socks] = sock;
- ++temp->num_listening_socks;
- }
+ temp->socks_listening[temp->num_listening_socks] = sock;
+ ++temp->num_listening_socks;
}
if (temp->num_listening_socks == 0) {
@@ -870,11 +1011,16 @@ TCP_Server *new_TCP_server(const Logger *logger, uint8_t ipv6_enabled, uint16_t
return nullptr;
}
- if (onion) {
+ if (onion != nullptr) {
temp->onion = onion;
set_callback_handle_recv_1(onion, &handle_onion_recv_1, temp);
}
+ if (forwarding != nullptr) {
+ temp->forwarding = forwarding;
+ set_callback_forward_reply(forwarding, &handle_forward_reply_tcp, temp);
+ }
+
memcpy(temp->secret_key, secret_key, CRYPTO_SECRET_KEY_SIZE);
crypto_derive_public_key(temp->public_key, temp->secret_key);
@@ -884,18 +1030,20 @@ TCP_Server *new_TCP_server(const Logger *logger, uint8_t ipv6_enabled, uint16_t
}
#ifndef TCP_SERVER_USE_EPOLL
+non_null()
static void do_TCP_accept_new(TCP_Server *tcp_server)
{
for (uint32_t i = 0; i < tcp_server->num_listening_socks; ++i) {
Socket sock;
do {
- sock = net_accept(tcp_server->socks_listening[i]);
+ sock = net_accept(tcp_server->ns, tcp_server->socks_listening[i]);
} while (accept_connection(tcp_server, sock) != -1);
}
}
#endif
+non_null()
static int do_incoming(TCP_Server *tcp_server, uint32_t i)
{
TCP_Secure_Connection *const conn = &tcp_server->incoming_connection_queue[i];
@@ -923,7 +1071,7 @@ static int do_incoming(TCP_Server *tcp_server, uint32_t i)
TCP_Secure_Connection *conn_new = &tcp_server->unconfirmed_connection_queue[index_new];
if (conn_new->status != TCP_STATUS_NO_STATUS) {
- LOGGER_WARNING(tcp_server->logger, "incoming connection %d would overwrite existing", i);
+ LOGGER_ERROR(tcp_server->logger, "incoming connection %d would overwrite existing", i);
kill_TCP_secure_connection(conn_new);
}
@@ -933,6 +1081,7 @@ static int do_incoming(TCP_Server *tcp_server, uint32_t i)
return index_new;
}
+non_null()
static int do_unconfirmed(TCP_Server *tcp_server, const Mono_Time *mono_time, uint32_t i)
{
TCP_Secure_Connection *const conn = &tcp_server->unconfirmed_connection_queue[i];
@@ -944,8 +1093,7 @@ static int do_unconfirmed(TCP_Server *tcp_server, const Mono_Time *mono_time, ui
LOGGER_TRACE(tcp_server->logger, "handling unconfirmed TCP connection %d", i);
uint8_t packet[MAX_PACKET_SIZE];
- const int len = read_packet_TCP_secure_connection(tcp_server->logger, conn->con.sock, &conn->next_packet_length,
- conn->con.shared_key, conn->recv_nonce, packet, sizeof(packet), &conn->con.ip_port);
+ const int len = read_packet_TCP_secure_connection(tcp_server->logger, conn->con.ns, conn->con.sock, &conn->next_packet_length, conn->con.shared_key, conn->recv_nonce, packet, sizeof(packet), &conn->con.ip_port);
if (len == 0) {
return -1;
@@ -959,13 +1107,13 @@ static int do_unconfirmed(TCP_Server *tcp_server, const Mono_Time *mono_time, ui
return confirm_TCP_connection(tcp_server, mono_time, conn, packet, len);
}
+non_null()
static bool tcp_process_secure_packet(TCP_Server *tcp_server, uint32_t i)
{
TCP_Secure_Connection *const conn = &tcp_server->accepted_connection_array[i];
uint8_t packet[MAX_PACKET_SIZE];
- const int len = read_packet_TCP_secure_connection(tcp_server->logger, conn->con.sock, &conn->next_packet_length,
- conn->con.shared_key, conn->recv_nonce, packet, sizeof(packet), &conn->con.ip_port);
+ const int len = read_packet_TCP_secure_connection(tcp_server->logger, conn->con.ns, conn->con.sock, &conn->next_packet_length, conn->con.shared_key, conn->recv_nonce, packet, sizeof(packet), &conn->con.ip_port);
LOGGER_TRACE(tcp_server->logger, "processing packet for %d: %d", i, len);
if (len == 0) {
@@ -986,6 +1134,7 @@ static bool tcp_process_secure_packet(TCP_Server *tcp_server, uint32_t i)
return true;
}
+non_null()
static void do_confirmed_recv(TCP_Server *tcp_server, uint32_t i)
{
while (tcp_process_secure_packet(tcp_server, i)) {
@@ -995,6 +1144,7 @@ static void do_confirmed_recv(TCP_Server *tcp_server, uint32_t i)
}
#ifndef TCP_SERVER_USE_EPOLL
+non_null()
static void do_TCP_incoming(TCP_Server *tcp_server)
{
for (uint32_t i = 0; i < MAX_INCOMING_CONNECTIONS; ++i) {
@@ -1002,6 +1152,7 @@ static void do_TCP_incoming(TCP_Server *tcp_server)
}
}
+non_null()
static void do_TCP_unconfirmed(TCP_Server *tcp_server, const Mono_Time *mono_time)
{
for (uint32_t i = 0; i < MAX_INCOMING_CONNECTIONS; ++i) {
@@ -1010,6 +1161,7 @@ static void do_TCP_unconfirmed(TCP_Server *tcp_server, const Mono_Time *mono_tim
}
#endif
+non_null()
static void do_TCP_confirmed(TCP_Server *tcp_server, const Mono_Time *mono_time)
{
#ifdef TCP_SERVER_USE_EPOLL
@@ -1031,14 +1183,14 @@ static void do_TCP_confirmed(TCP_Server *tcp_server, const Mono_Time *mono_time)
if (mono_time_is_timeout(mono_time, conn->last_pinged, TCP_PING_FREQUENCY)) {
uint8_t ping[1 + sizeof(uint64_t)];
ping[0] = TCP_PACKET_PING;
- uint64_t ping_id = random_u64();
+ uint64_t ping_id = random_u64(conn->con.rng);
- if (!ping_id) {
+ if (ping_id == 0) {
++ping_id;
}
memcpy(ping + 1, &ping_id, sizeof(uint64_t));
- int ret = write_packet_TCP_secure_connection(tcp_server->logger, &conn->con, ping, sizeof(ping), 1);
+ const int ret = write_packet_TCP_secure_connection(tcp_server->logger, &conn->con, ping, sizeof(ping), true);
if (ret == 1) {
conn->last_pinged = mono_time_get(mono_time);
@@ -1051,7 +1203,7 @@ static void do_TCP_confirmed(TCP_Server *tcp_server, const Mono_Time *mono_time)
}
}
- if (conn->ping_id && mono_time_is_timeout(mono_time, conn->last_pinged, TCP_PING_TIMEOUT)) {
+ if (conn->ping_id != 0 && mono_time_is_timeout(mono_time, conn->last_pinged, TCP_PING_TIMEOUT)) {
kill_accepted(tcp_server, i);
continue;
}
@@ -1067,6 +1219,7 @@ static void do_TCP_confirmed(TCP_Server *tcp_server, const Mono_Time *mono_time)
}
#ifdef TCP_SERVER_USE_EPOLL
+non_null()
static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time)
{
#define MAX_EVENTS 16
@@ -1079,7 +1232,7 @@ static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time
const int status = (events[n].data.u64 >> 32) & 0xFF;
const int index = events[n].data.u64 >> 40;
- if ((events[n].events & EPOLLERR) || (events[n].events & EPOLLHUP) || (events[n].events & EPOLLRDHUP)) {
+ if ((events[n].events & EPOLLERR) != 0 || (events[n].events & EPOLLHUP) != 0 || (events[n].events & EPOLLRDHUP) != 0) {
switch (status) {
case TCP_SOCKET_LISTENING: {
// should never happen
@@ -1110,15 +1263,15 @@ static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time
}
- if (!(events[n].events & EPOLLIN)) {
+ if ((events[n].events & EPOLLIN) == 0) {
continue;
}
switch (status) {
case TCP_SOCKET_LISTENING: {
// socket is from socks_listening, accept connection
- while (1) {
- Socket sock_new = net_accept(sock);
+ while (true) {
+ const Socket sock_new = net_accept(tcp_server->ns, sock);
if (!sock_valid(sock_new)) {
break;
@@ -1134,9 +1287,9 @@ static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time
ev.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
- ev.data.u64 = sock_new.socket | ((uint64_t)TCP_SOCKET_INCOMING << 32) | ((uint64_t)index_new << 40);
+ ev.data.u64 = sock_new.sock | ((uint64_t)TCP_SOCKET_INCOMING << 32) | ((uint64_t)index_new << 40);
- if (epoll_ctl(tcp_server->efd, EPOLL_CTL_ADD, sock_new.socket, &ev) == -1) {
+ if (epoll_ctl(tcp_server->efd, EPOLL_CTL_ADD, sock_new.sock, &ev) == -1) {
LOGGER_DEBUG(tcp_server->logger, "new connection %d was dropped due to epoll error %d", index, net_error());
kill_TCP_secure_connection(&tcp_server->incoming_connection_queue[index_new]);
continue;
@@ -1152,9 +1305,9 @@ static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time
if (index_new != -1) {
LOGGER_TRACE(tcp_server->logger, "incoming connection %d was accepted as %d", index, index_new);
events[n].events = EPOLLIN | EPOLLET | EPOLLRDHUP;
- events[n].data.u64 = sock.socket | ((uint64_t)TCP_SOCKET_UNCONFIRMED << 32) | ((uint64_t)index_new << 40);
+ events[n].data.u64 = sock.sock | ((uint64_t)TCP_SOCKET_UNCONFIRMED << 32) | ((uint64_t)index_new << 40);
- if (epoll_ctl(tcp_server->efd, EPOLL_CTL_MOD, sock.socket, &events[n]) == -1) {
+ if (epoll_ctl(tcp_server->efd, EPOLL_CTL_MOD, sock.sock, &events[n]) == -1) {
LOGGER_DEBUG(tcp_server->logger, "incoming connection %d was dropped due to epoll error %d", index, net_error());
kill_TCP_secure_connection(&tcp_server->unconfirmed_connection_queue[index_new]);
break;
@@ -1170,9 +1323,9 @@ static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time
if (index_new != -1) {
LOGGER_TRACE(tcp_server->logger, "unconfirmed connection %d was confirmed as %d", index, index_new);
events[n].events = EPOLLIN | EPOLLET | EPOLLRDHUP;
- events[n].data.u64 = sock.socket | ((uint64_t)TCP_SOCKET_CONFIRMED << 32) | ((uint64_t)index_new << 40);
+ events[n].data.u64 = sock.sock | ((uint64_t)TCP_SOCKET_CONFIRMED << 32) | ((uint64_t)index_new << 40);
- if (epoll_ctl(tcp_server->efd, EPOLL_CTL_MOD, sock.socket, &events[n]) == -1) {
+ if (epoll_ctl(tcp_server->efd, EPOLL_CTL_MOD, sock.sock, &events[n]) == -1) {
// remove from confirmed connections
LOGGER_DEBUG(tcp_server->logger, "unconfirmed connection %d was dropped due to epoll error %d", index, net_error());
kill_accepted(tcp_server, index_new);
@@ -1193,6 +1346,7 @@ static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time
return nfds > 0;
}
+non_null()
static void do_TCP_epoll(TCP_Server *tcp_server, const Mono_Time *mono_time)
{
while (tcp_epoll_process(tcp_server, mono_time)) {
@@ -1218,14 +1372,22 @@ void do_TCP_server(TCP_Server *tcp_server, const Mono_Time *mono_time)
void kill_TCP_server(TCP_Server *tcp_server)
{
+ if (tcp_server == nullptr) {
+ return;
+ }
+
for (uint32_t i = 0; i < tcp_server->num_listening_socks; ++i) {
- kill_sock(tcp_server->socks_listening[i]);
+ kill_sock(tcp_server->ns, tcp_server->socks_listening[i]);
}
- if (tcp_server->onion) {
+ if (tcp_server->onion != nullptr) {
set_callback_handle_recv_1(tcp_server->onion, nullptr, nullptr);
}
+ if (tcp_server->forwarding != nullptr) {
+ set_callback_forward_reply(tcp_server->forwarding, nullptr, nullptr);
+ }
+
bs_list_free(&tcp_server->accepted_key_list);
#ifdef TCP_SERVER_USE_EPOLL
diff --git a/protocols/Tox/libtox/src/toxcore/TCP_server.h b/protocols/Tox/libtox/src/toxcore/TCP_server.h
index 3269ed3b0a..2224938a09 100644
--- a/protocols/Tox/libtox/src/toxcore/TCP_server.h
+++ b/protocols/Tox/libtox/src/toxcore/TCP_server.h
@@ -10,6 +10,7 @@
#define C_TOXCORE_TOXCORE_TCP_SERVER_H
#include "crypto_core.h"
+#include "forwarding.h"
#include "onion.h"
#define MAX_INCOMING_CONNECTIONS 256
@@ -27,20 +28,23 @@ typedef enum 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.
- */
-TCP_Server *new_TCP_server(const Logger *logger, uint8_t ipv6_enabled, uint16_t num_sockets, const uint16_t *ports,
- const uint8_t *secret_key, Onion *onion);
+/** 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
- */
+/** Run the TCP_server */
+non_null()
void do_TCP_server(TCP_Server *tcp_server, const Mono_Time *mono_time);
-/** Kill the TCP server
- */
+/** Kill the TCP server */
+nullable(1)
void kill_TCP_server(TCP_Server *tcp_server);
diff --git a/protocols/Tox/libtox/src/toxcore/announce.c b/protocols/Tox/libtox/src/toxcore/announce.c
new file mode 100644
index 0000000000..7914d19615
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/announce.c
@@ -0,0 +1,689 @@
+/* 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 "timed_auth.h"
+#include "util.h"
+
+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_Keys 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);
+ uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE];
+
+ get_shared_key(announce->mono_time, &announce->shared_keys, shared_key,
+ announce->secret_key, data_public_key);
+
+ 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);
+ uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE];
+
+ dht_get_shared_key_recv(announce->dht, shared_key, 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->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);
+ crypto_memzero(&announce->shared_keys, sizeof(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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/announce.h b/protocols/Tox/libtox/src/toxcore/announce.h
new file mode 100644
index 0000000000..e680da68b6
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/announce.h
@@ -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
diff --git a/protocols/Tox/libtox/src/toxcore/attributes.h b/protocols/Tox/libtox/src/toxcore/attributes.h
new file mode 100644
index 0000000000..3da768f2a3
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/attributes.h
@@ -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
diff --git a/protocols/Tox/libtox/src/toxcore/bin_pack.c b/protocols/Tox/libtox/src/toxcore/bin_pack.c
new file mode 100644
index 0000000000..ca8940ee44
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/bin_pack.c
@@ -0,0 +1,161 @@
+/* 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_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;
+}
diff --git a/protocols/Tox/libtox/src/toxcore/bin_pack.h b/protocols/Tox/libtox/src/toxcore/bin_pack.h
new file mode 100644
index 0000000000..542f533b88
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/bin_pack.h
@@ -0,0 +1,122 @@
+/* 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 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
diff --git a/protocols/Tox/libtox/src/toxcore/bin_unpack.c b/protocols/Tox/libtox/src/toxcore/bin_unpack.c
new file mode 100644
index 0000000000..e4daec3a22
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/bin_unpack.c
@@ -0,0 +1,185 @@
+/* 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_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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/bin_unpack.h b/protocols/Tox/libtox/src/toxcore/bin_unpack.h
new file mode 100644
index 0000000000..c6ead96e1b
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/bin_unpack.h
@@ -0,0 +1,100 @@
+/* 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 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
diff --git a/protocols/Tox/libtox/src/toxcore/ccompat.c b/protocols/Tox/libtox/src/toxcore/ccompat.c
new file mode 100644
index 0000000000..30e689d06d
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/ccompat.c
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022 The TokTok team.
+ */
+#include "ccompat.h"
diff --git a/protocols/Tox/libtox/src/toxcore/ccompat.h b/protocols/Tox/libtox/src/toxcore/ccompat.h
index 295cd10d9b..9ea6739a7d 100644
--- a/protocols/Tox/libtox/src/toxcore/ccompat.h
+++ b/protocols/Tox/libtox/src/toxcore/ccompat.h
@@ -8,9 +8,9 @@
#ifndef C_TOXCORE_TOXCORE_CCOMPAT_H
#define C_TOXCORE_TOXCORE_CCOMPAT_H
-#include <stdbool.h>
+#include <stddef.h> // NULL, size_t
-bool unused_for_tokstyle(void);
+#include "attributes.h"
//!TOKSTYLE-
@@ -25,7 +25,7 @@ bool unused_for_tokstyle(void);
// you may run out of stack space.
#if !defined(DISABLE_VLA) && !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
// C99 VLAs.
-#define VLA(type, name, size) type name[size]
+#define ALLOC_VLA(type, name, size) type name[size]
#define SIZEOF_VLA sizeof
#else
@@ -46,19 +46,35 @@ bool unused_for_tokstyle(void);
#endif
#endif
-#define VLA(type, name, size) \
- const size_t name##_size = (size) * sizeof(type); \
- type *const name = (type *)alloca(name##_size)
-#define SIZEOF_VLA(name) name##_size
+#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
-#define static_assert(cond, msg) extern const int unused_for_static_assert
-#endif
-#endif
+#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)))
diff --git a/protocols/Tox/libtox/src/toxcore/crypto_core.c b/protocols/Tox/libtox/src/toxcore/crypto_core.c
index 3402395b63..1ec2cee5fd 100644
--- a/protocols/Tox/libtox/src/toxcore/crypto_core.c
+++ b/protocols/Tox/libtox/src/toxcore/crypto_core.c
@@ -10,15 +10,15 @@
*/
#include "crypto_core.h"
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
-#include "ccompat.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>
@@ -28,15 +28,17 @@
#include <randombytes.h>
#endif
+#include "ccompat.h"
+
#ifndef crypto_box_MACBYTES
#define crypto_box_MACBYTES (crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES)
#endif
-//!TOKSTYLE-
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-#include "../testing/fuzzing/fuzz_adapter.h"
+#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
-//!TOKSTYLE+
static_assert(CRYPTO_PUBLIC_KEY_SIZE == crypto_box_PUBLICKEYBYTES,
"CRYPTO_PUBLIC_KEY_SIZE should be equal to crypto_box_PUBLICKEYBYTES");
@@ -50,12 +52,68 @@ 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 public_key_cmp to work");
+ "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)
@@ -69,6 +127,7 @@ static uint8_t *crypto_malloc(size_t bytes)
return ptr;
}
+nullable(1)
static void crypto_free(uint8_t *ptr, size_t bytes)
{
if (ptr != nullptr) {
@@ -82,116 +141,154 @@ static void crypto_free(uint8_t *ptr, size_t bytes)
void crypto_memzero(void *data, size_t length)
{
-#ifndef VANILLA_NACL
- sodium_memzero(data, length);
-#else
+#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)
{
-#ifndef VANILLA_NACL
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(VANILLA_NACL)
+ return false;
+#else
if (sodium_mlock(data, length) != 0) {
return false;
}
return true;
-#else
- return false;
#endif
}
bool crypto_memunlock(void *data, size_t length)
{
-#ifndef VANILLA_NACL
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(VANILLA_NACL)
+ return false;
+#else
if (sodium_munlock(data, length) != 0) {
return false;
}
return true;
-#else
- return false;
#endif
}
-int32_t public_key_cmp(const uint8_t *pk1, const uint8_t *pk2)
+bool pk_equal(const uint8_t *pk1, const uint8_t *pk2)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
// Hope that this is better for the fuzzer
- return memcmp(pk1, pk2, CRYPTO_PUBLIC_KEY_SIZE) == 0 ? 0 : -1;
+ return memcmp(pk1, pk2, CRYPTO_PUBLIC_KEY_SIZE) == 0;
#else
- return crypto_verify_32(pk1, pk2);
+ return crypto_verify_32(pk1, pk2) == 0;
#endif
}
-int32_t crypto_sha512_cmp(const uint8_t *cksum1, const uint8_t *cksum2)
+void pk_copy(uint8_t *dest, const uint8_t *src)
{
-#ifndef VANILLA_NACL
- return crypto_verify_64(cksum1, cksum2);
+ 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) && crypto_verify_32(cksum1 + 8, cksum2 + 8);
+ return crypto_verify_32(cksum1, cksum2) == 0;
#endif
}
-uint8_t random_u08(void)
+uint8_t random_u08(const Random *rng)
{
uint8_t randnum;
- random_bytes(&randnum, 1);
+ random_bytes(rng, &randnum, 1);
return randnum;
}
-uint16_t random_u16(void)
+uint16_t random_u16(const Random *rng)
{
uint16_t randnum;
- random_bytes((uint8_t *)&randnum, sizeof(randnum));
+ random_bytes(rng, (uint8_t *)&randnum, sizeof(randnum));
return randnum;
}
-uint32_t random_u32(void)
+uint32_t random_u32(const Random *rng)
{
uint32_t randnum;
- random_bytes((uint8_t *)&randnum, sizeof(randnum));
+ random_bytes(rng, (uint8_t *)&randnum, sizeof(randnum));
return randnum;
}
-uint64_t random_u64(void)
+uint64_t random_u64(const Random *rng)
{
uint64_t randnum;
- random_bytes((uint8_t *)&randnum, sizeof(randnum));
+ random_bytes(rng, (uint8_t *)&randnum, sizeof(randnum));
return randnum;
}
-uint32_t random_range_u32(uint32_t upper_bound)
+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 random_u32() % upper_bound;
+ return false;
#else
- return randombytes_uniform(upper_bound);
-#endif // VANILLA_NACL
+ return crypto_sign_detached(signature, nullptr, message, message_length, secret_key) == 0;
+#endif // VANILLA_NACL
}
-bool public_key_valid(const uint8_t *public_key)
+bool crypto_signature_verify(const uint8_t *signature, const uint8_t *message, uint64_t message_length,
+ const uint8_t *public_key)
{
- if (public_key[31] >= 128) { /* Last bit of key is always zero. */
- return 0;
- }
+#ifdef VANILLA_NACL
+ return false;
+#else
+ return crypto_sign_verify_detached(signature, message, message_length, public_key) == 0;
+#endif
+}
- return 1;
+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 *secret_key, const uint8_t *nonce,
+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 || !secret_key || !nonce || !plain || !encrypted) {
+ if (length == 0 || shared_key == nullptr || nonce == nullptr || plain == nullptr || encrypted == nullptr) {
return -1;
}
@@ -224,7 +321,7 @@ int32_t encrypt_data_symmetric(const uint8_t *secret_key, const uint8_t *nonce,
memcpy(temp_plain + crypto_box_ZEROBYTES, plain, length);
if (crypto_box_afternm(temp_encrypted, temp_plain, length + crypto_box_ZEROBYTES, nonce,
- secret_key) != 0) {
+ shared_key) != 0) {
crypto_free(temp_plain, size_temp_plain);
crypto_free(temp_encrypted, size_temp_encrypted);
return -1;
@@ -236,13 +333,15 @@ int32_t encrypt_data_symmetric(const uint8_t *secret_key, const uint8_t *nonce,
crypto_free(temp_plain, size_temp_plain);
crypto_free(temp_encrypted, size_temp_encrypted);
#endif
- return length + crypto_box_MACBYTES;
+ assert(length < INT32_MAX - crypto_box_MACBYTES);
+ return (int32_t)(length + crypto_box_MACBYTES);
}
-int32_t decrypt_data_symmetric(const uint8_t *secret_key, const uint8_t *nonce,
+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 || !secret_key || !nonce || !encrypted || !plain) {
+ if (length <= crypto_box_BOXZEROBYTES || shared_key == nullptr || nonce == nullptr || encrypted == nullptr
+ || plain == nullptr) {
return -1;
}
@@ -273,7 +372,7 @@ int32_t decrypt_data_symmetric(const uint8_t *secret_key, const uint8_t *nonce,
memcpy(temp_encrypted + crypto_box_BOXZEROBYTES, encrypted, length);
if (crypto_box_open_afternm(temp_plain, temp_encrypted, length + crypto_box_BOXZEROBYTES, nonce,
- secret_key) != 0) {
+ shared_key) != 0) {
crypto_free(temp_plain, size_temp_plain);
crypto_free(temp_encrypted, size_temp_encrypted);
return -1;
@@ -284,19 +383,21 @@ int32_t decrypt_data_symmetric(const uint8_t *secret_key, const uint8_t *nonce,
crypto_free(temp_plain, size_temp_plain);
crypto_free(temp_encrypted, size_temp_encrypted);
#endif
- return length - crypto_box_MACBYTES;
+ 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 || !secret_key) {
+ if (public_key == nullptr || secret_key == nullptr) {
return -1;
}
uint8_t k[crypto_box_BEFORENMBYTES];
encrypt_precompute(public_key, secret_key, k);
- int ret = encrypt_data_symmetric(k, nonce, plain, length, encrypted);
+ const int ret = encrypt_data_symmetric(k, nonce, plain, length, encrypted);
crypto_memzero(k, sizeof(k));
return ret;
}
@@ -304,13 +405,13 @@ int32_t encrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const
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 || !secret_key) {
+ if (public_key == nullptr || secret_key == nullptr) {
return -1;
}
uint8_t k[crypto_box_BEFORENMBYTES];
encrypt_precompute(public_key, secret_key, k);
- int ret = decrypt_data_symmetric(k, nonce, encrypted, length, plain);
+ const int ret = decrypt_data_symmetric(k, nonce, encrypted, length, plain);
crypto_memzero(k, sizeof(k));
return ret;
}
@@ -356,20 +457,20 @@ void increment_nonce_number(uint8_t *nonce, uint32_t increment)
}
}
-void random_nonce(uint8_t *nonce)
+void random_nonce(const Random *rng, uint8_t *nonce)
{
- random_bytes(nonce, crypto_box_NONCEBYTES);
+ random_bytes(rng, nonce, crypto_box_NONCEBYTES);
}
-void new_symmetric_key(uint8_t *key)
+void new_symmetric_key(const Random *rng, uint8_t *key)
{
- random_bytes(key, CRYPTO_SYMMETRIC_KEY_SIZE);
+ random_bytes(rng, key, CRYPTO_SYMMETRIC_KEY_SIZE);
}
-int32_t crypto_new_keypair(uint8_t *public_key, uint8_t *secret_key)
+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(secret_key, CRYPTO_SECRET_KEY_SIZE);
+ random_bytes(rng, secret_key, CRYPTO_SECRET_KEY_SIZE);
memset(public_key, 0, CRYPTO_PUBLIC_KEY_SIZE); // Make MSAN happy
crypto_scalarmult_curve25519_base(public_key, secret_key);
return 0;
@@ -383,21 +484,90 @@ 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)
+{
+ 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)
+{
+ crypto_auth(auth, data, length, key);
+}
+
+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)
+{
+ return crypto_auth_verify(auth, data, length, key) == 0;
+}
+
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
}
-void random_bytes(uint8_t *data, size_t length)
+non_null()
+static void sys_random_bytes(void *obj, uint8_t *bytes, size_t length)
{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- fuzz_random_bytes(data, 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
- randombytes(data, length);
+ 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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/crypto_core.h b/protocols/Tox/libtox/src/toxcore/crypto_core.h
index f3709abaac..0aaadeacf1 100644
--- a/protocols/Tox/libtox/src/toxcore/crypto_core.h
+++ b/protocols/Tox/libtox/src/toxcore/crypto_core.h
@@ -13,11 +13,28 @@
#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
@@ -39,7 +56,7 @@ extern "C" {
/**
* @brief The number of bytes needed for the MAC (message authentication code) in an
- * encrypted message.
+ * encrypted message.
*/
#define CRYPTO_MAC_SIZE 16
@@ -58,6 +75,66 @@ extern "C" {
*/
#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.
*
@@ -65,54 +142,95 @@ extern "C" {
* 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 0 if both mem locations of length are equal
- * @retval -1 if they are not
+ * @retval true if both mem locations of length are equal
+ * @retval false if they are not
*/
-int32_t public_key_cmp(const uint8_t *pk1, const uint8_t *pk2);
+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 0 if both mem locations of length are equal, -1 if they are not.
+ * @return true if both mem locations of length are equal, false if they are not.
*/
-int32_t crypto_sha512_cmp(const uint8_t *cksum1, const uint8_t *cksum2);
+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.
*/
-uint8_t random_u08(void);
+non_null()
+uint8_t random_u08(const Random *rng);
/**
* @brief Return a random 16 bit integer.
*/
-uint16_t random_u16(void);
+non_null()
+uint16_t random_u16(const Random *rng);
/**
* @brief Return a random 32 bit integer.
*/
-uint32_t random_u32(void);
+non_null()
+uint32_t random_u32(const Random *rng);
/**
* @brief Return a random 64 bit integer.
*/
-uint64_t random_u64(void);
+non_null()
+uint64_t random_u64(const Random *rng);
/**
* @brief Return a random 32 bit integer between 0 and upper_bound (excluded).
@@ -120,17 +238,50 @@ uint64_t random_u64(void);
* On libsodium builds this function guarantees a uniform distribution of possible outputs.
* On vanilla NACL builds this function is equivalent to `random() % upper_bound`.
*/
-uint32_t random_range_u32(uint32_t 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.
*/
-void random_nonce(uint8_t *nonce);
+non_null()
+void random_nonce(const Random *rng, uint8_t *nonce);
/**
* @brief Fill an array of bytes with random values.
*/
-void random_bytes(uint8_t *bytes, size_t length);
+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.
@@ -139,18 +290,39 @@ void random_bytes(uint8_t *bytes, size_t length);
*
* @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.
*/
-int32_t crypto_new_keypair(uint8_t *public_key, uint8_t *secret_key);
+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);
/**
@@ -162,8 +334,9 @@ void crypto_derive_public_key(uint8_t *public_key, const uint8_t *secret_key);
* @ref CRYPTO_NONCE_SIZE byte nonce.
*
* @retval -1 if there was a problem.
- * @retval >=0 length of encrypted data if everything was fine.
+ * @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);
@@ -176,8 +349,9 @@ int32_t encrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const
* @ref CRYPTO_NONCE_SIZE byte nonce.
*
* @retval -1 if there was a problem (decryption failed).
- * @retval >=0 length of plain text data if everything was fine.
+ * @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);
@@ -188,6 +362,7 @@ int32_t decrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const
* 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);
/**
@@ -197,22 +372,24 @@ int32_t encrypt_precompute(const uint8_t *public_key, const uint8_t *secret_key,
* using a shared key @ref CRYPTO_SYMMETRIC_KEY_SIZE big and a @ref CRYPTO_NONCE_SIZE
* byte nonce.
*
- * @return -1 if there was a problem, length of encrypted data if everything
- * was fine.
+ * @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 -
- * @ref CRYPTO_MAC_SIZE using a shared key @ref CRYPTO_SHARED_KEY_SIZE big and a
- * @ref CRYPTO_NONCE_SIZE byte nonce.
+ * 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.
*
- * @return -1 if there was a problem (decryption failed), length of plain data
- * if everything was fine.
+ * @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);
@@ -220,6 +397,7 @@ int32_t decrypt_data_symmetric(const uint8_t *shared_key, const uint8_t *nonce,
* @brief Increment the given nonce by 1 in big endian (rightmost byte incremented
* first).
*/
+non_null()
void increment_nonce(uint8_t *nonce);
/**
@@ -227,12 +405,14 @@ void increment_nonce(uint8_t *nonce);
*
* 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.
*/
-void new_symmetric_key(uint8_t *key);
+non_null()
+void new_symmetric_key(const Random *rng, uint8_t *key);
/**
* @brief Locks `length` bytes of memory pointed to by `data`.
@@ -242,6 +422,7 @@ void new_symmetric_key(uint8_t *key);
*
* @return true on success.
*/
+non_null()
bool crypto_memlock(void *data, size_t length);
/**
@@ -255,8 +436,15 @@ bool crypto_memlock(void *data, size_t length);
*
* @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
diff --git a/protocols/Tox/libtox/src/toxcore/events/conference_connected.c b/protocols/Tox/libtox/src/toxcore/events/conference_connected.c
new file mode 100644
index 0000000000..ce278755e1
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/conference_connected.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/conference_invite.c b/protocols/Tox/libtox/src/toxcore/events/conference_invite.c
new file mode 100644
index 0000000000..1d88f41d76
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/conference_invite.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/conference_message.c b/protocols/Tox/libtox/src/toxcore/events/conference_message.c
new file mode 100644
index 0000000000..bc9a19b738
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/conference_message.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/conference_peer_list_changed.c b/protocols/Tox/libtox/src/toxcore/events/conference_peer_list_changed.c
new file mode 100644
index 0000000000..b6aaa3bf7a
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/conference_peer_list_changed.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/conference_peer_name.c b/protocols/Tox/libtox/src/toxcore/events/conference_peer_name.c
new file mode 100644
index 0000000000..4d3f285eee
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/conference_peer_name.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/conference_title.c b/protocols/Tox/libtox/src/toxcore/events/conference_title.c
new file mode 100644
index 0000000000..1866966e9f
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/conference_title.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/events_alloc.c b/protocols/Tox/libtox/src/toxcore/events/events_alloc.c
new file mode 100644
index 0000000000..f661c5c039
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/events_alloc.c
@@ -0,0 +1,64 @@
+/* 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);
+ free(events);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/events_alloc.h b/protocols/Tox/libtox/src/toxcore/events/events_alloc.h
new file mode 100644
index 0000000000..6c5a7abd4f
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/events_alloc.h
@@ -0,0 +1,216 @@
+/* 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;
+};
+
+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;
+
+// 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;
+
+// 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;
+
+// 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;
+
+non_null()
+Tox_Events_State *tox_events_alloc(void *user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // C_TOXCORE_TOXCORE_TOX_EVENTS_INTERNAL_H
diff --git a/protocols/Tox/libtox/src/toxcore/events/file_chunk_request.c b/protocols/Tox/libtox/src/toxcore/events/file_chunk_request.c
new file mode 100644
index 0000000000..f5a9421f58
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/file_chunk_request.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/file_recv.c b/protocols/Tox/libtox/src/toxcore/events/file_recv.c
new file mode 100644
index 0000000000..a907b3fdbe
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/file_recv.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/file_recv_chunk.c b/protocols/Tox/libtox/src/toxcore/events/file_recv_chunk.c
new file mode 100644
index 0000000000..0fe7784465
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/file_recv_chunk.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/file_recv_control.c b/protocols/Tox/libtox/src/toxcore/events/file_recv_control.c
new file mode 100644
index 0000000000..af04b33dce
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/file_recv_control.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_connection_status.c b/protocols/Tox/libtox/src/toxcore/events/friend_connection_status.c
new file mode 100644
index 0000000000..3d325ef29f
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/friend_connection_status.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_lossless_packet.c b/protocols/Tox/libtox/src/toxcore/events/friend_lossless_packet.c
new file mode 100644
index 0000000000..944abd46c7
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/friend_lossless_packet.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_lossy_packet.c b/protocols/Tox/libtox/src/toxcore/events/friend_lossy_packet.c
new file mode 100644
index 0000000000..a787fce0cf
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/friend_lossy_packet.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_message.c b/protocols/Tox/libtox/src/toxcore/events/friend_message.c
new file mode 100644
index 0000000000..8fa6a19e34
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/friend_message.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_name.c b/protocols/Tox/libtox/src/toxcore/events/friend_name.c
new file mode 100644
index 0000000000..123918386a
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/friend_name.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_read_receipt.c b/protocols/Tox/libtox/src/toxcore/events/friend_read_receipt.c
new file mode 100644
index 0000000000..1485b67cca
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/friend_read_receipt.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_request.c b/protocols/Tox/libtox/src/toxcore/events/friend_request.c
new file mode 100644
index 0000000000..3366f54fe4
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/friend_request.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_status.c b/protocols/Tox/libtox/src/toxcore/events/friend_status.c
new file mode 100644
index 0000000000..c6eabdaa47
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/friend_status.c
@@ -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_unpack.h"
+
+#include "../bin_pack.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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_status_message.c b/protocols/Tox/libtox/src/toxcore/events/friend_status_message.c
new file mode 100644
index 0000000000..91fa88e321
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/friend_status_message.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_typing.c b/protocols/Tox/libtox/src/toxcore/events/friend_typing.c
new file mode 100644
index 0000000000..83c4dbac63
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/friend_typing.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/events/self_connection_status.c b/protocols/Tox/libtox/src/toxcore/events/self_connection_status.c
new file mode 100644
index 0000000000..0e381de7fa
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/events/self_connection_status.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/forwarding.c b/protocols/Tox/libtox/src/toxcore/forwarding.c
new file mode 100644
index 0000000000..5e885abd21
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/forwarding.c
@@ -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);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/forwarding.h b/protocols/Tox/libtox/src/toxcore/forwarding.h
new file mode 100644
index 0000000000..36ce8ad894
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/forwarding.h
@@ -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
diff --git a/protocols/Tox/libtox/src/toxcore/friend_connection.c b/protocols/Tox/libtox/src/toxcore/friend_connection.c
index 8a46194848..9e5dee7cbd 100644
--- a/protocols/Tox/libtox/src/toxcore/friend_connection.c
+++ b/protocols/Tox/libtox/src/toxcore/friend_connection.c
@@ -11,6 +11,7 @@
#include <stdlib.h>
#include <string.h>
+#include "ccompat.h"
#include "mono_time.h"
#include "util.h"
@@ -48,16 +49,20 @@ struct Friend_Conn {
Node_format tcp_relays[FRIEND_MAX_STORED_TCP_RELAYS];
uint16_t tcp_relay_counter;
+ uint32_t tcp_relay_share_index;
bool hosting_tcp_relay;
};
+static const Friend_Conn empty_friend_conn = {0};
+
struct Friend_Connections {
const Mono_Time *mono_time;
const Logger *logger;
Net_Crypto *net_crypto;
DHT *dht;
+ Broadcast_Info *broadcast;
Onion_Client *onion_c;
Friend_Conn *conns;
@@ -91,9 +96,11 @@ const IP_Port *friend_conn_get_dht_ip_port(const Friend_Conn *fc)
}
-/** return true if the friendcon_id is valid.
- * return false if the friendcon_id is not valid.
+/**
+ * @retval true if the friendcon_id is valid.
+ * @retval false if the friendcon_id is not valid.
*/
+non_null()
static bool friendconn_id_valid(const Friend_Connections *fr_c, int friendcon_id)
{
return (unsigned int)friendcon_id < fr_c->num_cons &&
@@ -102,11 +109,12 @@ static bool friendconn_id_valid(const Friend_Connections *fr_c, int friendcon_id
}
-/** Set the size of the friend connections list to num.
+/** @brief Set the size of the friend connections list to num.
*
- * return false if realloc fails.
- * return true if it succeeds.
+ * @retval false if realloc fails.
+ * @retval true if it succeeds.
*/
+non_null()
static bool realloc_friendconns(Friend_Connections *fr_c, uint32_t num)
{
if (num == 0) {
@@ -125,11 +133,12 @@ static bool realloc_friendconns(Friend_Connections *fr_c, uint32_t num)
return true;
}
-/** Create a new empty friend connection.
+/** @brief Create a new empty friend connection.
*
- * return -1 on failure.
- * return friendcon_id on success.
+ * @retval -1 on failure.
+ * @return friendcon_id on success.
*/
+non_null()
static int create_friend_conn(Friend_Connections *fr_c)
{
for (uint32_t i = 0; i < fr_c->num_cons; ++i) {
@@ -144,23 +153,24 @@ static int create_friend_conn(Friend_Connections *fr_c)
const int id = fr_c->num_cons;
++fr_c->num_cons;
- memset(&fr_c->conns[id], 0, sizeof(Friend_Conn));
+ fr_c->conns[id] = empty_friend_conn;
return id;
}
-/** Wipe a friend connection.
+/** @brief Wipe a friend connection.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int wipe_friend_conn(Friend_Connections *fr_c, int friendcon_id)
{
if (!friendconn_id_valid(fr_c, friendcon_id)) {
return -1;
}
- memset(&fr_c->conns[friendcon_id], 0, sizeof(Friend_Conn));
+ fr_c->conns[friendcon_id] = empty_friend_conn;
uint32_t i;
@@ -187,16 +197,17 @@ Friend_Conn *get_conn(const Friend_Connections *fr_c, int friendcon_id)
return &fr_c->conns[friendcon_id];
}
-/** return friendcon_id corresponding to the real public key on success.
- * return -1 on failure.
+/**
+ * @return friendcon_id corresponding to the real public key on success.
+ * @retval -1 on failure.
*/
int getfriend_conn_id_pk(const Friend_Connections *fr_c, const uint8_t *real_pk)
{
for (uint32_t i = 0; i < fr_c->num_cons; ++i) {
const Friend_Conn *friend_con = get_conn(fr_c, i);
- if (friend_con) {
- if (public_key_cmp(friend_con->real_public_key, real_pk) == 0) {
+ if (friend_con != nullptr) {
+ if (pk_equal(friend_con->real_public_key, real_pk)) {
return i;
}
}
@@ -205,11 +216,12 @@ int getfriend_conn_id_pk(const Friend_Connections *fr_c, const uint8_t *real_pk)
return -1;
}
-/** Add a TCP relay associated to the friend.
+/** @brief Add a TCP relay associated to the friend.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int friend_add_tcp_relay(Friend_Connections *fr_c, int friendcon_id, const IP_Port *ip_port,
const uint8_t *public_key)
{
@@ -217,12 +229,12 @@ static int friend_add_tcp_relay(Friend_Connections *fr_c, int friendcon_id, cons
Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
/* Local ip and same pk means that they are hosting a TCP relay. */
- if (ip_is_local(&ipp_copy.ip) && public_key_cmp(friend_con->dht_temp_pk, public_key) == 0) {
+ if (ip_is_local(&ipp_copy.ip) && pk_equal(friend_con->dht_temp_pk, public_key)) {
if (!net_family_is_unspec(friend_con->dht_ip_port.ip.family)) {
ipp_copy.ip = friend_con->dht_ip_port.ip;
} else {
@@ -234,8 +246,8 @@ static int friend_add_tcp_relay(Friend_Connections *fr_c, int friendcon_id, cons
for (unsigned i = 0; i < FRIEND_MAX_STORED_TCP_RELAYS; ++i) {
if (!net_family_is_unspec(friend_con->tcp_relays[i].ip_port.ip.family)
- && public_key_cmp(friend_con->tcp_relays[i].public_key, public_key) == 0) {
- memset(&friend_con->tcp_relays[i], 0, sizeof(Node_format));
+ && pk_equal(friend_con->tcp_relays[i].public_key, public_key)) {
+ friend_con->tcp_relays[i] = empty_node_format;
}
}
@@ -247,11 +259,12 @@ static int friend_add_tcp_relay(Friend_Connections *fr_c, int friendcon_id, cons
}
/** Connect to number saved relays for friend. */
+non_null()
static void connect_to_saved_tcp_relays(Friend_Connections *fr_c, int friendcon_id, unsigned int number)
{
const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return;
}
@@ -267,26 +280,30 @@ static void connect_to_saved_tcp_relays(Friend_Connections *fr_c, int friendcon_
}
}
+non_null()
static unsigned int send_relays(Friend_Connections *fr_c, int friendcon_id)
{
Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return 0;
}
Node_format nodes[MAX_SHARED_RELAYS] = {{{0}}};
uint8_t data[1024];
- const int n = copy_connected_tcp_relays(fr_c->net_crypto, nodes, MAX_SHARED_RELAYS);
+ const uint32_t n = copy_connected_tcp_relays_index(fr_c->net_crypto, nodes, MAX_SHARED_RELAYS,
+ friend_con->tcp_relay_share_index);
- for (int i = 0; i < n; ++i) {
+ friend_con->tcp_relay_share_index += MAX_SHARED_RELAYS;
+
+ for (uint32_t i = 0; i < n; ++i) {
/* Associated the relays being sent with this connection.
* On receiving the peer will do the same which will establish the connection. */
friend_add_tcp_relay(fr_c, friendcon_id, &nodes[i].ip_port, nodes[i].public_key);
}
- int length = pack_nodes(data + 1, sizeof(data) - 1, nodes, n);
+ int length = pack_nodes(fr_c->logger, data + 1, sizeof(data) - 1, nodes, n);
if (length <= 0) {
return 0;
@@ -295,7 +312,7 @@ static unsigned int send_relays(Friend_Connections *fr_c, int friendcon_id)
data[0] = PACKET_ID_SHARE_RELAYS;
++length;
- if (write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, data, length, 0) != -1) {
+ if (write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, data, length, false) != -1) {
friend_con->share_relays_lastsent = mono_time_get(fr_c->mono_time);
return 1;
}
@@ -304,12 +321,13 @@ static unsigned int send_relays(Friend_Connections *fr_c, int friendcon_id)
}
/** callback for recv TCP relay nodes. */
+non_null()
static int tcp_relay_node_callback(void *object, uint32_t number, const IP_Port *ip_port, const uint8_t *public_key)
{
Friend_Connections *fr_c = (Friend_Connections *)object;
const Friend_Conn *friend_con = get_conn(fr_c, number);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
@@ -320,14 +338,17 @@ static int tcp_relay_node_callback(void *object, uint32_t number, const IP_Port
return add_tcp_relay(fr_c->net_crypto, ip_port, public_key);
}
+non_null()
static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id);
+
/** Callback for DHT ip_port changes. */
+non_null()
static void dht_ip_callback(void *object, int32_t number, const IP_Port *ip_port)
{
Friend_Connections *const fr_c = (Friend_Connections *)object;
Friend_Conn *const friend_con = get_conn(fr_c, number);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return;
}
@@ -335,7 +356,7 @@ static void dht_ip_callback(void *object, int32_t number, const IP_Port *ip_port
friend_new_connection(fr_c, number);
}
- set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port, 1);
+ set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port, true);
friend_con->dht_ip_port = *ip_port;
friend_con->dht_ip_port_lastrecv = mono_time_get(fr_c->mono_time);
@@ -345,17 +366,18 @@ static void dht_ip_callback(void *object, int32_t number, const IP_Port *ip_port
}
}
+non_null()
static void change_dht_pk(Friend_Connections *fr_c, int friendcon_id, const uint8_t *dht_public_key)
{
Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return;
}
friend_con->dht_pk_lastrecv = mono_time_get(fr_c->mono_time);
- if (friend_con->dht_lock) {
+ if (friend_con->dht_lock > 0) {
if (dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock) != 0) {
LOGGER_ERROR(fr_c->logger, "a. Could not delete dht peer. Please report this.");
return;
@@ -368,26 +390,27 @@ static void change_dht_pk(Friend_Connections *fr_c, int friendcon_id, const uint
memcpy(friend_con->dht_temp_pk, dht_public_key, CRYPTO_PUBLIC_KEY_SIZE);
}
-static int handle_status(void *object, int number, uint8_t status, void *userdata)
+non_null()
+static int handle_status(void *object, int number, bool status, void *userdata)
{
Friend_Connections *const fr_c = (Friend_Connections *)object;
Friend_Conn *const friend_con = get_conn(fr_c, number);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
- bool status_changed = 0;
+ bool status_changed = false;
if (status) { /* Went online. */
- status_changed = 1;
+ status_changed = true;
friend_con->status = FRIENDCONN_STATUS_CONNECTED;
friend_con->ping_lastrecv = mono_time_get(fr_c->mono_time);
friend_con->share_relays_lastsent = 0;
onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status);
} else { /* Went offline. */
if (friend_con->status != FRIENDCONN_STATUS_CONNECTING) {
- status_changed = 1;
+ status_changed = true;
friend_con->dht_pk_lastrecv = mono_time_get(fr_c->mono_time);
onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status);
}
@@ -398,12 +421,12 @@ static int handle_status(void *object, int number, uint8_t status, void *userdat
}
if (status_changed) {
- if (fr_c->global_status_callback) {
+ if (fr_c->global_status_callback != nullptr) {
fr_c->global_status_callback(fr_c->global_status_callback_object, number, status, userdata);
}
for (unsigned i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
- if (friend_con->callbacks[i].status_callback) {
+ if (friend_con->callbacks[i].status_callback != nullptr) {
friend_con->callbacks[i].status_callback(
friend_con->callbacks[i].callback_object,
friend_con->callbacks[i].callback_id, status, userdata);
@@ -415,16 +438,17 @@ static int handle_status(void *object, int number, uint8_t status, void *userdat
}
/** Callback for dht public key changes. */
+non_null()
static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_public_key, void *userdata)
{
Friend_Connections *const fr_c = (Friend_Connections *)object;
Friend_Conn *const friend_con = get_conn(fr_c, number);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return;
}
- if (public_key_cmp(friend_con->dht_temp_pk, dht_public_key) == 0) {
+ if (pk_equal(friend_con->dht_temp_pk, dht_public_key)) {
return;
}
@@ -434,13 +458,14 @@ static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_pub
if (friend_con->crypt_connection_id != -1) {
crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id);
friend_con->crypt_connection_id = -1;
- handle_status(object, number, 0, userdata); /* Going offline. */
+ handle_status(object, number, false, userdata); /* Going offline. */
}
friend_new_connection(fr_c, number);
onion_set_friend_DHT_pubkey(fr_c->onion_c, friend_con->onion_friendnum, dht_public_key);
}
+non_null()
static int handle_packet(void *object, int number, const uint8_t *data, uint16_t length, void *userdata)
{
if (length == 0) {
@@ -450,12 +475,12 @@ static int handle_packet(void *object, int number, const uint8_t *data, uint16_t
Friend_Connections *const fr_c = (Friend_Connections *)object;
Friend_Conn *friend_con = get_conn(fr_c, number);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
if (data[0] == PACKET_ID_FRIEND_REQUESTS) {
- if (fr_c->fr_request_callback) {
+ if (fr_c->fr_request_callback != nullptr) {
fr_c->fr_request_callback(fr_c->fr_request_object, friend_con->real_public_key, data, length, userdata);
}
@@ -469,7 +494,7 @@ static int handle_packet(void *object, int number, const uint8_t *data, uint16_t
if (data[0] == PACKET_ID_SHARE_RELAYS) {
Node_format nodes[MAX_SHARED_RELAYS];
- const int n = unpack_nodes(nodes, MAX_SHARED_RELAYS, nullptr, data + 1, length - 1, 1);
+ const int n = unpack_nodes(nodes, MAX_SHARED_RELAYS, nullptr, data + 1, length - 1, true);
if (n == -1) {
return -1;
@@ -483,7 +508,7 @@ static int handle_packet(void *object, int number, const uint8_t *data, uint16_t
}
for (unsigned i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
- if (friend_con->callbacks[i].data_callback) {
+ if (friend_con->callbacks[i].data_callback != nullptr) {
friend_con->callbacks[i].data_callback(
friend_con->callbacks[i].callback_object,
friend_con->callbacks[i].callback_id, data, length, userdata);
@@ -491,7 +516,7 @@ static int handle_packet(void *object, int number, const uint8_t *data, uint16_t
friend_con = get_conn(fr_c, number);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
}
@@ -499,6 +524,7 @@ static int handle_packet(void *object, int number, const uint8_t *data, uint16_t
return 0;
}
+non_null()
static int handle_lossy_packet(void *object, int number, const uint8_t *data, uint16_t length, void *userdata)
{
if (length == 0) {
@@ -508,12 +534,12 @@ static int handle_lossy_packet(void *object, int number, const uint8_t *data, ui
const Friend_Connections *const fr_c = (const Friend_Connections *)object;
const Friend_Conn *friend_con = get_conn(fr_c, number);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
for (unsigned i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
- if (friend_con->callbacks[i].lossy_data_callback) {
+ if (friend_con->callbacks[i].lossy_data_callback != nullptr) {
friend_con->callbacks[i].lossy_data_callback(
friend_con->callbacks[i].callback_object,
friend_con->callbacks[i].callback_id, data, length, userdata);
@@ -521,7 +547,7 @@ static int handle_lossy_packet(void *object, int number, const uint8_t *data, ui
friend_con = get_conn(fr_c, number);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
}
@@ -529,13 +555,14 @@ static int handle_lossy_packet(void *object, int number, const uint8_t *data, ui
return 0;
}
+non_null()
static int handle_new_connections(void *object, const New_Connection *n_c)
{
Friend_Connections *const fr_c = (Friend_Connections *)object;
const int friendcon_id = getfriend_conn_id_pk(fr_c, n_c->public_key);
Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
@@ -555,13 +582,13 @@ static int handle_new_connections(void *object, const New_Connection *n_c)
friend_con->crypt_connection_id = id;
if (!net_family_is_ipv4(n_c->source.ip.family) && !net_family_is_ipv6(n_c->source.ip.family)) {
- set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, &friend_con->dht_ip_port, 0);
+ set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, &friend_con->dht_ip_port, false);
} else {
friend_con->dht_ip_port = n_c->source;
friend_con->dht_ip_port_lastrecv = mono_time_get(fr_c->mono_time);
}
- if (public_key_cmp(friend_con->dht_temp_pk, n_c->dht_public_key) != 0) {
+ if (!pk_equal(friend_con->dht_temp_pk, n_c->dht_public_key)) {
change_dht_pk(fr_c, friendcon_id, n_c->dht_public_key);
}
@@ -573,7 +600,7 @@ static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id)
{
Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
@@ -582,7 +609,7 @@ static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id)
}
/* If dht_temp_pk does not contains a pk. */
- if (!friend_con->dht_lock) {
+ if (friend_con->dht_lock == 0) {
return -1;
}
@@ -601,16 +628,17 @@ static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id)
return 0;
}
+non_null()
static int send_ping(const Friend_Connections *fr_c, int friendcon_id)
{
Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
const uint8_t ping = PACKET_ID_ALIVE;
- const int64_t ret = write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, &ping, sizeof(ping), 0);
+ const int64_t ret = write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, &ping, sizeof(ping), false);
if (ret != -1) {
friend_con->ping_lastsent = mono_time_get(fr_c->mono_time);
@@ -620,16 +648,16 @@ static int send_ping(const Friend_Connections *fr_c, int friendcon_id)
return -1;
}
-/** Increases lock_count for the connection with friendcon_id by 1.
+/** @brief Increases lock_count for the connection with friendcon_id by 1.
*
- * return 0 on success.
- * return -1 on failure.
+ * @retval 0 on success.
+ * @retval -1 on failure.
*/
int friend_connection_lock(const Friend_Connections *fr_c, int friendcon_id)
{
Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
@@ -637,57 +665,58 @@ int friend_connection_lock(const Friend_Connections *fr_c, int friendcon_id)
return 0;
}
-/** return FRIENDCONN_STATUS_CONNECTED if the friend is connected.
- * return FRIENDCONN_STATUS_CONNECTING if the friend isn't connected.
- * return FRIENDCONN_STATUS_NONE on failure.
+/**
+ * @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.
*/
unsigned int friend_con_connected(const Friend_Connections *fr_c, int friendcon_id)
{
const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return 0;
}
return friend_con->status;
}
-/** Copy public keys associated to friendcon_id.
+/** @brief Copy public keys associated to friendcon_id.
*
- * return 0 on success.
- * return -1 on failure.
+ * @retval 0 on success.
+ * @retval -1 on failure.
*/
int get_friendcon_public_keys(uint8_t *real_pk, uint8_t *dht_temp_pk, const Friend_Connections *fr_c, int friendcon_id)
{
const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
- if (real_pk) {
+ if (real_pk != nullptr) {
memcpy(real_pk, friend_con->real_public_key, CRYPTO_PUBLIC_KEY_SIZE);
}
- if (dht_temp_pk) {
+ if (dht_temp_pk != nullptr) {
memcpy(dht_temp_pk, friend_con->dht_temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
}
return 0;
}
-/** Set temp dht key for connection.
- */
+/** Set temp dht key for connection. */
void set_dht_temp_pk(Friend_Connections *fr_c, int friendcon_id, const uint8_t *dht_temp_pk, void *userdata)
{
dht_pk_callback(fr_c, friendcon_id, dht_temp_pk, userdata);
}
-/** Set the callbacks for the friend connection.
- * index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array.
+/** @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.
*
- * return 0 on success.
- * return -1 on failure
+ * @retval 0 on success.
+ * @retval -1 on failure
*/
int friend_connection_callbacks(const Friend_Connections *fr_c, int friendcon_id, unsigned int index,
fc_status_cb *status_callback,
@@ -697,7 +726,7 @@ int friend_connection_callbacks(const Friend_Connections *fr_c, int friendcon_id
{
Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
@@ -705,6 +734,11 @@ int friend_connection_callbacks(const Friend_Connections *fr_c, int friendcon_id
return -1;
}
+ if (object != nullptr && (status_callback == nullptr || data_callback == nullptr || lossy_data_callback == nullptr)) {
+ LOGGER_ERROR(fr_c->logger, "non-null user data object but null callbacks");
+ return -1;
+ }
+
friend_con->callbacks[index].status_callback = status_callback;
friend_con->callbacks[index].data_callback = data_callback;
friend_con->callbacks[index].lossy_data_callback = lossy_data_callback;
@@ -718,31 +752,36 @@ int friend_connection_callbacks(const Friend_Connections *fr_c, int friendcon_id
/** Set global status callback for friend connections. */
void set_global_status_callback(Friend_Connections *fr_c, global_status_cb *global_status_callback, void *object)
{
+ if (object != nullptr && global_status_callback == nullptr) {
+ LOGGER_ERROR(fr_c->logger, "non-null user data object but null callback");
+ object = nullptr;
+ }
+
fr_c->global_status_callback = global_status_callback;
fr_c->global_status_callback_object = object;
}
-/** return the crypt_connection_id for the connection.
+/** @brief return the crypt_connection_id for the connection.
*
- * return crypt_connection_id on success.
- * return -1 on failure.
+ * @return crypt_connection_id on success.
+ * @retval -1 on failure.
*/
int friend_connection_crypt_connection_id(const Friend_Connections *fr_c, int friendcon_id)
{
const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
return friend_con->crypt_connection_id;
}
-/** Create a new friend connection.
+/** @brief Create a new friend connection.
* If one to that real public key already exists, increase lock count and return it.
*
- * return -1 on failure.
- * return connection id on success.
+ * @retval -1 on failure.
+ * @return connection id on success.
*/
int new_friend_connection(Friend_Connections *fr_c, const uint8_t *real_public_key)
{
@@ -778,20 +817,20 @@ int new_friend_connection(Friend_Connections *fr_c, const uint8_t *real_public_k
return friendcon_id;
}
-/** Kill a friend connection.
+/** @brief Kill a friend connection.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id)
{
Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
- if (friend_con->lock_count) {
+ if (friend_con->lock_count > 0) {
--friend_con->lock_count;
return 0;
}
@@ -799,7 +838,7 @@ int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id)
onion_delfriend(fr_c->onion_c, friend_con->onion_friendnum);
crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id);
- if (friend_con->dht_lock) {
+ if (friend_con->dht_lock > 0) {
dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock);
}
@@ -807,7 +846,7 @@ int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id)
}
-/** Set friend request callback.
+/** @brief Set friend request callback.
*
* This function will be called every time a friend request packet is received.
*/
@@ -818,11 +857,11 @@ void set_friend_request_callback(Friend_Connections *fr_c, fr_request_cb *fr_req
oniondata_registerhandler(fr_c->onion_c, CRYPTO_PACKET_FRIEND_REQ, fr_request_callback, object);
}
-/** Send a Friend request packet.
+/** @brief Send a Friend request packet.
*
- * return -1 if failure.
- * return 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.
+ * @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.
*/
int send_friend_request_packet(Friend_Connections *fr_c, int friendcon_id, uint32_t nospam_num, const uint8_t *data,
uint16_t length)
@@ -833,7 +872,7 @@ int send_friend_request_packet(Friend_Connections *fr_c, int friendcon_id, uint3
const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
- if (!friend_con) {
+ if (friend_con == nullptr) {
return -1;
}
@@ -843,7 +882,7 @@ int send_friend_request_packet(Friend_Connections *fr_c, int friendcon_id, uint3
if (friend_con->status == FRIENDCONN_STATUS_CONNECTED) {
packet[0] = PACKET_ID_FRIEND_REQUESTS;
- return write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, packet, SIZEOF_VLA(packet), 0) != -1;
+ return write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, packet, SIZEOF_VLA(packet), false) != -1 ? 1 : 0;
}
packet[0] = CRYPTO_PACKET_FRIEND_REQ;
@@ -857,8 +896,9 @@ int send_friend_request_packet(Friend_Connections *fr_c, int friendcon_id, uint3
}
/** Create new friend_connections instance. */
-Friend_Connections *new_friend_connections(const Logger *logger, const Mono_Time *mono_time, Onion_Client *onion_c,
- bool local_discovery_enabled)
+Friend_Connections *new_friend_connections(
+ const Logger *logger, const Mono_Time *mono_time, const Network *ns,
+ Onion_Client *onion_c, bool local_discovery_enabled)
{
if (onion_c == nullptr) {
return nullptr;
@@ -882,13 +922,18 @@ Friend_Connections *new_friend_connections(const Logger *logger, const Mono_Time
new_connection_handler(temp->net_crypto, &handle_new_connections, temp);
if (temp->local_discovery_enabled) {
- lan_discovery_init(temp->dht);
+ temp->broadcast = lan_discovery_init(ns);
+
+ if (temp->broadcast == nullptr) {
+ LOGGER_ERROR(logger, "could not initialise LAN discovery");
+ }
}
return temp;
}
/** Send a LAN discovery packet every LAN_DISCOVERY_INTERVAL seconds. */
+non_null()
static void lan_discovery(Friend_Connections *fr_c)
{
if (fr_c->last_lan_discovery + LAN_DISCOVERY_INTERVAL < mono_time_get(fr_c->mono_time)) {
@@ -897,11 +942,12 @@ static void lan_discovery(Friend_Connections *fr_c)
last = last > TOX_PORTRANGE_TO ? TOX_PORTRANGE_TO : last;
// Always send to default port
- lan_discovery_send(dht_get_net(fr_c->dht), dht_get_self_public_key(fr_c->dht), net_htons(TOX_PORT_DEFAULT));
+ lan_discovery_send(dht_get_net(fr_c->dht), fr_c->broadcast, dht_get_self_public_key(fr_c->dht),
+ net_htons(TOX_PORT_DEFAULT));
// And check some extra ports
for (uint16_t port = first; port < last; ++port) {
- lan_discovery_send(dht_get_net(fr_c->dht), dht_get_self_public_key(fr_c->dht), net_htons(port));
+ lan_discovery_send(dht_get_net(fr_c->dht), fr_c->broadcast, dht_get_self_public_key(fr_c->dht), net_htons(port));
}
// Don't include default port in port range
@@ -918,10 +964,10 @@ void do_friend_connections(Friend_Connections *fr_c, void *userdata)
for (uint32_t i = 0; i < fr_c->num_cons; ++i) {
Friend_Conn *const friend_con = get_conn(fr_c, i);
- if (friend_con) {
+ if (friend_con != nullptr) {
if (friend_con->status == FRIENDCONN_STATUS_CONNECTING) {
if (friend_con->dht_pk_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) {
- if (friend_con->dht_lock) {
+ if (friend_con->dht_lock > 0) {
dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock);
friend_con->dht_lock = 0;
memset(friend_con->dht_temp_pk, 0, CRYPTO_PUBLIC_KEY_SIZE);
@@ -929,12 +975,12 @@ void do_friend_connections(Friend_Connections *fr_c, void *userdata)
}
if (friend_con->dht_ip_port_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) {
- friend_con->dht_ip_port.ip.family = net_family_unspec;
+ friend_con->dht_ip_port.ip.family = net_family_unspec();
}
- if (friend_con->dht_lock) {
+ if (friend_con->dht_lock > 0) {
if (friend_new_connection(fr_c, i) == 0) {
- set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, &friend_con->dht_ip_port, 0);
+ set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, &friend_con->dht_ip_port, false);
connect_to_saved_tcp_relays(fr_c, i, MAX_FRIEND_TCP_CONNECTIONS / 2); /* Only fill it half up. */
}
}
@@ -951,7 +997,7 @@ void do_friend_connections(Friend_Connections *fr_c, void *userdata)
/* If we stopped receiving ping packets, kill it. */
crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id);
friend_con->crypt_connection_id = -1;
- handle_status(fr_c, i, 0, userdata); /* Going offline. */
+ handle_status(fr_c, i, false, userdata); /* Going offline. */
}
}
}
@@ -965,7 +1011,7 @@ void do_friend_connections(Friend_Connections *fr_c, void *userdata)
/** Free everything related with friend_connections. */
void kill_friend_connections(Friend_Connections *fr_c)
{
- if (!fr_c) {
+ if (fr_c == nullptr) {
return;
}
@@ -973,9 +1019,6 @@ void kill_friend_connections(Friend_Connections *fr_c)
kill_friend_connection(fr_c, i);
}
- if (fr_c->local_discovery_enabled) {
- lan_discovery_kill(fr_c->dht);
- }
-
+ lan_discovery_kill(fr_c->broadcast);
free(fr_c);
}
diff --git a/protocols/Tox/libtox/src/toxcore/friend_connection.h b/protocols/Tox/libtox/src/toxcore/friend_connection.h
index 4986b3bfb9..93bd5113ac 100644
--- a/protocols/Tox/libtox/src/toxcore/friend_connection.h
+++ b/protocols/Tox/libtox/src/toxcore/friend_connection.h
@@ -36,8 +36,8 @@
/** Max number of tcp relays sent to friends */
#define MAX_SHARED_RELAYS RECOMMENDED_FRIEND_TCP_CONNECTIONS
-/** Interval between the sending of tcp relay information */
-#define SHARE_RELAYS_INTERVAL (5 * 60)
+/** How often we share our TCP relays with each friend connection */
+#define SHARE_RELAYS_INTERVAL (60 * 2)
typedef enum Friendconn_Status {
@@ -48,112 +48,129 @@ typedef enum Friendconn_Status {
typedef struct Friend_Connections Friend_Connections;
-Net_Crypto *friendconn_net_crypto(const Friend_Connections *fr_c);
+non_null() Net_Crypto *friendconn_net_crypto(const Friend_Connections *fr_c);
-/** return friendcon_id corresponding to the real public key on success.
- * return -1 on failure.
+/** @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);
-/** Increases lock_count for the connection with friendcon_id by 1.
+/** @brief Increases lock_count for the connection with friendcon_id by 1.
*
- * return 0 on success.
- * return -1 on failure.
+ * @retval 0 on success.
+ * @retval -1 on failure.
*/
+non_null()
int friend_connection_lock(const Friend_Connections *fr_c, int friendcon_id);
-/** return FRIENDCONN_STATUS_CONNECTED if the friend is connected.
- * return FRIENDCONN_STATUS_CONNECTING if the friend isn't connected.
- * return FRIENDCONN_STATUS_NONE on failure.
+/**
+ * @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);
-/** Copy public keys associated to friendcon_id.
+/** @brief Copy public keys associated to friendcon_id.
*
- * return 0 on success.
- * return -1 on failure.
+ * @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.
- */
+/** 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, uint8_t status, void *userdata);
+typedef int global_status_cb(void *object, int id, bool status, void *userdata);
-typedef int fc_status_cb(void *object, int id, uint8_t 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);
-/** Set the callbacks for the friend connection.
- * index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array.
+/** @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.
*
- * return 0 on success.
- * return -1 on failure
+ * @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);
-/** return the crypt_connection_id for the connection.
+/** @brief return the crypt_connection_id for the connection.
*
- * return crypt_connection_id on success.
- * return -1 on failure.
+ * @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);
-/** Create a new friend connection.
+/** @brief Create a new friend connection.
* If one to that real public key already exists, increase lock count and return it.
*
- * return -1 on failure.
- * return connection id on success.
+ * @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);
-/** Kill a friend connection.
+/** @brief Kill a friend connection.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id);
-/** Send a Friend request packet.
+/** @brief Send a Friend request packet.
*
- * return -1 if failure.
- * return 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.
+ * @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.
*/
-int send_friend_request_packet(Friend_Connections *fr_c, int friendcon_id, uint32_t nospam_num, const uint8_t *data,
- uint16_t length);
+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);
+typedef int fr_request_cb(
+ void *object, const uint8_t *source_pubkey, const uint8_t *data, uint16_t len, void *userdata);
-/** Set friend request callback.
+/** @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. */
-Friend_Connections *new_friend_connections(const Logger *logger, const Mono_Time *mono_time, Onion_Client *onion_c,
- bool local_discovery_enabled);
+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;
-Friend_Conn *get_conn(const Friend_Connections *fr_c, int friendcon_id);
-int friend_conn_get_onion_friendnum(const Friend_Conn *fc);
-const IP_Port *friend_conn_get_dht_ip_port(const Friend_Conn *fc);
+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
diff --git a/protocols/Tox/libtox/src/toxcore/friend_requests.c b/protocols/Tox/libtox/src/toxcore/friend_requests.c
index f4d572996d..7f18b1ff02 100644
--- a/protocols/Tox/libtox/src/toxcore/friend_requests.c
+++ b/protocols/Tox/libtox/src/toxcore/friend_requests.c
@@ -11,10 +11,12 @@
#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.)
+/**
+ * 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
@@ -47,8 +49,7 @@ uint32_t get_nospam(const Friend_Requests *fr)
}
-/** Set the function that will be executed when a friend request for us is received.
- */
+/** 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;
@@ -56,8 +57,8 @@ void callback_friendrequest(Friend_Requests *fr, fr_friend_request_cb *function,
fr->handle_friendrequest_object = object;
}
-/** 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.)
+/** @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)
{
@@ -66,25 +67,27 @@ void set_filter_function(Friend_Requests *fr, filter_function_cb *function, void
}
/** 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;
}
- id_copy(fr->received.requests[fr->received.requests_index], real_pk);
+ pk_copy(fr->received.requests[fr->received.requests_index], real_pk);
++fr->received.requests_index;
}
-/** Check if a friend request was already received.
+/** @brief Check if a friend request was already received.
*
- * return false if it did not.
- * return true if it did.
+ * @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 (id_equal(fr->received.requests[i], real_pk)) {
+ if (pk_equal(fr->received.requests[i], real_pk)) {
return true;
}
}
@@ -92,15 +95,15 @@ static bool request_received(const Friend_Requests *fr, const uint8_t *real_pk)
return false;
}
-/** Remove real_pk from received_requests list.
+/** @brief Remove real_pk from received_requests list.
*
- * return 0 if it removed it successfully.
- * return -1 if it didn't find it.
+ * @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 (id_equal(fr->received.requests[i], real_pk)) {
+ if (pk_equal(fr->received.requests[i], real_pk)) {
crypto_memzero(fr->received.requests[i], CRYPTO_PUBLIC_KEY_SIZE);
return 0;
}
@@ -110,6 +113,7 @@ int remove_request_received(Friend_Requests *fr, const uint8_t *real_pk)
}
+non_null()
static int friendreq_handlepacket(void *object, const uint8_t *source_pubkey, const uint8_t *packet, uint16_t length,
void *userdata)
{
@@ -134,7 +138,7 @@ static int friendreq_handlepacket(void *object, const uint8_t *source_pubkey, co
return 1;
}
- if (fr->filter_function) {
+ if (fr->filter_function != nullptr) {
if (fr->filter_function(source_pubkey, fr->filter_function_userdata) != 0) {
return 1;
}
diff --git a/protocols/Tox/libtox/src/toxcore/friend_requests.h b/protocols/Tox/libtox/src/toxcore/friend_requests.h
index bb2b10b81c..2614527163 100644
--- a/protocols/Tox/libtox/src/toxcore/friend_requests.h
+++ b/protocols/Tox/libtox/src/toxcore/friend_requests.h
@@ -16,34 +16,39 @@
typedef struct Friend_Requests Friend_Requests;
/** Set and get the nospam variable used to prevent one type of friend request spam. */
-void set_nospam(Friend_Requests *fr, uint32_t num);
-uint32_t get_nospam(const Friend_Requests *fr);
+non_null() void set_nospam(Friend_Requests *fr, uint32_t num);
+non_null() uint32_t get_nospam(const Friend_Requests *fr);
-/** Remove real_pk from received_requests list.
+/** @brief Remove real_pk from received_requests list.
*
- * return 0 if it removed it successfully.
- * return -1 if it didn't find it.
+ * @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.
- */
+/** 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);
-/** 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.)
+/** @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
diff --git a/protocols/Tox/libtox/src/toxcore/group.c b/protocols/Tox/libtox/src/toxcore/group.c
index fd7c491a1d..0e851b1e34 100644
--- a/protocols/Tox/libtox/src/toxcore/group.c
+++ b/protocols/Tox/libtox/src/toxcore/group.c
@@ -12,10 +12,153 @@
#include <stdlib.h>
#include <string.h>
+#include "ccompat.h"
#include "mono_time.h"
#include "state.h"
#include "util.h"
+enum {
+ /** Connection is to one of the closest DESIRED_CLOSEST peers */
+ GROUPCHAT_CONNECTION_REASON_CLOSEST = 1 << 0,
+
+ /** Connection is to a peer we are introducing to the conference */
+ GROUPCHAT_CONNECTION_REASON_INTRODUCING = 1 << 1,
+
+ /** Connection is to a peer who is introducing us to the conference */
+ GROUPCHAT_CONNECTION_REASON_INTRODUCER = 1 << 2,
+};
+
+typedef enum Groupchat_Connection_Type {
+ GROUPCHAT_CONNECTION_NONE,
+ GROUPCHAT_CONNECTION_CONNECTING,
+ GROUPCHAT_CONNECTION_ONLINE,
+} Groupchat_Connection_Type;
+
+typedef enum Groupchat_Status {
+ GROUPCHAT_STATUS_NONE,
+ GROUPCHAT_STATUS_VALID,
+ GROUPCHAT_STATUS_CONNECTED,
+} Groupchat_Status;
+
+#define GROUP_ID_LENGTH CRYPTO_SYMMETRIC_KEY_SIZE
+
+#define DESIRED_CLOSEST 4
+#define MAX_GROUP_CONNECTIONS 16
+#define MAX_LAST_MESSAGE_INFOS 8
+#define MAX_LOSSY_COUNT 256
+
+/** Maximum number of frozen peers to store; `group_set_max_frozen()` overrides. */
+#define MAX_FROZEN_DEFAULT 128
+
+typedef struct Message_Info {
+ uint32_t message_number;
+ uint8_t message_id;
+} Message_Info;
+
+typedef struct Group_Peer {
+ uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
+ uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
+ bool temp_pk_updated;
+ bool is_friend;
+
+ uint64_t last_active;
+
+ Message_Info
+ last_message_infos[MAX_LAST_MESSAGE_INFOS]; /* received messages, strictly decreasing in message_number */
+ uint8_t num_last_message_infos;
+
+ uint8_t nick[MAX_NAME_LENGTH];
+ uint8_t nick_len;
+ bool nick_updated;
+
+ uint16_t peer_number;
+
+ uint8_t recv_lossy[MAX_LOSSY_COUNT];
+ uint16_t bottom_lossy_number;
+ uint16_t top_lossy_number;
+
+ void *object;
+} Group_Peer;
+
+typedef struct Groupchat_Connection {
+ uint8_t type; /* `GROUPCHAT_CONNECTION_*` */
+ uint8_t reasons; /* bit field with flags `GROUPCHAT_CONNECTION_REASON_*` */
+ uint32_t number;
+ uint16_t group_number;
+} Groupchat_Connection;
+
+typedef struct Groupchat_Closest {
+ /**
+ * Whether this peer is active in the closest_peers array.
+ */
+ bool active;
+ uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
+ uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
+} Groupchat_Closest;
+
+typedef struct Group_c {
+ uint8_t status;
+
+ bool need_send_name;
+ bool title_fresh;
+
+ Group_Peer *group;
+ uint32_t numpeers;
+
+ Group_Peer *frozen;
+ uint32_t numfrozen;
+
+ uint32_t maxfrozen;
+
+ Groupchat_Connection connections[MAX_GROUP_CONNECTIONS];
+
+ uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
+ Groupchat_Closest closest_peers[DESIRED_CLOSEST];
+ uint8_t changed;
+
+ uint8_t type;
+ uint8_t id[GROUP_ID_LENGTH];
+
+ uint8_t title[MAX_NAME_LENGTH];
+ uint8_t title_len;
+
+ uint32_t message_number;
+ uint16_t lossy_message_number;
+ uint16_t peer_number;
+
+ uint64_t last_sent_ping;
+
+ uint32_t num_introducer_connections;
+
+ void *object;
+
+ peer_on_join_cb *peer_on_join;
+ peer_on_leave_cb *peer_on_leave;
+ group_on_delete_cb *group_on_delete;
+} Group_c;
+
+struct Group_Chats {
+ const Mono_Time *mono_time;
+
+ Messenger *m;
+ Friend_Connections *fr_c;
+
+ Group_c *chats;
+ uint16_t num_chats;
+
+ g_conference_invite_cb *invite_callback;
+ g_conference_connected_cb *connected_callback;
+ g_conference_message_cb *message_callback;
+ peer_name_cb *peer_name_callback;
+ peer_list_changed_cb *peer_list_changed_callback;
+ title_cb *title_callback;
+
+ lossy_packet_cb *lossy_packethandlers[256];
+};
+
+static const Group_c empty_group_c = {0};
+static const Group_Peer empty_group_peer = {{0}};
+
/**
* Packet type IDs as per the protocol specification.
*/
@@ -57,14 +200,34 @@ typedef enum Peer_Id {
static_assert(GROUP_ID_LENGTH == CRYPTO_PUBLIC_KEY_SIZE,
"GROUP_ID_LENGTH should be equal to CRYPTO_PUBLIC_KEY_SIZE");
+const Mono_Time *g_mono_time(const Group_Chats *g_c)
+{
+ return g_c->mono_time;
+}
+
+non_null()
static bool group_id_eq(const uint8_t *a, const uint8_t *b)
{
- return public_key_cmp(a, b) == 0;
+ return pk_equal(a, b);
}
-/** return false if the groupnumber is not valid.
- * return true if the groupnumber is valid.
+non_null()
+static bool g_title_eq(Group_c *g, const uint8_t *title, uint8_t title_len)
+{
+ return memeq(g->title, g->title_len, title, title_len);
+}
+
+non_null()
+static bool g_peer_nick_eq(Group_Peer *peer, const uint8_t *nick, uint8_t nick_len)
+{
+ return memeq(peer->nick, peer->nick_len, nick, nick_len);
+}
+
+/**
+ * @retval false if the groupnumber is not valid.
+ * @retval true if the groupnumber is valid.
*/
+non_null()
static bool is_groupnumber_valid(const Group_Chats *g_c, uint32_t groupnumber)
{
return groupnumber < g_c->num_chats
@@ -73,11 +236,12 @@ static bool is_groupnumber_valid(const Group_Chats *g_c, uint32_t groupnumber)
}
-/** Set the size of the groupchat list to num.
+/** @brief Set the size of the groupchat list to num.
*
- * return false if realloc fails.
- * return true if it succeeds.
+ * @retval false if realloc fails.
+ * @retval true if it succeeds.
*/
+non_null()
static bool realloc_conferences(Group_Chats *g_c, uint16_t num)
{
if (num == 0) {
@@ -96,18 +260,19 @@ static bool realloc_conferences(Group_Chats *g_c, uint16_t num)
return true;
}
+non_null()
static void setup_conference(Group_c *g)
{
- memset(g, 0, sizeof(Group_c));
-
+ *g = empty_group_c;
g->maxfrozen = MAX_FROZEN_DEFAULT;
}
-/** Create a new empty groupchat connection.
+/** @brief Create a new empty groupchat connection.
*
- * return -1 on failure.
- * return groupnumber on success.
+ * @retval -1 on failure.
+ * @return groupnumber on success.
*/
+non_null()
static int32_t create_group_chat(Group_Chats *g_c)
{
for (uint16_t i = 0; i < g_c->num_chats; ++i) {
@@ -117,7 +282,7 @@ static int32_t create_group_chat(Group_Chats *g_c)
}
if (realloc_conferences(g_c, g_c->num_chats + 1)) {
- uint16_t id = g_c->num_chats;
+ const uint16_t id = g_c->num_chats;
++g_c->num_chats;
setup_conference(&g_c->chats[id]);
return id;
@@ -126,19 +291,28 @@ static int32_t create_group_chat(Group_Chats *g_c)
return -1;
}
+non_null()
+static void wipe_group_c(Group_c *g)
+{
+ free(g->frozen);
+ free(g->group);
+ crypto_memzero(g, sizeof(Group_c));
+}
-/** Wipe a groupchat.
+/** @brief Wipe a groupchat.
*
- * return true on success.
+ * @retval true on success.
*/
+non_null()
static bool wipe_group_chat(Group_Chats *g_c, uint32_t groupnumber)
{
- if (!is_groupnumber_valid(g_c, groupnumber)) {
+ if (groupnumber >= g_c->num_chats || g_c->chats == nullptr) {
return false;
}
+ wipe_group_c(&g_c->chats[groupnumber]);
+
uint16_t i;
- crypto_memzero(&g_c->chats[groupnumber], sizeof(Group_c));
for (i = g_c->num_chats; i != 0; --i) {
if (g_c->chats[i - 1].status != GROUPCHAT_STATUS_NONE) {
@@ -154,6 +328,7 @@ static bool wipe_group_chat(Group_Chats *g_c, uint32_t groupnumber)
return true;
}
+non_null()
static Group_c *get_group_c(const Group_Chats *g_c, uint32_t groupnumber)
{
if (!is_groupnumber_valid(g_c, groupnumber)) {
@@ -166,15 +341,16 @@ static Group_c *get_group_c(const Group_Chats *g_c, uint32_t groupnumber)
/**
* check if peer with real_pk is in peer array.
*
- * return peer index if peer is in group.
- * return -1 if peer is not in group.
+ * @return peer index if peer is in group.
+ * @retval -1 if peer is not in group.
*
* TODO(irungentoo): make this more efficient.
*/
+non_null()
static int peer_in_group(const Group_c *g, const uint8_t *real_pk)
{
for (uint32_t i = 0; i < g->numpeers; ++i) {
- if (id_equal(g->group[i].real_pk, real_pk)) {
+ if (pk_equal(g->group[i].real_pk, real_pk)) {
return i;
}
}
@@ -182,10 +358,11 @@ static int peer_in_group(const Group_c *g, const uint8_t *real_pk)
return -1;
}
+non_null()
static int frozen_in_group(const Group_c *g, const uint8_t *real_pk)
{
for (uint32_t i = 0; i < g->numfrozen; ++i) {
- if (id_equal(g->frozen[i].real_pk, real_pk)) {
+ if (pk_equal(g->frozen[i].real_pk, real_pk)) {
return i;
}
}
@@ -196,11 +373,12 @@ static int frozen_in_group(const Group_c *g, const uint8_t *real_pk)
/**
* check if group with the given type and id is in group array.
*
- * return group number if peer is in list.
- * return -1 if group is not in list.
+ * @return group number if peer is in list.
+ * @retval -1 if group is not in list.
*
* TODO(irungentoo): make this more efficient and maybe use constant time comparisons?
*/
+non_null()
static int32_t get_group_num(const Group_Chats *g_c, const uint8_t type, const uint8_t *id)
{
for (uint16_t i = 0; i < g_c->num_chats; ++i) {
@@ -226,11 +404,12 @@ int32_t conference_by_id(const Group_Chats *g_c, const uint8_t *id)
/**
* check if peer with peer_number is in peer array.
*
- * return peer index if peer is in chat.
- * return -1 if peer is not in chat.
+ * @return peer index if peer is in chat.
+ * @retval -1 if peer is not in chat.
*
* TODO(irungentoo): make this more efficient.
*/
+non_null()
static int get_peer_index(const Group_c *g, uint16_t peer_number)
{
for (uint32_t i = 0; i < g->numpeers; ++i) {
@@ -243,6 +422,7 @@ static int get_peer_index(const Group_c *g, uint16_t peer_number)
}
+non_null()
static uint64_t calculate_comp_value(const uint8_t *pk1, const uint8_t *pk2)
{
uint64_t cmp1 = 0;
@@ -262,22 +442,23 @@ typedef enum Groupchat_Closest_Change {
GROUPCHAT_CLOSEST_CHANGE_REMOVED,
} Groupchat_Closest_Change;
+non_null()
static bool add_to_closest(Group_c *g, const uint8_t *real_pk, const uint8_t *temp_pk)
{
- if (public_key_cmp(g->real_pk, real_pk) == 0) {
+ if (pk_equal(g->real_pk, real_pk)) {
return false;
}
unsigned int index = DESIRED_CLOSEST;
for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) {
- if (g->closest_peers[i].entry && public_key_cmp(real_pk, g->closest_peers[i].real_pk) == 0) {
+ if (g->closest_peers[i].active && pk_equal(real_pk, g->closest_peers[i].real_pk)) {
return true;
}
}
for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) {
- if (g->closest_peers[i].entry == 0) {
+ if (!g->closest_peers[i].active) {
index = i;
break;
}
@@ -288,7 +469,7 @@ static bool add_to_closest(Group_c *g, const uint8_t *real_pk, const uint8_t *te
uint64_t comp_d = 0;
for (unsigned int i = 0; i < (DESIRED_CLOSEST / 2); ++i) {
- uint64_t comp = calculate_comp_value(g->real_pk, g->closest_peers[i].real_pk);
+ const uint64_t comp = calculate_comp_value(g->real_pk, g->closest_peers[i].real_pk);
if (comp > comp_val && comp > comp_d) {
index = i;
@@ -314,15 +495,15 @@ static bool add_to_closest(Group_c *g, const uint8_t *real_pk, const uint8_t *te
uint8_t old_real_pk[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t old_temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
- uint8_t old = 0;
+ bool old = false;
- if (g->closest_peers[index].entry) {
+ if (g->closest_peers[index].active) {
memcpy(old_real_pk, g->closest_peers[index].real_pk, CRYPTO_PUBLIC_KEY_SIZE);
memcpy(old_temp_pk, g->closest_peers[index].temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
- old = 1;
+ old = true;
}
- g->closest_peers[index].entry = 1;
+ g->closest_peers[index].active = true;
memcpy(g->closest_peers[index].real_pk, real_pk, CRYPTO_PUBLIC_KEY_SIZE);
memcpy(g->closest_peers[index].temp_pk, temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
@@ -330,21 +511,22 @@ static bool add_to_closest(Group_c *g, const uint8_t *real_pk, const uint8_t *te
add_to_closest(g, old_real_pk, old_temp_pk);
}
- if (!g->changed) {
+ if (g->changed == GROUPCHAT_CLOSEST_CHANGE_NONE) {
g->changed = GROUPCHAT_CLOSEST_CHANGE_ADDED;
}
return true;
}
+non_null()
static bool pk_in_closest_peers(const Group_c *g, const uint8_t *real_pk)
{
for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) {
- if (!g->closest_peers[i].entry) {
+ if (!g->closest_peers[i].active) {
continue;
}
- if (public_key_cmp(g->closest_peers[i].real_pk, real_pk) == 0) {
+ if (pk_equal(g->closest_peers[i].real_pk, real_pk)) {
return true;
}
}
@@ -352,13 +534,15 @@ static bool pk_in_closest_peers(const Group_c *g, const uint8_t *real_pk)
return false;
}
+non_null()
static void remove_connection_reason(Group_Chats *g_c, Group_c *g, uint16_t i, uint8_t reason);
+non_null()
static void purge_closest(Group_Chats *g_c, uint32_t groupnumber)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return;
}
@@ -367,7 +551,7 @@ static void purge_closest(Group_Chats *g_c, uint32_t groupnumber)
continue;
}
- if (!(g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST)) {
+ if ((g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) == 0) {
continue;
}
@@ -380,32 +564,35 @@ static void purge_closest(Group_Chats *g_c, uint32_t groupnumber)
}
}
-static int send_packet_online(const Friend_Connections *fr_c, int friendcon_id, uint16_t group_num,
- uint8_t type, const uint8_t *id);
+non_null()
+static bool send_packet_online(const Friend_Connections *fr_c, int friendcon_id, uint16_t group_num,
+ uint8_t type, const uint8_t *id);
+non_null()
static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g, uint8_t reason,
- uint8_t lock);
+ bool lock);
+non_null(1) nullable(3)
static void add_closest_connections(Group_Chats *g_c, uint32_t groupnumber, void *userdata)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return;
}
for (uint32_t i = 0; i < DESIRED_CLOSEST; ++i) {
- if (!g->closest_peers[i].entry) {
+ if (!g->closest_peers[i].active) {
continue;
}
int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->closest_peers[i].real_pk);
- uint8_t fresh = 0;
+ bool fresh = false;
if (friendcon_id == -1) {
friendcon_id = new_friend_connection(g_c->fr_c, g->closest_peers[i].real_pk);
- fresh = 1;
+ fresh = true;
if (friendcon_id == -1) {
continue;
@@ -432,15 +619,16 @@ static void add_closest_connections(Group_Chats *g_c, uint32_t groupnumber, void
}
}
+non_null(1) nullable(3)
static bool connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *userdata)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return false;
}
- if (!g->changed) {
+ if (g->changed == GROUPCHAT_CLOSEST_CHANGE_NONE) {
return true;
}
@@ -459,6 +647,7 @@ static bool connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *use
return true;
}
+non_null()
static int get_frozen_index(const Group_c *g, uint16_t peer_number)
{
for (uint32_t i = 0; i < g->numfrozen; ++i) {
@@ -470,6 +659,7 @@ static int get_frozen_index(const Group_c *g, uint16_t peer_number)
return -1;
}
+non_null()
static bool delete_frozen(Group_c *g, uint32_t frozen_index)
{
if (frozen_index >= g->numfrozen) {
@@ -498,16 +688,17 @@ static bool delete_frozen(Group_c *g, uint32_t frozen_index)
return true;
}
-/** Update last_active timestamp on peer, and thaw the peer if it is frozen.
+/** @brief Update last_active timestamp on peer, and thaw the peer if it is frozen.
*
- * return peer index if peer is in the conference.
- * return -1 otherwise, and on error.
+ * @return peer index if peer is in the conference.
+ * @retval -1 otherwise, and on error.
*/
+non_null(1) nullable(4)
static int note_peer_active(Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_number, void *userdata)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -545,11 +736,11 @@ static int note_peer_active(Group_Chats *g_c, uint32_t groupnumber, uint16_t pee
delete_frozen(g, frozen_index);
- if (g_c->peer_list_changed_callback) {
+ if (g_c->peer_list_changed_callback != nullptr) {
g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata);
}
- if (g->peer_on_join) {
+ if (g->peer_on_join != nullptr) {
g->peer_on_join(g->object, groupnumber, thawed_index);
}
@@ -558,13 +749,15 @@ static int note_peer_active(Group_Chats *g_c, uint32_t groupnumber, uint16_t pee
return thawed_index;
}
+non_null(1) nullable(4)
static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata);
+non_null(1, 3) nullable(4)
static void delete_any_peer_with_pk(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, void *userdata)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return;
}
@@ -581,24 +774,25 @@ static void delete_any_peer_with_pk(Group_Chats *g_c, uint32_t groupnumber, cons
}
}
-/** Add a peer to the group chat, or update an existing peer.
+/** @brief Add a peer to the group chat, or update an existing peer.
*
* fresh indicates whether we should consider this information on the peer to
* be current, and so should update temp_pk and consider the peer active.
*
* do_gc_callback indicates whether we want to trigger callbacks set by the client
* via the public API. This should be set to false if this function is called
- * from outside of the tox_iterate() loop.
+ * from outside of the `tox_iterate()` loop.
*
- * return peer_index if success or peer already in chat.
- * return -1 if error.
+ * @return peer_index if success or peer already in chat.
+ * @retval -1 if error.
*/
+non_null(1, 3, 4) nullable(6)
static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk,
uint16_t peer_number, void *userdata, bool fresh, bool do_gc_callback)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -607,12 +801,12 @@ static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_p
get_peer_index(g, peer_number);
if (peer_index != -1) {
- if (!id_equal(g->group[peer_index].real_pk, real_pk)) {
+ if (!pk_equal(g->group[peer_index].real_pk, real_pk)) {
return -1;
}
if (fresh || !g->group[peer_index].temp_pk_updated) {
- id_copy(g->group[peer_index].temp_pk, temp_pk);
+ pk_copy(g->group[peer_index].temp_pk, temp_pk);
g->group[peer_index].temp_pk_updated = true;
}
@@ -623,11 +817,11 @@ static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_p
const int frozen_index = get_frozen_index(g, peer_number);
if (frozen_index != -1) {
- if (!id_equal(g->frozen[frozen_index].real_pk, real_pk)) {
+ if (!pk_equal(g->frozen[frozen_index].real_pk, real_pk)) {
return -1;
}
- id_copy(g->frozen[frozen_index].temp_pk, temp_pk);
+ pk_copy(g->frozen[frozen_index].temp_pk, temp_pk);
return -1;
}
@@ -641,35 +835,36 @@ static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_p
return -1;
}
- memset(&temp[g->numpeers], 0, sizeof(Group_Peer));
+ temp[g->numpeers] = empty_group_peer;
g->group = temp;
const uint32_t new_index = g->numpeers;
- id_copy(g->group[new_index].real_pk, real_pk);
- id_copy(g->group[new_index].temp_pk, temp_pk);
+ pk_copy(g->group[new_index].real_pk, real_pk);
+ pk_copy(g->group[new_index].temp_pk, temp_pk);
g->group[new_index].temp_pk_updated = true;
g->group[new_index].peer_number = peer_number;
g->group[new_index].last_active = mono_time_get(g_c->mono_time);
- g->group[new_index].is_friend = (getfriend_id(g_c->m, real_pk) != -1);
+ g->group[new_index].is_friend = getfriend_id(g_c->m, real_pk) != -1;
++g->numpeers;
add_to_closest(g, real_pk, temp_pk);
- if (do_gc_callback && g_c->peer_list_changed_callback) {
+ if (do_gc_callback && g_c->peer_list_changed_callback != nullptr) {
g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata);
}
- if (g->peer_on_join) {
+ if (g->peer_on_join != nullptr) {
g->peer_on_join(g->object, groupnumber, new_index);
}
return new_index;
}
+non_null()
static void remove_connection(Group_Chats *g_c, Group_c *g, uint16_t i)
{
- if (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) {
+ if ((g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) != 0) {
--g->num_introducer_connections;
}
@@ -677,12 +872,13 @@ static void remove_connection(Group_Chats *g_c, Group_c *g, uint16_t i)
g->connections[i].type = GROUPCHAT_CONNECTION_NONE;
}
+non_null()
static void remove_from_closest(Group_c *g, int peer_index)
{
for (uint32_t i = 0; i < DESIRED_CLOSEST; ++i) {
- if (g->closest_peers[i].entry
- && id_equal(g->closest_peers[i].real_pk, g->group[peer_index].real_pk)) {
- g->closest_peers[i].entry = 0;
+ if (g->closest_peers[i].active
+ && pk_equal(g->closest_peers[i].real_pk, g->group[peer_index].real_pk)) {
+ g->closest_peers[i].active = false;
g->changed = GROUPCHAT_CLOSEST_CHANGE_REMOVED;
break;
}
@@ -698,7 +894,7 @@ static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return false;
}
@@ -740,11 +936,11 @@ static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void
g->group = temp;
}
- if (g_c->peer_list_changed_callback) {
+ if (g_c->peer_list_changed_callback != nullptr) {
g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata);
}
- if (g->peer_on_leave) {
+ if (g->peer_on_leave != nullptr) {
g->peer_on_leave(g->object, groupnumber, peer_object);
}
@@ -753,10 +949,11 @@ static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void
static int cmp_u64(uint64_t a, uint64_t b)
{
- return (a > b) - (a < b);
+ return (a > b ? 1 : 0) - (a < b ? 1 : 0);
}
/** Order peers with friends first and with more recently active earlier */
+non_null()
static int cmp_frozen(const void *a, const void *b)
{
const Group_Peer *pa = (const Group_Peer *)a;
@@ -769,10 +966,11 @@ static int cmp_frozen(const void *a, const void *b)
return cmp_u64(pb->last_active, pa->last_active);
}
-/** Delete frozen peers as necessary to ensure at most g->maxfrozen remain.
+/** @brief Delete frozen peers as necessary to ensure at most `g->maxfrozen` remain.
*
- * return true if any frozen peers are removed.
+ * @retval true if any frozen peers are removed.
*/
+non_null()
static bool delete_old_frozen(Group_c *g)
{
if (g->numfrozen <= g->maxfrozen) {
@@ -801,13 +999,15 @@ static bool delete_old_frozen(Group_c *g)
return true;
}
+non_null()
static bool try_send_rejoin(Group_Chats *g_c, Group_c *g, const uint8_t *real_pk);
+non_null(1) nullable(4)
static bool freeze_peer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return false;
}
@@ -835,14 +1035,15 @@ static bool freeze_peer(Group_Chats *g_c, uint32_t groupnumber, int peer_index,
}
-/** Set the nick for a peer.
+/** @brief Set the nick for a peer.
*
* do_gc_callback indicates whether we want to trigger callbacks set by the client
* via the public API. This should be set to false if this function is called
- * from outside of the tox_iterate() loop.
+ * from outside of the `tox_iterate()` loop.
*
- * return true on success.
+ * @retval true on success.
*/
+non_null(1, 4) nullable(6)
static bool setnick(Group_Chats *g_c, uint32_t groupnumber, int peer_index, const uint8_t *nick, uint16_t nick_len,
void *userdata, bool do_gc_callback)
{
@@ -852,35 +1053,35 @@ static bool setnick(Group_Chats *g_c, uint32_t groupnumber, int peer_index, cons
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return false;
}
g->group[peer_index].nick_updated = true;
- if (g->group[peer_index].nick_len == nick_len
- && (nick_len == 0 || !memcmp(g->group[peer_index].nick, nick, nick_len))) {
+ if (g_peer_nick_eq(&g->group[peer_index], nick, nick_len)) {
/* same name as already stored */
return true;
}
- if (nick_len) {
+ if (nick_len > 0) {
memcpy(g->group[peer_index].nick, nick, nick_len);
}
g->group[peer_index].nick_len = nick_len;
- if (do_gc_callback && g_c->peer_name_callback) {
+ if (do_gc_callback && g_c->peer_name_callback != nullptr) {
g_c->peer_name_callback(g_c->m, groupnumber, peer_index, nick, nick_len, userdata);
}
return true;
}
-/** Set the title for a group.
+/** @brief Set the title for a group.
*
- * return true on success.
+ * @retval true on success.
*/
+non_null(1, 4) nullable(6)
static bool settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, const uint8_t *title, uint8_t title_len,
void *userdata)
{
@@ -890,11 +1091,11 @@ static bool settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, con
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return false;
}
- if (g->title_len == title_len && !memcmp(g->title, title, title_len)) {
+ if (g_title_eq(g, title, title_len)) {
/* same title as already set */
return true;
}
@@ -904,7 +1105,7 @@ static bool settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, con
g->title_fresh = true;
- if (g_c->title_callback) {
+ if (g_c->title_callback != nullptr) {
g_c->title_callback(g_c->m, groupnumber, peer_index, title, title_len, userdata);
}
@@ -912,11 +1113,12 @@ static bool settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, con
}
/** Check if the group has no online connection, and freeze all peers if so */
+non_null(1) nullable(3)
static void check_disconnected(Group_Chats *g_c, uint32_t groupnumber, void *userdata)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return;
}
@@ -927,18 +1129,19 @@ static void check_disconnected(Group_Chats *g_c, uint32_t groupnumber, void *use
}
for (uint32_t i = 0; i < g->numpeers; ++i) {
- while (i < g->numpeers && !id_equal(g->group[i].real_pk, g->real_pk)) {
+ while (i < g->numpeers && !pk_equal(g->group[i].real_pk, g->real_pk)) {
freeze_peer(g_c, groupnumber, i, userdata);
}
}
}
+non_null(1) nullable(5)
static void set_conns_type_connections(Group_Chats *g_c, uint32_t groupnumber, int friendcon_id, uint8_t type,
void *userdata)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return;
}
@@ -961,6 +1164,7 @@ static void set_conns_type_connections(Group_Chats *g_c, uint32_t groupnumber, i
}
/** Set the type for all connections with friendcon_id */
+non_null(1) nullable(4)
static void set_conns_status_groups(Group_Chats *g_c, int friendcon_id, uint8_t type, void *userdata)
{
for (uint16_t i = 0; i < g_c->num_chats; ++i) {
@@ -968,6 +1172,7 @@ static void set_conns_status_groups(Group_Chats *g_c, int friendcon_id, uint8_t
}
}
+non_null()
static void rejoin_frozen_friend(Group_Chats *g_c, int friendcon_id)
{
uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
@@ -976,12 +1181,12 @@ static void rejoin_frozen_friend(Group_Chats *g_c, int friendcon_id)
for (uint16_t i = 0; i < g_c->num_chats; ++i) {
Group_c *g = get_group_c(g_c, i);
- if (!g) {
+ if (g == nullptr) {
continue;
}
for (uint32_t j = 0; j < g->numfrozen; ++j) {
- if (id_equal(g->frozen[j].real_pk, real_pk)) {
+ if (pk_equal(g->frozen[j].real_pk, real_pk)) {
try_send_rejoin(g_c, g, real_pk);
break;
}
@@ -989,7 +1194,8 @@ static void rejoin_frozen_friend(Group_Chats *g_c, int friendcon_id)
}
}
-static int g_handle_any_status(void *object, int friendcon_id, uint8_t status, void *userdata)
+non_null(1) nullable(4)
+static int g_handle_any_status(void *object, int friendcon_id, bool status, void *userdata)
{
Group_Chats *g_c = (Group_Chats *)object;
@@ -1000,7 +1206,8 @@ static int g_handle_any_status(void *object, int friendcon_id, uint8_t status, v
return 0;
}
-static int g_handle_status(void *object, int friendcon_id, uint8_t status, void *userdata)
+non_null(1) nullable(4)
+static int g_handle_status(void *object, int friendcon_id, bool status, void *userdata)
{
Group_Chats *g_c = (Group_Chats *)object;
@@ -1014,16 +1221,18 @@ static int g_handle_status(void *object, int friendcon_id, uint8_t status, void
return 0;
}
+non_null(1, 3) nullable(5)
static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata);
+non_null(1, 3) nullable(5)
static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata);
-/** Add friend to group chat.
+/** @brief Add friend to group chat.
*
- * return connections index on success
- * return -1 on failure.
+ * @return connections index on success
+ * @retval -1 on failure.
*/
static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g, uint8_t reason,
- uint8_t lock)
+ bool lock)
{
uint16_t empty = MAX_GROUP_CONNECTIONS;
uint16_t ind = MAX_GROUP_CONNECTIONS;
@@ -1058,7 +1267,7 @@ static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g,
ind = empty;
}
- if (!(g->connections[ind].reasons & reason)) {
+ if ((g->connections[ind].reasons & reason) == 0) {
g->connections[ind].reasons |= reason;
if (reason == GROUPCHAT_CONNECTION_REASON_INTRODUCER) {
@@ -1069,15 +1278,16 @@ static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g,
return ind;
}
-static unsigned int send_peer_introduced(const Group_Chats *g_c, int friendcon_id, uint16_t group_num);
+non_null()
+static bool send_peer_introduced(const Group_Chats *g_c, int friendcon_id, uint16_t group_num);
-/** Removes reason for keeping connection.
+/** @brief Removes reason for keeping connection.
*
* Kills connection if this was the last reason.
*/
static void remove_connection_reason(Group_Chats *g_c, Group_c *g, uint16_t i, uint8_t reason)
{
- if (!(g->connections[i].reasons & reason)) {
+ if ((g->connections[i].reasons & reason) == 0) {
return;
}
@@ -1097,14 +1307,15 @@ static void remove_connection_reason(Group_Chats *g_c, Group_c *g, uint16_t i, u
}
}
-/** Creates a new groupchat and puts it in the chats array.
+/** @brief Creates a new groupchat and puts it in the chats array.
*
- * type is one of `GROUPCHAT_TYPE_*`
+ * @param rng Random number generator used for generating the group ID.
+ * @param type is one of `GROUPCHAT_TYPE_*`
*
- * return group number on success.
- * return -1 on failure.
+ * @return group number on success.
+ * @retval -1 on failure.
*/
-int add_groupchat(Group_Chats *g_c, uint8_t type)
+int add_groupchat(Group_Chats *g_c, const Random *rng, uint8_t type)
{
const int32_t groupnumber = create_group_chat(g_c);
@@ -1116,7 +1327,7 @@ int add_groupchat(Group_Chats *g_c, uint8_t type)
g->status = GROUPCHAT_STATUS_CONNECTED;
g->type = type;
- new_symmetric_key(g->id);
+ new_symmetric_key(rng, g->id);
g->peer_number = 0; /* Founder is peer 0. */
memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE);
const int peer_index = addpeer(g_c, groupnumber, g->real_pk, dht_get_self_public_key(g_c->m->dht), 0, nullptr, true,
@@ -1131,20 +1342,21 @@ int add_groupchat(Group_Chats *g_c, uint8_t type)
return groupnumber;
}
+non_null()
static bool group_leave(const Group_Chats *g_c, uint32_t groupnumber, bool permanent);
-/** Delete a groupchat from the chats array, informing the group first as
+/** @brief Delete a groupchat from the chats array, informing the group first as
* appropriate.
*
- * return 0 on success.
- * return -1 if groupnumber is invalid.
+ * @retval true on success.
+ * @retval false if groupnumber is invalid.
*/
-int del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently)
+bool del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
- return -1;
+ if (g == nullptr) {
+ return false;
}
group_leave(g_c, groupnumber, leave_permanently);
@@ -1159,21 +1371,19 @@ int del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently
}
for (uint32_t i = 0; i < g->numpeers; ++i) {
- if (g->peer_on_leave) {
+ if (g->peer_on_leave != nullptr) {
g->peer_on_leave(g->object, groupnumber, g->group[i].object);
}
}
- free(g->group);
- free(g->frozen);
-
- if (g->group_on_delete) {
+ if (g->group_on_delete != nullptr) {
g->group_on_delete(g->object, groupnumber);
}
return wipe_group_chat(g_c, groupnumber);
}
+non_null()
static const Group_Peer *peer_in_list(const Group_c *g, uint32_t peernumber, bool frozen)
{
const Group_Peer *list = frozen ? g->frozen : g->group;
@@ -1187,18 +1397,21 @@ static const Group_Peer *peer_in_list(const Group_c *g, uint32_t peernumber, boo
}
-/** Copy the public key of (frozen, if frozen is true) peernumber who is in
- * groupnumber to pk. pk must be CRYPTO_PUBLIC_KEY_SIZE long.
+/**
+ * @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.
*
- * return 0 on success
- * return -1 if groupnumber is invalid.
- * return -2 if peernumber is invalid.
+ * @retval 0 on success
+ * @retval -1 if groupnumber is invalid.
+ * @retval -2 if peernumber is invalid.
*/
int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *pk, bool frozen)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1213,16 +1426,16 @@ int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, uint32_t pee
}
/**
- * Return the size of (frozen, if frozen is true) peernumber's name.
+ * @brief Return the size of (frozen, if frozen is true) peernumber's name.
*
- * return -1 if groupnumber is invalid.
- * return -2 if peernumber is invalid.
+ * @retval -1 if groupnumber is invalid.
+ * @retval -2 if peernumber is invalid.
*/
int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, bool frozen)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1235,18 +1448,21 @@ int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, uint32_t p
return peer->nick_len;
}
-/** Copy the name of (frozen, if frozen is true) peernumber who is in
- * groupnumber to name. name must be at least MAX_NAME_LENGTH long.
+/**
+ * @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
- * return -1 if groupnumber is invalid.
- * return -2 if peernumber is invalid.
+ * @return length of name if success
+ * @retval -1 if groupnumber is invalid.
+ * @retval -2 if peernumber is invalid.
*/
int group_peername(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *name, bool frozen)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1263,19 +1479,20 @@ int group_peername(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernu
return peer->nick_len;
}
-/** Copy last active timestamp of frozen peernumber who is in groupnumber to
- * last_active.
+/**
+ * @brief Copy last active timestamp of frozen peernumber who is in groupnumber to
+ * last_active.
*
- * return 0 on success.
- * return -1 if groupnumber is invalid.
- * return -2 if peernumber is invalid.
+ * @retval 0 on success.
+ * @retval -1 if groupnumber is invalid.
+ * @retval -2 if peernumber is invalid.
*/
int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber,
uint64_t *last_active)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1287,16 +1504,16 @@ int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, uint3
return 0;
}
-/** Set maximum number of frozen peers.
+/** @brief Set maximum number of frozen peers.
*
- * return 0 on success.
- * return -1 if groupnumber is invalid.
+ * @retval 0 on success.
+ * @retval -1 if groupnumber is invalid.
*/
int group_set_max_frozen(const Group_Chats *g_c, uint32_t groupnumber, uint32_t maxfrozen)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1305,32 +1522,33 @@ int group_set_max_frozen(const Group_Chats *g_c, uint32_t groupnumber, uint32_t
return 0;
}
-/** Return the number of (frozen, if frozen is true) peers in the group chat on
- * success.
- * return -1 if groupnumber is invalid.
+/**
+ * @return the number of (frozen, if frozen is true) peers in the group chat on success.
+ * @retval -1 if groupnumber is invalid.
*/
int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber, bool frozen)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
return frozen ? g->numfrozen : g->numpeers;
}
-/** return 1 if the peernumber corresponds to ours.
- * return 0 if the peernumber is not ours.
- * return -1 if groupnumber is invalid.
- * return -2 if peernumber is invalid.
- * return -3 if we are not connected to the group chat.
+/**
+ * @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.
*/
int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1342,35 +1560,35 @@ int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, uint3
return -3;
}
- return g->peer_number == g->group[peernumber].peer_number;
+ return (g->peer_number == g->group[peernumber].peer_number) ? 1 : 0;
}
-/** return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is.
+/** @brief return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is.
*
- * return -1 on failure.
- * return type on success.
+ * @retval -1 on failure.
+ * @return type on success.
*/
int group_get_type(const Group_Chats *g_c, uint32_t groupnumber)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
return g->type;
}
-/** Copies the unique id of `group_chat[groupnumber]` into `id`.
+/** @brief Copies the unique id of `group_chat[groupnumber]` into `id`.
*
- * return false on failure.
- * return true on success.
+ * @retval false on failure.
+ * @retval true on success.
*/
bool conference_get_id(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *id)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return false;
}
@@ -1381,16 +1599,17 @@ bool conference_get_id(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *id
return true;
}
-/** Send a group packet to friendcon_id.
+/** @brief Send a group packet to friendcon_id.
*
- * return 1 on success
- * return 0 on failure
+ * @retval true on success
+ * @retval false on failure
*/
-static unsigned int send_packet_group_peer(const Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id,
- uint16_t group_num, const uint8_t *data, uint16_t length)
+non_null()
+static bool send_packet_group_peer(const Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id,
+ uint16_t group_num, const uint8_t *data, uint16_t length)
{
if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE) {
- return 0;
+ return false;
}
group_num = net_htons(group_num);
@@ -1399,19 +1618,20 @@ static unsigned int send_packet_group_peer(const Friend_Connections *fr_c, int f
memcpy(packet + 1, &group_num, sizeof(uint16_t));
memcpy(packet + 1 + sizeof(uint16_t), data, length);
return write_cryptpacket(friendconn_net_crypto(fr_c), friend_connection_crypt_connection_id(fr_c, friendcon_id), packet,
- SIZEOF_VLA(packet), 0) != -1;
+ SIZEOF_VLA(packet), false) != -1;
}
-/** Send a group lossy packet to friendcon_id.
+/** @brief Send a group lossy packet to friendcon_id.
*
- * return 1 on success
- * return 0 on failure
+ * @retval true on success
+ * @retval false on failure
*/
-static unsigned int send_lossy_group_peer(const Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id,
- uint16_t group_num, const uint8_t *data, uint16_t length)
+non_null()
+static bool send_lossy_group_peer(const Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id,
+ uint16_t group_num, const uint8_t *data, uint16_t length)
{
if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE) {
- return 0;
+ return false;
}
group_num = net_htons(group_num);
@@ -1423,18 +1643,18 @@ static unsigned int send_lossy_group_peer(const Friend_Connections *fr_c, int fr
packet, SIZEOF_VLA(packet)) != -1;
}
-/** invite friendnumber to groupnumber.
+/** @brief invite friendnumber to groupnumber.
*
- * return 0 on success.
- * return -1 if groupnumber is invalid.
- * return -2 if invite packet failed to send.
- * return -3 if we are not connected to the group chat.
+ * @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.
*/
int invite_friend(const Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1456,9 +1676,9 @@ int invite_friend(const Group_Chats *g_c, uint32_t friendnumber, uint32_t groupn
return -2;
}
-/** Send a rejoin packet to a peer if we have a friend connection to the peer.
- * return true if a packet was sent.
- * return false otherwise.
+/** @brief Send a rejoin packet to a peer if we have a friend connection to the peer.
+ * @retval true if a packet was sent.
+ * @retval false otherwise.
*/
static bool try_send_rejoin(Group_Chats *g_c, Group_c *g, const uint8_t *real_pk)
{
@@ -1474,32 +1694,34 @@ static bool try_send_rejoin(Group_Chats *g_c, Group_c *g, const uint8_t *real_pk
memcpy(packet + 2, g->id, GROUP_ID_LENGTH);
if (write_cryptpacket(friendconn_net_crypto(g_c->fr_c), friend_connection_crypt_connection_id(g_c->fr_c, friendcon_id),
- packet, sizeof(packet), 0) == -1) {
+ packet, sizeof(packet), false) == -1) {
return false;
}
- add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, 1);
+ add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, true);
return true;
}
-static unsigned int send_peer_query(const Group_Chats *g_c, int friendcon_id, uint16_t group_num);
+non_null()
+static bool send_peer_query(const Group_Chats *g_c, int friendcon_id, uint16_t group_num);
+non_null()
static bool send_invite_response(Group_Chats *g_c, int groupnumber, uint32_t friendnumber, const uint8_t *data,
uint16_t length);
-/** Join a group (we need to have been invited first.)
+/** @brief Join a group (we need to have been invited first).
*
- * expected_type is the groupchat type we expect the chat we are joining to
- * have.
+ * @param expected_type is the groupchat type we expect the chat we are joining
+ * to have.
*
- * return group number on success.
- * return -1 if data length is invalid.
- * return -2 if group is not the expected type.
- * return -3 if friendnumber is invalid.
- * return -4 if client is already in this group.
- * return -5 if group instance failed to initialize.
- * return -6 if join packet fails to send.
+ * @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.
*/
int join_groupchat(Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_type, const uint8_t *data, uint16_t length)
{
@@ -1578,10 +1800,10 @@ static bool send_invite_response(Group_Chats *g_c, int groupnumber, uint32_t fri
return false;
}
- const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, 1);
+ const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, true);
if (member) {
- add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCING, 0);
+ add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCING, false);
}
if (connection_index != -1) {
@@ -1597,7 +1819,7 @@ static bool send_invite_response(Group_Chats *g_c, int groupnumber, uint32_t fri
/** Set handlers for custom lossy packets. */
void group_lossy_packet_registerhandler(Group_Chats *g_c, uint8_t byte, lossy_packet_cb *function)
{
- g_c->lossy_packethandlers[byte].function = function;
+ g_c->lossy_packethandlers[byte] = function;
}
/** Set the callback for group invites. */
@@ -1618,7 +1840,7 @@ void g_callback_group_message(Group_Chats *g_c, g_conference_message_cb *functio
g_c->message_callback = function;
}
-/** Set callback function for peer nickname changes.
+/** @brief Set callback function for peer nickname changes.
*
* It gets called every time a peer changes their nickname.
*/
@@ -1627,7 +1849,7 @@ void g_callback_peer_name(Group_Chats *g_c, peer_name_cb *function)
g_c->peer_name_callback = function;
}
-/** Set callback function for peer list changes.
+/** @brief Set callback function for peer list changes.
*
* It gets called every time the name list changes(new peer, deleted peer)
*/
@@ -1642,16 +1864,16 @@ void g_callback_group_title(Group_Chats *g_c, title_cb *function)
g_c->title_callback = function;
}
-/** Set a function to be called when a new peer joins a group chat.
+/** @brief Set a function to be called when a new peer joins a group chat.
*
- * return 0 on success.
- * return -1 on failure.
+ * @retval 0 on success.
+ * @retval -1 on failure.
*/
int callback_groupchat_peer_new(const Group_Chats *g_c, uint32_t groupnumber, peer_on_join_cb *function)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1659,16 +1881,16 @@ int callback_groupchat_peer_new(const Group_Chats *g_c, uint32_t groupnumber, pe
return 0;
}
-/** Set a function to be called when a peer leaves a group chat.
+/** @brief Set a function to be called when a peer leaves a group chat.
*
- * return 0 on success.
- * return -1 on failure.
+ * @retval 0 on success.
+ * @retval -1 on failure.
*/
int callback_groupchat_peer_delete(const Group_Chats *g_c, uint32_t groupnumber, peer_on_leave_cb *function)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1676,16 +1898,16 @@ int callback_groupchat_peer_delete(const Group_Chats *g_c, uint32_t groupnumber,
return 0;
}
-/** Set a function to be called when the group chat is deleted.
+/** @brief Set a function to be called when the group chat is deleted.
*
- * return 0 on success.
- * return -1 on failure.
+ * @retval 0 on success.
+ * @retval -1 on failure.
*/
int callback_groupchat_delete(const Group_Chats *g_c, uint32_t groupnumber, group_on_delete_cb *function)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1693,24 +1915,23 @@ int callback_groupchat_delete(const Group_Chats *g_c, uint32_t groupnumber, grou
return 0;
}
+non_null(1) nullable(4)
static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint8_t message_id, const uint8_t *data,
uint16_t len);
-/** send a ping message
+/** @brief send a ping message
* return true on success
*/
+non_null()
static bool group_ping_send(const Group_Chats *g_c, uint32_t groupnumber)
{
- if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_PING_ID, nullptr, 0) > 0) {
- return true;
- }
-
- return false;
+ return send_message_group(g_c, groupnumber, GROUP_MESSAGE_PING_ID, nullptr, 0) > 0;
}
-/** send a new_peer message
+/** @brief send a new_peer message
* return true on success
*/
+non_null()
static bool group_new_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_num, const uint8_t *real_pk,
const uint8_t *temp_pk)
{
@@ -1721,16 +1942,13 @@ static bool group_new_peer_send(const Group_Chats *g_c, uint32_t groupnumber, ui
memcpy(packet + sizeof(uint16_t), real_pk, CRYPTO_PUBLIC_KEY_SIZE);
memcpy(packet + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE, temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
- if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_NEW_PEER_ID, packet, sizeof(packet)) > 0) {
- return true;
- }
-
- return false;
+ return send_message_group(g_c, groupnumber, GROUP_MESSAGE_NEW_PEER_ID, packet, sizeof(packet)) > 0;
}
-/** send a kill_peer message
+/** @brief send a kill_peer message
* return true on success
*/
+non_null()
static bool group_kill_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_num)
{
uint8_t packet[GROUP_MESSAGE_KILL_PEER_LENGTH];
@@ -1738,16 +1956,13 @@ static bool group_kill_peer_send(const Group_Chats *g_c, uint32_t groupnumber, u
peer_num = net_htons(peer_num);
memcpy(packet, &peer_num, sizeof(uint16_t));
- if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_KILL_PEER_ID, packet, sizeof(packet)) > 0) {
- return true;
- }
-
- return false;
+ return send_message_group(g_c, groupnumber, GROUP_MESSAGE_KILL_PEER_ID, packet, sizeof(packet)) > 0;
}
-/** send a freeze_peer message
+/** @brief send a freeze_peer message
* return true on success
*/
+non_null()
static bool group_freeze_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_num)
{
uint8_t packet[GROUP_MESSAGE_KILL_PEER_LENGTH];
@@ -1755,37 +1970,30 @@ static bool group_freeze_peer_send(const Group_Chats *g_c, uint32_t groupnumber,
peer_num = net_htons(peer_num);
memcpy(packet, &peer_num, sizeof(uint16_t));
- if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_FREEZE_PEER_ID, packet, sizeof(packet)) > 0) {
- return true;
- }
-
- return false;
+ return send_message_group(g_c, groupnumber, GROUP_MESSAGE_FREEZE_PEER_ID, packet, sizeof(packet)) > 0;
}
-/** send a name message
+/** @brief send a name message
* return true on success
*/
+non_null()
static bool group_name_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *nick, uint16_t nick_len)
{
if (nick_len > MAX_NAME_LENGTH) {
return false;
}
- if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_NAME_ID, nick, nick_len) > 0) {
- return true;
- }
-
- return false;
+ return send_message_group(g_c, groupnumber, GROUP_MESSAGE_NAME_ID, nick, nick_len) > 0;
}
-/** send message to announce leaving group
+/** @brief send message to announce leaving group
* return true on success
*/
static bool group_leave(const Group_Chats *g_c, uint32_t groupnumber, bool permanent)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return false;
}
@@ -1797,17 +2005,17 @@ static bool group_leave(const Group_Chats *g_c, uint32_t groupnumber, bool perma
}
-/** set the group's title, limited to MAX_NAME_LENGTH
- * return 0 on success
- * return -1 if groupnumber is invalid.
- * return -2 if title is too long or empty.
- * return -3 if packet fails to send.
+/** @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.
*/
int group_title_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *title, uint8_t title_len)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1816,7 +2024,7 @@ int group_title_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t
}
/* same as already set? */
- if (g->title_len == title_len && !memcmp(g->title, title, title_len)) {
+ if (g_title_eq(g, title, title_len)) {
return 0;
}
@@ -1834,15 +2042,15 @@ int group_title_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t
return -3;
}
-/** return the group's title size.
- * return -1 of groupnumber is invalid.
- * return -2 if title is too long or empty.
+/** @brief return the group's title size.
+ * @retval -1 of groupnumber is invalid.
+ * @retval -2 if title is too long or empty.
*/
int group_title_get_size(const Group_Chats *g_c, uint32_t groupnumber)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1853,18 +2061,19 @@ int group_title_get_size(const Group_Chats *g_c, uint32_t groupnumber)
return g->title_len;
}
-/** Get group title from groupnumber and put it in title.
+/** @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.
- * return -1 if groupnumber is invalid.
- * return -2 if title is too long or empty.
+ * @return length of copied title if success.
+ * @retval -1 if groupnumber is invalid.
+ * @retval -2 if title is too long or empty.
*/
int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -1876,6 +2085,7 @@ int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title
return g->title_len;
}
+non_null()
static bool get_peer_number(const Group_c *g, const uint8_t *real_pk, uint16_t *peer_number)
{
const int peer_index = peer_in_group(g, real_pk);
@@ -1895,6 +2105,7 @@ static bool get_peer_number(const Group_c *g, const uint8_t *real_pk, uint16_t *
return false;
}
+non_null(1, 3) nullable(5)
static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length,
void *userdata)
{
@@ -1904,9 +2115,6 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con
return;
}
- const uint8_t *invite_data = data + 1;
- const uint16_t invite_length = length - 1;
-
switch (data[0]) {
case INVITE_ID: {
if (length != INVITE_PACKET_SIZE) {
@@ -1915,8 +2123,11 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con
const int groupnumber = get_group_num(g_c, data[1 + sizeof(uint16_t)], data + 1 + sizeof(uint16_t) + 1);
+ const uint8_t *invite_data = data + 1;
+ const uint16_t invite_length = length - 1;
+
if (groupnumber == -1) {
- if (g_c->invite_callback) {
+ if (g_c->invite_callback != nullptr) {
g_c->invite_callback(m, friendnumber, invite_data[sizeof(uint16_t)], invite_data, invite_length, userdata);
}
@@ -1924,7 +2135,7 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con
} else {
const Group_c *g = get_group_c(g_c, groupnumber);
- if (g && g->status == GROUPCHAT_STATUS_CONNECTED) {
+ if (g != nullptr && g->status == GROUPCHAT_STATUS_CONNECTED) {
send_invite_response(g_c, groupnumber, friendnumber, invite_data, invite_length);
}
}
@@ -1947,7 +2158,7 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con
Group_c *g = get_group_c(g_c, groupnum);
- if (!g) {
+ if (g == nullptr) {
return;
}
@@ -1967,12 +2178,12 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con
/* TODO(irungentoo): what if two people enter the group at the
* same time and are given the same peer_number by different
* nodes? */
- peer_number = random_u16();
+ peer_number = random_u16(m->rng);
unsigned int tries = 0;
while (get_peer_index(g, peer_number) != -1 || get_frozen_index(g, peer_number) != -1) {
- peer_number = random_u16();
+ peer_number = random_u16(m->rng);
++tries;
if (tries > 32) {
@@ -1994,10 +2205,10 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con
addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true, true);
const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g,
- GROUPCHAT_CONNECTION_REASON_INTRODUCING, 1);
+ GROUPCHAT_CONNECTION_REASON_INTRODUCING, true);
if (member) {
- add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, 0);
+ add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, false);
send_peer_query(g_c, friendcon_id, other_groupnum);
}
@@ -2017,11 +2228,12 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con
}
}
-/** Find index of friend in the connections list.
+/** @brief Find index of friend in the connections list.
*
* return index on success
* return -1 on failure.
*/
+non_null()
static int friend_in_connections(const Group_c *g, int friendcon_id)
{
for (unsigned int i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
@@ -2037,8 +2249,8 @@ static int friend_in_connections(const Group_c *g, int friendcon_id)
return -1;
}
-/** return number of connections.
- */
+/** return number of connections. */
+non_null()
static unsigned int count_connected(const Group_c *g)
{
unsigned int count = 0;
@@ -2052,8 +2264,8 @@ static unsigned int count_connected(const Group_c *g)
return count;
}
-static int send_packet_online(const Friend_Connections *fr_c, int friendcon_id, uint16_t group_num,
- uint8_t type, const uint8_t *id)
+static bool send_packet_online(const Friend_Connections *fr_c, int friendcon_id, uint16_t group_num,
+ uint8_t type, const uint8_t *id)
{
uint8_t packet[1 + ONLINE_PACKET_DATA_SIZE];
group_num = net_htons(group_num);
@@ -2062,11 +2274,13 @@ static int send_packet_online(const Friend_Connections *fr_c, int friendcon_id,
packet[1 + sizeof(uint16_t)] = type;
memcpy(packet + 1 + sizeof(uint16_t) + 1, id, GROUP_ID_LENGTH);
return write_cryptpacket(friendconn_net_crypto(fr_c), friend_connection_crypt_connection_id(fr_c, friendcon_id), packet,
- sizeof(packet), 0) != -1;
+ sizeof(packet), false) != -1;
}
+non_null()
static bool ping_groupchat(const Group_Chats *g_c, uint32_t groupnumber);
+non_null()
static int handle_packet_online(const Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length)
{
if (length != ONLINE_PACKET_DATA_SIZE) {
@@ -2085,7 +2299,7 @@ static int handle_packet_online(const Group_Chats *g_c, int friendcon_id, const
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -2099,7 +2313,7 @@ static int handle_packet_online(const Group_Chats *g_c, int friendcon_id, const
return -1;
}
- if (count_connected(g) == 0 || (g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER)) {
+ if (count_connected(g) == 0 || (g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) != 0) {
send_peer_query(g_c, friendcon_id, other_groupnum);
}
@@ -2107,7 +2321,7 @@ static int handle_packet_online(const Group_Chats *g_c, int friendcon_id, const
g->connections[index].type = GROUPCHAT_CONNECTION_ONLINE;
send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id);
- if (g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCING) {
+ if ((g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCING) != 0) {
uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id);
@@ -2126,6 +2340,7 @@ static int handle_packet_online(const Group_Chats *g_c, int friendcon_id, const
return 0;
}
+non_null(1, 3) nullable(5)
static int handle_packet_rejoin(Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length,
void *userdata)
{
@@ -2137,7 +2352,7 @@ static int handle_packet_rejoin(Group_Chats *g_c, int friendcon_id, const uint8_
Group_c *g = get_group_c(g_c, groupnum);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -2153,7 +2368,7 @@ static int handle_packet_rejoin(Group_Chats *g_c, int friendcon_id, const uint8_
addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true, true);
const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g,
- GROUPCHAT_CONNECTION_REASON_INTRODUCING, 1);
+ GROUPCHAT_CONNECTION_REASON_INTRODUCING, true);
if (connection_index != -1) {
send_packet_online(g_c->fr_c, friendcon_id, groupnum, g->type, g->id);
@@ -2165,10 +2380,11 @@ static int handle_packet_rejoin(Group_Chats *g_c, int friendcon_id, const uint8_
// we could send title with invite, but then if it changes between sending and accepting inv, joinee won't see it
-/** return 1 on success.
- * return 0 on failure
+/**
+ * @retval true on success.
+ * @retval false on failure
*/
-static unsigned int send_peer_introduced(const Group_Chats *g_c, int friendcon_id, uint16_t group_num)
+static bool send_peer_introduced(const Group_Chats *g_c, int friendcon_id, uint16_t group_num)
{
uint8_t packet[1];
packet[0] = PEER_INTRODUCED_ID;
@@ -2176,19 +2392,22 @@ static unsigned int send_peer_introduced(const Group_Chats *g_c, int friendcon_i
}
-/** return 1 on success.
- * return 0 on failure
+/**
+ * @retval true on success.
+ * @retval false on failure
*/
-static unsigned int send_peer_query(const Group_Chats *g_c, int friendcon_id, uint16_t group_num)
+static bool send_peer_query(const Group_Chats *g_c, int friendcon_id, uint16_t group_num)
{
uint8_t packet[1];
packet[0] = PEER_QUERY_ID;
return send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, packet, sizeof(packet));
}
-/** return number of peers sent on success.
- * return 0 on failure.
+/**
+ * @return number of peers sent on success.
+ * @retval 0 on failure.
*/
+non_null()
static unsigned int send_peers(const Group_Chats *g_c, const Group_c *g, int friendcon_id, uint16_t group_num)
{
uint8_t response_packet[MAX_CRYPTO_DATA_SIZE - (1 + sizeof(uint16_t))];
@@ -2228,7 +2447,7 @@ static unsigned int send_peers(const Group_Chats *g_c, const Group_c *g, int fri
p += g->group[i].nick_len;
}
- if (g->title_len) {
+ if (g->title_len > 0) {
VLA(uint8_t, title_packet, 1 + g->title_len);
title_packet[0] = PEER_TITLE_ID;
memcpy(title_packet + 1, g->title, g->title_len);
@@ -2239,6 +2458,7 @@ static unsigned int send_peers(const Group_Chats *g_c, const Group_c *g, int fri
return sent;
}
+non_null(1, 3) nullable(5)
static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length,
void *userdata)
{
@@ -2248,7 +2468,7 @@ static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -2261,11 +2481,11 @@ static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8
d += sizeof(uint16_t);
if (g->status == GROUPCHAT_STATUS_VALID
- && public_key_cmp(d, nc_get_self_public_key(g_c->m->net_crypto)) == 0) {
+ && pk_equal(d, nc_get_self_public_key(g_c->m->net_crypto))) {
g->peer_number = peer_num;
g->status = GROUPCHAT_STATUS_CONNECTED;
- if (g_c->connected_callback) {
+ if (g_c->connected_callback != nullptr) {
g_c->connected_callback(g_c->m, groupnumber, userdata);
}
@@ -2296,6 +2516,7 @@ static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8
return 0;
}
+non_null(1, 3) nullable(6)
static void handle_direct_packet(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length,
int connection_index, void *userdata)
{
@@ -2305,7 +2526,7 @@ static void handle_direct_packet(Group_Chats *g_c, uint32_t groupnumber, const u
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return;
}
@@ -2341,11 +2562,13 @@ static void handle_direct_packet(Group_Chats *g_c, uint32_t groupnumber, const u
}
}
-/** Send message to all connections except receiver (if receiver isn't -1)
+/** @brief Send message to all connections except receiver (if receiver isn't -1)
+ *
* NOTE: this function appends the group chat number to the data passed to it.
*
- * return number of messages sent.
+ * @return number of messages sent.
*/
+non_null()
static unsigned int send_message_all_connections(const Group_Chats *g_c, const Group_c *g, const uint8_t *data,
uint16_t length, int receiver)
{
@@ -2369,11 +2592,13 @@ static unsigned int send_message_all_connections(const Group_Chats *g_c, const G
return sent;
}
-/** Send lossy message to all connections except receiver (if receiver isn't -1)
+/** @brief Send lossy message to all connections except receiver (if receiver isn't -1)
+ *
* NOTE: this function appends the group chat number to the data passed to it.
*
- * return number of messages sent.
+ * @return number of messages sent.
*/
+non_null()
static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Group_c *g, const uint8_t *data,
uint16_t length, int receiver)
{
@@ -2390,7 +2615,7 @@ static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Gro
continue;
}
- if (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) {
+ if ((g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) != 0) {
connected_closest[num_connected_closest] = i;
++num_connected_closest;
continue;
@@ -2402,7 +2627,7 @@ static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Gro
}
}
- if (!num_connected_closest) {
+ if (num_connected_closest == 0) {
return sent;
}
@@ -2415,7 +2640,7 @@ static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Gro
const uint64_t comp_val = calculate_comp_value(g->real_pk, real_pk);
for (uint8_t j = 0; j < 2; ++j) {
- if (j ? (comp_val > comp_val_old[j]) : (comp_val < comp_val_old[j])) {
+ if (j > 0 ? (comp_val > comp_val_old[j]) : (comp_val < comp_val_old[j])) {
to_send[j] = connected_closest[i];
comp_val_old[j] = comp_val;
}
@@ -2423,7 +2648,7 @@ static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Gro
}
for (uint8_t j = 0; j < 2; ++j) {
- if (j && to_send[1] == to_send[0]) {
+ if (j > 0 && to_send[1] == to_send[0]) {
break;
}
@@ -2436,20 +2661,21 @@ static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Gro
return sent;
}
-/** Send data of len with message_id to groupnumber.
+/** @brief Send data of len with message_id to groupnumber.
*
- * return number of peers it was sent to on success.
- * return -1 if groupnumber is invalid.
- * return -2 if message is too long.
- * return -3 if we are not connected to the group.
- * return -4 if message failed to send.
+ * @return number of peers it was sent to on success.
+ * @retval -1 if groupnumber is invalid.
+ * @retval -2 if message is too long.
+ * @retval -3 if we are not connected to the group.
+ * @retval -4 if message failed to send.
*/
static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint8_t message_id, const uint8_t *data,
uint16_t len)
{
+ assert(len == 0 || data != nullptr);
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -2467,7 +2693,7 @@ static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint
++g->message_number;
- if (!g->message_number) {
+ if (g->message_number == 0) {
++g->message_number;
}
@@ -2476,11 +2702,11 @@ static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint
packet[sizeof(uint16_t) + sizeof(uint32_t)] = message_id;
- if (len) {
+ if (len != 0) {
memcpy(packet + sizeof(uint16_t) + sizeof(uint32_t) + 1, data, len);
}
- unsigned int ret = send_message_all_connections(g_c, g, packet, SIZEOF_VLA(packet), -1);
+ const unsigned int ret = send_message_all_connections(g_c, g, packet, SIZEOF_VLA(packet), -1);
if (ret == 0) {
return -4;
@@ -2489,9 +2715,9 @@ static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint
return ret;
}
-/** send a group message
- * return 0 on success
- * see: send_message_group() for error codes.
+/** @brief send a group message
+ * @retval 0 on success
+ * @see send_message_group for error codes.
*/
int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *message, uint16_t length)
{
@@ -2504,9 +2730,9 @@ int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8
return ret;
}
-/** send a group action
- * return 0 on success
- * see: send_message_group() for error codes.
+/** @brief send a group action
+ * @retval 0 on success
+ * @see send_message_group for error codes.
*/
int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length)
{
@@ -2519,17 +2745,17 @@ int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_
return ret;
}
-/** High level function to send custom lossy packets.
+/** @brief High level function to send custom lossy packets.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
int send_group_lossy_packet(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length)
{
// TODO(irungentoo): length check here?
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -2548,6 +2774,7 @@ int send_group_lossy_packet(const Group_Chats *g_c, uint32_t groupnumber, const
return 0;
}
+non_null()
static Message_Info *find_message_slot_or_reject(uint32_t message_number, uint8_t message_id, Group_Peer *peer)
{
const bool ignore_older = message_id == GROUP_MESSAGE_NAME_ID || message_id == GROUP_MESSAGE_TITLE_ID;
@@ -2571,11 +2798,12 @@ static Message_Info *find_message_slot_or_reject(uint32_t message_number, uint8_
return i;
}
-/** Stores message info in peer->last_message_infos.
+/** @brief Stores message info in `peer->last_message_infos`.
*
- * return true if message should be processed.
- * return false otherwise.
+ * @retval true if message should be processed.
+ * @retval false otherwise.
*/
+non_null()
static bool check_message_info(uint32_t message_number, uint8_t message_id, Group_Peer *peer)
{
Message_Info *const i = find_message_slot_or_reject(message_number, message_id, peer);
@@ -2592,7 +2820,7 @@ static bool check_message_info(uint32_t message_number, uint8_t message_id, Grou
++peer->num_last_message_infos;
}
- memmove(i + 1, i, ((peer->last_message_infos + peer->num_last_message_infos - 1) - i) * sizeof(Message_Info));
+ memmove(i + 1, i, (&peer->last_message_infos[peer->num_last_message_infos - 1] - i) * sizeof(Message_Info));
i->message_number = message_number;
i->message_id = message_id;
@@ -2600,6 +2828,7 @@ static bool check_message_info(uint32_t message_number, uint8_t message_id, Grou
return true;
}
+non_null(1, 3) nullable(6)
static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length,
int connection_index, void *userdata)
{
@@ -2609,7 +2838,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber,
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return;
}
@@ -2649,7 +2878,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber,
if (g->num_introducer_connections > 0 && count_connected(g) > DESIRED_CLOSEST) {
for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE
- || !(g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER)
+ || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) == 0
|| i == connection_index) {
continue;
}
@@ -2657,7 +2886,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber,
uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[i].number);
- if (id_equal(g->group[index].real_pk, real_pk)) {
+ if (pk_equal(g->group[index].real_pk, real_pk)) {
/* Received message from peer relayed via another peer, so
* the introduction was successful */
remove_connection_reason(g_c, g, i, GROUPCHAT_CONNECTION_REASON_INTRODUCER);
@@ -2671,7 +2900,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber,
uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[connection_index].number);
- const bool direct_from_sender = id_equal(g->group[index].real_pk, real_pk);
+ const bool direct_from_sender = pk_equal(g->group[index].real_pk, real_pk);
switch (message_id) {
case GROUP_MESSAGE_PING_ID: {
@@ -2741,7 +2970,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber,
newmsg[msg_data_len] = 0;
// TODO(irungentoo):
- if (g_c->message_callback) {
+ if (g_c->message_callback != nullptr) {
g_c->message_callback(g_c->m, groupnumber, index, 0, newmsg, msg_data_len, userdata);
}
@@ -2758,7 +2987,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber,
newmsg[msg_data_len] = 0;
// TODO(irungentoo):
- if (g_c->message_callback) {
+ if (g_c->message_callback != nullptr) {
g_c->message_callback(g_c->m, groupnumber, index, 1, newmsg, msg_data_len, userdata);
}
@@ -2798,7 +3027,7 @@ static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data,
groupnumber = net_ntohs(groupnumber);
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -2823,14 +3052,15 @@ static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data,
return -1;
}
-/** Did we already receive the lossy packet or not.
+/** @brief Did we already receive the lossy packet or not.
*
- * return -1 on failure.
- * return 0 if packet was not received.
- * return 1 if packet was received.
+ * @retval -1 on failure.
+ * @retval 0 if packet was not received.
+ * @retval 1 if packet was received.
*
* TODO(irungentoo): test this
*/
+non_null()
static int lossy_packet_not_received(const Group_c *g, int peer_index, uint16_t message_number)
{
if (peer_index == -1) {
@@ -2845,7 +3075,7 @@ static int lossy_packet_not_received(const Group_c *g, int peer_index, uint16_t
}
if ((uint16_t)(message_number - g->group[peer_index].bottom_lossy_number) < MAX_LOSSY_COUNT) {
- if (g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT]) {
+ if (g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] != 0) {
return 1;
}
@@ -2880,7 +3110,7 @@ static int lossy_packet_not_received(const Group_c *g, int peer_index, uint16_t
/** Does this group type make use of lossy packets? */
static bool type_uses_lossy(uint8_t type)
{
- return (type == GROUPCHAT_TYPE_AV);
+ return type == GROUPCHAT_TYPE_AV;
}
static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata)
@@ -2907,7 +3137,7 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -2943,11 +3173,11 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin
send_lossy_all_connections(g_c, g, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index);
- if (!g_c->lossy_packethandlers[message_id].function) {
+ if (g_c->lossy_packethandlers[message_id] == nullptr) {
return -1;
}
- if (g_c->lossy_packethandlers[message_id].function(g->object, groupnumber, peer_index, g->group[peer_index].object,
+ if (g_c->lossy_packethandlers[message_id](g->object, groupnumber, peer_index, g->group[peer_index].object,
lossy_data, lossy_length) == -1) {
return -1;
}
@@ -2955,16 +3185,16 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin
return 0;
}
-/** Set the object that is tied to the group chat.
+/** @brief Set the object that is tied to the group chat.
*
- * return 0 on success.
- * return -1 on failure
+ * @retval 0 on success.
+ * @retval -1 on failure
*/
int group_set_object(const Group_Chats *g_c, uint32_t groupnumber, void *object)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -2972,16 +3202,16 @@ int group_set_object(const Group_Chats *g_c, uint32_t groupnumber, void *object)
return 0;
}
-/** Set the object that is tied to the group peer.
+/** @brief Set the object that is tied to the group peer.
*
- * return 0 on success.
- * return -1 on failure
+ * @retval 0 on success.
+ * @retval -1 on failure
*/
int group_peer_set_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, void *object)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return -1;
}
@@ -2993,32 +3223,32 @@ int group_peer_set_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t
return 0;
}
-/** Return the object tied to the group chat previously set by group_set_object.
+/** @brief Return the object tied to the group chat previously set by group_set_object.
*
- * return NULL on failure.
- * return object on success.
+ * @retval NULL on failure.
+ * @return object on success.
*/
void *group_get_object(const Group_Chats *g_c, uint32_t groupnumber)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return nullptr;
}
return g->object;
}
-/** Return the object tied to the group chat peer previously set by group_peer_set_object.
+/** @brief Return the object tied to the group chat peer previously set by group_peer_set_object.
*
- * return NULL on failure.
- * return object on success.
+ * @retval NULL on failure.
+ * @return object on success.
*/
void *group_peer_get_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber)
{
const Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return nullptr;
}
@@ -3036,7 +3266,7 @@ static bool ping_groupchat(const Group_Chats *g_c, uint32_t groupnumber)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return false;
}
@@ -3052,11 +3282,12 @@ static bool ping_groupchat(const Group_Chats *g_c, uint32_t groupnumber)
/** Seconds of inactivity after which to freeze a peer */
#define FREEZE_TIMEOUT (GROUP_PING_INTERVAL * 3)
+non_null(1) nullable(3)
static bool groupchat_freeze_timedout(Group_Chats *g_c, uint32_t groupnumber, void *userdata)
{
Group_c *g = get_group_c(g_c, groupnumber);
- if (!g) {
+ if (g == nullptr) {
return false;
}
@@ -3078,6 +3309,7 @@ static bool groupchat_freeze_timedout(Group_Chats *g_c, uint32_t groupnumber, vo
}
/** Push non-empty slots to start. */
+non_null()
static void squash_connections(Group_c *g)
{
uint16_t num_connected = 0;
@@ -3096,6 +3328,7 @@ static void squash_connections(Group_c *g)
#define MIN_EMPTY_CONNECTIONS (1 + MAX_GROUP_CONNECTIONS / 10)
+non_null()
static uint16_t empty_connection_count(const Group_c *g)
{
uint16_t to_clear = MIN_EMPTY_CONNECTIONS;
@@ -3113,10 +3346,14 @@ static uint16_t empty_connection_count(const Group_c *g)
return to_clear;
}
-/** Remove old connections as necessary to ensure we have space for new
- * connections. This invalidates connections array indices (which is
+/**
+ * @brief Remove old connections as necessary to ensure we have space for new
+ * connections.
+ *
+ * This invalidates connections array indices (which is
* why we do this periodically rather than on adding a connection).
*/
+non_null()
static void clean_connections(Group_Chats *g_c, Group_c *g)
{
for (uint16_t to_clear = empty_connection_count(g); to_clear > 0; --to_clear) {
@@ -3127,7 +3364,7 @@ static void clean_connections(Group_Chats *g_c, Group_c *g)
while (i < MAX_GROUP_CONNECTIONS
&& (g->connections[i].type != GROUPCHAT_CONNECTION_CONNECTING
- || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST))) {
+ || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) != 0)) {
++i;
}
@@ -3136,7 +3373,7 @@ static void clean_connections(Group_Chats *g_c, Group_c *g)
while (i < MAX_GROUP_CONNECTIONS - to_clear
&& (g->connections[i].type != GROUPCHAT_CONNECTION_ONLINE
- || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST))) {
+ || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) != 0)) {
++i;
}
}
@@ -3149,14 +3386,13 @@ static void clean_connections(Group_Chats *g_c, Group_c *g)
squash_connections(g);
}
-/** Send current name (set in messenger) to all online groups.
- */
+/** Send current name (set in messenger) to all online groups. */
void send_name_all_groups(const Group_Chats *g_c)
{
for (uint16_t i = 0; i < g_c->num_chats; ++i) {
Group_c *g = get_group_c(g_c, i);
- if (!g) {
+ if (g == nullptr) {
continue;
}
@@ -3169,11 +3405,13 @@ void send_name_all_groups(const Group_Chats *g_c)
#define SAVED_PEER_SIZE_CONSTANT (2 * CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint16_t) + sizeof(uint64_t) + 1)
+non_null()
static uint32_t saved_peer_size(const Group_Peer *peer)
{
return SAVED_PEER_SIZE_CONSTANT + peer->nick_len;
}
+non_null()
static uint8_t *save_peer(const Group_Peer *peer, uint8_t *data)
{
memcpy(data, peer->real_pk, CRYPTO_PUBLIC_KEY_SIZE);
@@ -3188,6 +3426,7 @@ static uint8_t *save_peer(const Group_Peer *peer, uint8_t *data)
host_to_lendian_bytes64(data, peer->last_active);
data += sizeof(uint64_t);
+ // TODO(iphydf): This looks broken: nick_len can be > 255.
*data = peer->nick_len;
++data;
@@ -3200,6 +3439,7 @@ static uint8_t *save_peer(const Group_Peer *peer, uint8_t *data)
#define SAVED_CONF_SIZE_CONSTANT (1 + GROUP_ID_LENGTH + sizeof(uint32_t) \
+ sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + 1)
+non_null()
static uint32_t saved_conf_size(const Group_c *g)
{
uint32_t len = SAVED_CONF_SIZE_CONSTANT + g->title_len;
@@ -3207,7 +3447,7 @@ static uint32_t saved_conf_size(const Group_c *g)
for (uint32_t j = 0; j < g->numpeers + g->numfrozen; ++j) {
const Group_Peer *peer = (j < g->numpeers) ? &g->group[j] : &g->frozen[j - g->numpeers];
- if (id_equal(peer->real_pk, g->real_pk)) {
+ if (pk_equal(peer->real_pk, g->real_pk)) {
continue;
}
@@ -3217,11 +3457,14 @@ static uint32_t saved_conf_size(const Group_c *g)
return len;
}
-/** Save a future message number. The save will remain valid until we have sent
- * this many more messages. */
+/**
+ * Save a future message number. The save will remain valid until we have sent
+ * this many more messages.
+ */
#define SAVE_OFFSET_MESSAGE_NUMBER (1 << 16)
#define SAVE_OFFSET_LOSSY_MESSAGE_NUMBER (1 << 13)
+non_null()
static uint8_t *save_conf(const Group_c *g, uint8_t *data)
{
*data = g->type;
@@ -3253,7 +3496,7 @@ static uint8_t *save_conf(const Group_c *g, uint8_t *data)
for (uint32_t j = 0; j < g->numpeers + g->numfrozen; ++j) {
const Group_Peer *peer = (j < g->numpeers) ? &g->group[j] : &g->frozen[j - g->numpeers];
- if (id_equal(peer->real_pk, g->real_pk)) {
+ if (pk_equal(peer->real_pk, g->real_pk)) {
continue;
}
@@ -3266,6 +3509,7 @@ static uint8_t *save_conf(const Group_c *g, uint8_t *data)
return data;
}
+non_null()
static uint32_t conferences_section_size(const Group_Chats *g_c)
{
uint32_t len = 0;
@@ -3273,7 +3517,7 @@ static uint32_t conferences_section_size(const Group_Chats *g_c)
for (uint16_t i = 0; i < g_c->num_chats; ++i) {
const Group_c *g = get_group_c(g_c, i);
- if (!g || g->status != GROUPCHAT_STATUS_CONNECTED) {
+ if (g == nullptr || g->status != GROUPCHAT_STATUS_CONNECTED) {
continue;
}
@@ -3296,7 +3540,7 @@ uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data)
for (uint16_t i = 0; i < g_c->num_chats; ++i) {
const Group_c *g = get_group_c(g_c, i);
- if (!g || g->status != GROUPCHAT_STATUS_CONNECTED) {
+ if (g == nullptr || g->status != GROUPCHAT_STATUS_CONNECTED) {
continue;
}
@@ -3306,104 +3550,157 @@ uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data)
return data;
}
-static State_Load_Status load_conferences(Group_Chats *g_c, const uint8_t *data, uint32_t length)
+/**
+ * @brief load_group Load a Group section from a save file
+ * @param g Group to load
+ * @param g_c Reference to all groupchats, need for utility functions
+ * @param data Start of the data to deserialze
+ * @param length Length of data
+ * @return 0 on error, number of consumed bytes otherwise
+ */
+non_null()
+static uint32_t load_group(Group_c *g, const Group_Chats *g_c, const uint8_t *data, uint32_t length)
{
const uint8_t *init_data = data;
- while (length >= (uint32_t)(data - init_data) + SAVED_CONF_SIZE_CONSTANT) {
- const int groupnumber = create_group_chat(g_c);
+ // Initialize to default values so we can unconditionally free in case of an error
+ setup_conference(g);
- if (groupnumber == -1) {
- return STATE_LOAD_STATUS_ERROR;
- }
+ g->type = *data;
+ ++data;
- Group_c *g = &g_c->chats[groupnumber];
+ memcpy(g->id, data, GROUP_ID_LENGTH);
+ data += GROUP_ID_LENGTH;
- g->type = *data;
- ++data;
+ lendian_bytes_to_host32(&g->message_number, data);
+ data += sizeof(uint32_t);
- memcpy(g->id, data, GROUP_ID_LENGTH);
- data += GROUP_ID_LENGTH;
+ lendian_bytes_to_host16(&g->lossy_message_number, data);
+ data += sizeof(uint16_t);
- lendian_bytes_to_host32(&g->message_number, data);
- data += sizeof(uint32_t);
+ lendian_bytes_to_host16(&g->peer_number, data);
+ data += sizeof(uint16_t);
- lendian_bytes_to_host16(&g->lossy_message_number, data);
- data += sizeof(uint16_t);
+ lendian_bytes_to_host32(&g->numfrozen, data);
+ data += sizeof(uint32_t);
- lendian_bytes_to_host16(&g->peer_number, data);
- data += sizeof(uint16_t);
+ g->title_len = *data;
+
+ if (g->title_len > MAX_NAME_LENGTH) {
+ return 0;
+ }
- lendian_bytes_to_host32(&g->numfrozen, data);
- data += sizeof(uint32_t);
+ ++data;
- if (g->numfrozen > 0) {
- g->frozen = (Group_Peer *)calloc(g->numfrozen, sizeof(Group_Peer));
+ assert((data - init_data) < UINT32_MAX);
- if (g->frozen == nullptr) {
- return STATE_LOAD_STATUS_ERROR;
- }
+ if (length < (uint32_t)(data - init_data) + g->title_len) {
+ return 0;
+ }
+
+ memcpy(g->title, data, g->title_len);
+ data += g->title_len;
+
+ for (uint32_t j = 0; j < g->numfrozen; ++j) {
+
+ assert((data - init_data) < UINT32_MAX);
+
+ if (length < (uint32_t)(data - init_data) + SAVED_PEER_SIZE_CONSTANT) {
+ return 0;
}
- g->title_len = *data;
+ // This is inefficient, but allows us to check data consistency before allocating memory
+ Group_Peer *tmp_frozen = (Group_Peer *)realloc(g->frozen, (j + 1) * sizeof(Group_Peer));
- if (g->title_len > MAX_NAME_LENGTH) {
- return STATE_LOAD_STATUS_ERROR;
+ if (tmp_frozen == nullptr) {
+ // Memory allocation failure
+ return 0;
}
- ++data;
+ g->frozen = tmp_frozen;
- if (length < (uint32_t)(data - init_data) + g->title_len) {
- return STATE_LOAD_STATUS_ERROR;
+ Group_Peer *peer = &g->frozen[j];
+ *peer = empty_group_peer;
+
+ pk_copy(peer->real_pk, data);
+ data += CRYPTO_PUBLIC_KEY_SIZE;
+ pk_copy(peer->temp_pk, data);
+ data += CRYPTO_PUBLIC_KEY_SIZE;
+
+ lendian_bytes_to_host16(&peer->peer_number, data);
+ data += sizeof(uint16_t);
+
+ lendian_bytes_to_host64(&peer->last_active, data);
+ data += sizeof(uint64_t);
+
+ peer->nick_len = *data;
+
+ if (peer->nick_len > MAX_NAME_LENGTH) {
+ return 0;
}
- memcpy(g->title, data, g->title_len);
- data += g->title_len;
+ ++data;
+ assert((data - init_data) < UINT32_MAX);
- for (uint32_t j = 0; j < g->numfrozen; ++j) {
- if (length < (uint32_t)(data - init_data) + SAVED_PEER_SIZE_CONSTANT) {
- return STATE_LOAD_STATUS_ERROR;
- }
+ if (length < (uint32_t)(data - init_data) + peer->nick_len) {
+ return 0;
+ }
- Group_Peer *peer = &g->frozen[j];
- memset(peer, 0, sizeof(Group_Peer));
+ memcpy(peer->nick, data, peer->nick_len);
+ data += peer->nick_len;
- id_copy(peer->real_pk, data);
- data += CRYPTO_PUBLIC_KEY_SIZE;
- id_copy(peer->temp_pk, data);
- data += CRYPTO_PUBLIC_KEY_SIZE;
+ // NOTE: this relies on friends being loaded before conferences.
+ peer->is_friend = getfriend_id(g_c->m, peer->real_pk) != -1;
+ }
- lendian_bytes_to_host16(&peer->peer_number, data);
- data += sizeof(uint16_t);
+ if (g->numfrozen > g->maxfrozen) {
+ g->maxfrozen = g->numfrozen;
+ }
- lendian_bytes_to_host64(&peer->last_active, data);
- data += sizeof(uint64_t);
+ g->status = GROUPCHAT_STATUS_CONNECTED;
- peer->nick_len = *data;
+ pk_copy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto));
- if (peer->nick_len > MAX_NAME_LENGTH) {
- return STATE_LOAD_STATUS_ERROR;
- }
+ assert((data - init_data) < UINT32_MAX);
- ++data;
+ return (uint32_t)(data - init_data);
+}
- if (length < (uint32_t)(data - init_data) + peer->nick_len) {
- return STATE_LOAD_STATUS_ERROR;
- }
+non_null()
+static State_Load_Status load_conferences_helper(Group_Chats *g_c, const uint8_t *data, uint32_t length)
+{
+ const uint8_t *init_data = data;
- memcpy(peer->nick, data, peer->nick_len);
- data += peer->nick_len;
+ while (length >= (uint32_t)(data - init_data) + SAVED_CONF_SIZE_CONSTANT) {
+ const int groupnumber = create_group_chat(g_c);
+
+ // Helpful for testing
+ assert(groupnumber != -1);
- // NOTE: this relies on friends being loaded before conferences.
- peer->is_friend = (getfriend_id(g_c->m, peer->real_pk) != -1);
+ if (groupnumber == -1) {
+ // If this fails there's a serious problem, don't bother with cleanup
+ return STATE_LOAD_STATUS_ERROR;
}
- if (g->numfrozen > g->maxfrozen) {
- g->maxfrozen = g->numfrozen;
+ Group_c *g = &g_c->chats[groupnumber];
+
+ const uint32_t consumed = load_group(g, g_c, data, length - (uint32_t)(data - init_data));
+
+ if (consumed == 0) {
+ // remove partially loaded stuff, wipe_group_chat must be able to wipe a partially loaded group
+ const bool ret = wipe_group_chat(g_c, groupnumber);
+
+ // HACK: suppress unused variable warning
+ if (!ret) {
+ // wipe_group_chat(...) must be able to wipe partially allocated groups
+ assert(ret);
+ }
+
+ return STATE_LOAD_STATUS_ERROR;
}
- g->status = GROUPCHAT_STATUS_CONNECTED;
- memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE);
+ data += consumed;
+
const int peer_index = addpeer(g_c, groupnumber, g->real_pk, dht_get_self_public_key(g_c->m->dht), g->peer_number,
nullptr, true, false);
@@ -3417,6 +3714,27 @@ static State_Load_Status load_conferences(Group_Chats *g_c, const uint8_t *data,
return STATE_LOAD_STATUS_CONTINUE;
}
+non_null()
+static State_Load_Status load_conferences(Group_Chats *g_c, const uint8_t *data, uint32_t length)
+{
+ const State_Load_Status res = load_conferences_helper(g_c, data, length);
+
+ if (res == STATE_LOAD_STATUS_CONTINUE) {
+ return res;
+ }
+
+ // Loading failed, cleanup all Group_c
+
+ // save locally, because wipe_group_chat(...) modifies it
+ const uint16_t num_groups = g_c->num_chats;
+
+ for (uint16_t i = 0; i < num_groups; ++i) {
+ wipe_group_chat(g_c, i);
+ }
+
+ return res;
+}
+
bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type,
State_Load_Status *status)
{
@@ -3432,7 +3750,7 @@ bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint3
/** Create new groupchat instance. */
Group_Chats *new_groupchats(const Mono_Time *mono_time, Messenger *m)
{
- if (!m) {
+ if (m == nullptr) {
return nullptr;
}
@@ -3459,7 +3777,7 @@ void do_groupchats(Group_Chats *g_c, void *userdata)
for (uint16_t i = 0; i < g_c->num_chats; ++i) {
Group_c *g = get_group_c(g_c, i);
- if (!g) {
+ if (g == nullptr) {
continue;
}
@@ -3482,6 +3800,10 @@ void do_groupchats(Group_Chats *g_c, void *userdata)
/** Free everything related with group chats. */
void kill_groupchats(Group_Chats *g_c)
{
+ if (g_c == nullptr) {
+ return;
+ }
+
for (uint16_t i = 0; i < g_c->num_chats; ++i) {
del_groupchat(g_c, i, false);
}
@@ -3492,7 +3814,9 @@ void kill_groupchats(Group_Chats *g_c)
free(g_c);
}
-/** Return the number of chats in the instance m.
+/**
+ * @brief Return the number of chats in the instance m.
+ *
* You should use this to determine how much memory to allocate
* for copy_chatlist.
*/
@@ -3509,14 +3833,16 @@ uint32_t count_chatlist(const Group_Chats *g_c)
return ret;
}
-/** Copy a list of valid chat IDs into the array out_list.
+/** @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. */
+ * of out_list will be truncated to list_size.
+ */
uint32_t copy_chatlist(const Group_Chats *g_c, uint32_t *out_list, uint32_t list_size)
{
- if (!out_list) {
+ if (out_list == nullptr) {
return 0;
}
diff --git a/protocols/Tox/libtox/src/toxcore/group.h b/protocols/Tox/libtox/src/toxcore/group.h
index a2e51cd443..e6f6c44733 100644
--- a/protocols/Tox/libtox/src/toxcore/group.h
+++ b/protocols/Tox/libtox/src/toxcore/group.h
@@ -11,134 +11,18 @@
#include "Messenger.h"
-typedef enum Groupchat_Status {
- GROUPCHAT_STATUS_NONE,
- GROUPCHAT_STATUS_VALID,
- GROUPCHAT_STATUS_CONNECTED,
-} Groupchat_Status;
-
typedef enum Groupchat_Type {
GROUPCHAT_TYPE_TEXT,
GROUPCHAT_TYPE_AV,
} Groupchat_Type;
-#define MAX_LOSSY_COUNT 256
-
-typedef struct Message_Info {
- uint32_t message_number;
- uint8_t message_id;
-} Message_Info;
-
-#define MAX_LAST_MESSAGE_INFOS 8
-
-typedef struct Group_Peer {
- uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
- uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
- bool temp_pk_updated;
- bool is_friend;
-
- uint64_t last_active;
-
- Message_Info
- last_message_infos[MAX_LAST_MESSAGE_INFOS]; /* received messages, strictly decreasing in message_number */
- uint8_t num_last_message_infos;
-
- uint8_t nick[MAX_NAME_LENGTH];
- uint8_t nick_len;
- bool nick_updated;
-
- uint16_t peer_number;
-
- uint8_t recv_lossy[MAX_LOSSY_COUNT];
- uint16_t bottom_lossy_number;
- uint16_t top_lossy_number;
-
- void *object;
-} Group_Peer;
-
-#define DESIRED_CLOSEST 4
-#define MAX_GROUP_CONNECTIONS 16
-#define GROUP_ID_LENGTH CRYPTO_SYMMETRIC_KEY_SIZE
-
-typedef enum Groupchat_Connection_Type {
- GROUPCHAT_CONNECTION_NONE,
- GROUPCHAT_CONNECTION_CONNECTING,
- GROUPCHAT_CONNECTION_ONLINE,
-} Groupchat_Connection_Type;
-
-/** Connection is to one of the closest DESIRED_CLOSEST peers */
-#define GROUPCHAT_CONNECTION_REASON_CLOSEST (1 << 0)
-
-/** Connection is to a peer we are introducing to the conference */
-#define GROUPCHAT_CONNECTION_REASON_INTRODUCING (1 << 1)
-
-/** Connection is to a peer who is introducing us to the conference */
-#define GROUPCHAT_CONNECTION_REASON_INTRODUCER (1 << 2)
-
-typedef struct Groupchat_Connection {
- uint8_t type; /* `GROUPCHAT_CONNECTION_*` */
- uint8_t reasons; /* bit field with flags `GROUPCHAT_CONNECTION_REASON_*` */
- uint32_t number;
- uint16_t group_number;
-} Groupchat_Connection;
-
-typedef struct Groupchat_Closest {
- uint8_t entry;
- uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
- uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
-} Groupchat_Closest;
-
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);
-// maximum number of frozen peers to store; group_set_max_frozen() overrides.
-#define MAX_FROZEN_DEFAULT 128
-
-typedef struct Group_c {
- uint8_t status;
-
- bool need_send_name;
- bool title_fresh;
-
- Group_Peer *group;
- uint32_t numpeers;
-
- Group_Peer *frozen;
- uint32_t numfrozen;
-
- uint32_t maxfrozen;
-
- Groupchat_Connection connections[MAX_GROUP_CONNECTIONS];
-
- uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
- Groupchat_Closest closest_peers[DESIRED_CLOSEST];
- uint8_t changed;
-
- uint8_t type;
- uint8_t id[GROUP_ID_LENGTH];
-
- uint8_t title[MAX_NAME_LENGTH];
- uint8_t title_len;
-
- uint32_t message_number;
- uint16_t lossy_message_number;
- uint16_t peer_number;
-
- uint64_t last_sent_ping;
-
- uint32_t num_introducer_connections;
-
- void *object;
-
- peer_on_join_cb *peer_on_join;
- peer_on_leave_cb *peer_on_leave;
- group_on_delete_cb *group_on_delete;
-} Group_c;
-
-/** Callback for group invites.
+/** @brief Callback for group invites.
*
- * data of length is what needs to be passed to join_groupchat().
+ * 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);
@@ -157,299 +41,333 @@ typedef void peer_name_cb(Messenger *m, uint32_t conference_number, uint32_t pee
/** Set callback function for peer list changes. */
typedef void peer_list_changed_cb(Messenger *m, uint32_t conference_number, void *user_data);
-/** Callback for title changes.
+/** @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);
-/** Callback for lossy packets.
+/** @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_Lossy_Handler {
- lossy_packet_cb *function;
-} Group_Lossy_Handler;
-
-typedef struct Group_Chats {
- const Mono_Time *mono_time;
+typedef struct Group_Chats Group_Chats;
- Messenger *m;
- Friend_Connections *fr_c;
-
- Group_c *chats;
- uint16_t num_chats;
-
- g_conference_invite_cb *invite_callback;
- g_conference_connected_cb *connected_callback;
- g_conference_message_cb *message_callback;
- peer_name_cb *peer_name_callback;
- peer_list_changed_cb *peer_list_changed_callback;
- title_cb *title_callback;
-
- Group_Lossy_Handler lossy_packethandlers[256];
-} 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);
-/** Set callback function for peer nickname changes.
+/** @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);
-/** Set callback function for peer list changes.
+/** @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);
-/** Creates a new groupchat and puts it in the chats array.
+/** @brief Creates a new groupchat and puts it in the chats array.
*
- * type is one of `GROUPCHAT_TYPE_*`
+ * @param rng Random number generator used for generating the group ID.
+ * @param type is one of `GROUPCHAT_TYPE_*`
*
- * return group number on success.
- * return -1 on failure.
+ * @return group number on success.
+ * @retval -1 on failure.
*/
-int add_groupchat(Group_Chats *g_c, uint8_t type);
+non_null()
+int add_groupchat(Group_Chats *g_c, const Random *rng, uint8_t type);
-/** Delete a groupchat from the chats array, informing the group first as
+/** @brief Delete a groupchat from the chats array, informing the group first as
* appropriate.
*
- * return 0 on success.
- * return -1 if groupnumber is invalid.
+ * @retval true on success.
+ * @retval false if groupnumber is invalid.
*/
-int del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently);
+non_null()
+bool del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently);
-/** Copy the public key of (frozen, if frozen is true) peernumber who is in
- * groupnumber to pk. pk must be CRYPTO_PUBLIC_KEY_SIZE long.
+/**
+ * @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.
*
- * return 0 on success
- * return -1 if groupnumber is invalid.
- * return -2 if peernumber is invalid.
+ * @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);
/**
- * Return the size of (frozen, if frozen is true) peernumber's name.
+ * @brief Return the size of (frozen, if frozen is true) peernumber's name.
*
- * return -1 if groupnumber is invalid.
- * return -2 if peernumber is invalid.
+ * @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);
-/** Copy the name of (frozen, if frozen is true) peernumber who is in
- * groupnumber to name. name must be at least MAX_NAME_LENGTH long.
+/**
+ * @brief Copy the name of (frozen, if frozen is true) peernumber who is in
+ * groupnumber to name.
*
- * return length of name if success
- * return -1 if groupnumber is invalid.
- * return -2 if peernumber is invalid.
+ * @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.
*/
-int group_peername(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *name, bool frozen);
+non_null()
+int group_peername(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *name,
+ bool frozen);
-/** Copy last active timestamp of frozen peernumber who is in groupnumber to
- * last_active.
+/**
+ * @brief Copy last active timestamp of frozen peernumber who is in groupnumber to
+ * last_active.
*
- * return 0 on success.
- * return -1 if groupnumber is invalid.
- * return -2 if peernumber is invalid.
+ * @retval 0 on success.
+ * @retval -1 if groupnumber is invalid.
+ * @retval -2 if peernumber is invalid.
*/
-int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber,
- uint64_t *last_active);
+non_null()
+int group_frozen_last_active(
+ const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint64_t *last_active);
-/** Set maximum number of frozen peers.
+/** @brief Set maximum number of frozen peers.
*
- * return 0 on success.
- * return -1 if groupnumber is invalid.
+ * @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);
-/** invite friendnumber to groupnumber.
+/** @brief invite friendnumber to groupnumber.
*
- * return 0 on success.
- * return -1 if groupnumber is invalid.
- * return -2 if invite packet failed to send.
- * return -3 if we are not connected to the group chat.
+ * @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);
-/** Join a group (we need to have been invited first.)
+/** @brief Join a group (we need to have been invited first).
*
- * expected_type is the groupchat type we expect the chat we are joining to
- * have.
+ * @param expected_type is the groupchat type we expect the chat we are joining
+ * to have.
*
- * return group number on success.
- * return -1 if data length is invalid.
- * return -2 if group is not the expected type.
- * return -3 if friendnumber is invalid.
- * return -4 if client is already in this group.
- * return -5 if group instance failed to initialize.
- * return -6 if join packet fails to send.
- */
-int join_groupchat(Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_type, const uint8_t *data,
- uint16_t length);
-
-/** send a group message
- * return 0 on success
- * see: send_message_group() for error codes.
- */
+ * @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);
-/** send a group action
- * return 0 on success
- * see: send_message_group() for error codes.
+/** @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);
-/** set the group's title, limited to MAX_NAME_LENGTH
- * return 0 on success
- * return -1 if groupnumber is invalid.
- * return -2 if title is too long or empty.
- * return -3 if packet fails to send.
+/** @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);
-/** return the group's title size.
- * return -1 of groupnumber is invalid.
- * return -2 if title is too long or empty.
+/** @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);
-/** Get group title from groupnumber and put it in title.
+/** @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.
- * return -1 if groupnumber is invalid.
- * return -2 if title is too long or empty.
+ * @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.
- * return -1 if groupnumber is invalid.
+/**
+ * @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);
-/** return 1 if the peernumber corresponds to ours.
- * return 0 if the peernumber is not ours.
- * return -1 if groupnumber is invalid.
- * return -2 if peernumber is invalid.
- * return -3 if we are not connected to the group chat.
+/**
+ * @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);
-/** High level function to send custom lossy packets.
+/** @brief High level function to send custom lossy packets.
*
- * return -1 on failure.
- * return 0 on success.
+ * @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);
-/** Return the number of chats in the instance m.
+/**
+ * @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);
-/** Copy a list of valid chat IDs into the array out_list.
+/** @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. */
+ * 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);
-/** return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is.
+/** @brief return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is.
*
- * return -1 on failure.
- * return type on success.
+ * @retval -1 on failure.
+ * @return type on success.
*/
+non_null()
int group_get_type(const Group_Chats *g_c, uint32_t groupnumber);
-/** Copies the unique id of `group_chat[groupnumber]` into `id`.
+/** @brief Copies the unique id of `group_chat[groupnumber]` into `id`.
*
- * return false on failure.
- * return true on success.
+ * @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);
-int32_t conference_by_id(const Group_Chats *g_c, const 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.
- */
+/** Send current name (set in messenger) to all online groups. */
+non_null()
void send_name_all_groups(const Group_Chats *g_c);
-/** Set the object that is tied to the group chat.
+/** @brief Set the object that is tied to the group chat.
*
- * return 0 on success.
- * return -1 on failure
+ * @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);
-/** Set the object that is tied to the group peer.
+/** @brief Set the object that is tied to the group peer.
*
- * return 0 on success.
- * return -1 on failure
+ * @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);
-/** Return the object tied to the group chat previously set by group_set_object.
+/** @brief Return the object tied to the group chat previously set by group_set_object.
*
- * return NULL on failure.
- * return object on success.
+ * @retval NULL on failure.
+ * @return object on success.
*/
+non_null()
void *group_get_object(const Group_Chats *g_c, uint32_t groupnumber);
-/** Return the object tied to the group chat peer previously set by group_peer_set_object.
+/** @brief Return the object tied to the group chat peer previously set by group_peer_set_object.
*
- * return NULL on failure.
- * return object on success.
+ * @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);
-/** Set a function to be called when a new peer joins a group chat.
+/** @brief Set a function to be called when a new peer joins a group chat.
*
- * return 0 on success.
- * return -1 on failure.
+ * @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);
-/** Set a function to be called when a peer leaves a group chat.
+/** @brief Set a function to be called when a peer leaves a group chat.
*
- * return 0 on success.
- * return -1 on failure.
+ * @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);
-/** Set a function to be called when the group chat is deleted.
+/** @brief Set a function to be called when the group chat is deleted.
*
- * return 0 on success.
- * return -1 on failure.
+ * @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()) */
+/** 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);
/**
@@ -461,16 +379,20 @@ uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data);
* @param status Result of loading section is stored here if the section is handled.
* @return true iff section handled.
*/
-bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type,
- State_Load_Status *status);
+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
diff --git a/protocols/Tox/libtox/src/toxcore/group_announce.c b/protocols/Tox/libtox/src/toxcore/group_announce.c
new file mode 100644
index 0000000000..83dd3acf54
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/group_announce.c
@@ -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;
+
+ announce->ip_port_is_set = data[offset] == 1;
+ ++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);
+ }
+}
diff --git a/protocols/Tox/libtox/src/toxcore/group_announce.h b/protocols/Tox/libtox/src/toxcore/group_announce.h
new file mode 100644
index 0000000000..11bba7d530
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/group_announce.h
@@ -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 annoucne. */
+#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
diff --git a/protocols/Tox/libtox/src/toxcore/group_moderation.c b/protocols/Tox/libtox/src/toxcore/group_moderation.c
new file mode 100644
index 0000000000..dea38a648b
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/group_moderation.c
@@ -0,0 +1,864 @@
+/* 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");
+
+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 supplid, 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;
+}
diff --git a/protocols/Tox/libtox/src/toxcore/group_moderation.h b/protocols/Tox/libtox/src/toxcore/group_moderation.h
new file mode 100644
index 0000000000..36b44a42f0
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/group_moderation.h
@@ -0,0 +1,288 @@
+/* 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
+
+/* These values must take into account the maximum allowed packet size and headers. */
+#define MOD_MAX_NUM_MODERATORS (((MAX_PACKET_SIZE_NO_HEADERS) / (MOD_LIST_ENTRY_SIZE)))
+#define MOD_MAX_NUM_SANCTIONS (((MAX_PACKET_SIZE_NO_HEADERS - (MOD_SANCTIONS_CREDS_SIZE)) / (MOD_SANCTION_PACKED_SIZE)))
+
+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
diff --git a/protocols/Tox/libtox/src/toxcore/list.c b/protocols/Tox/libtox/src/toxcore/list.c
index 02a0b1e536..e367487380 100644
--- a/protocols/Tox/libtox/src/toxcore/list.c
+++ b/protocols/Tox/libtox/src/toxcore/list.c
@@ -5,8 +5,8 @@
/**
* Simple struct with functions to create a list which associates ids with data
- * -Allows for finding ids associated with data such as IPs or public keys in a short time
- * -Should only be used if there are relatively few add/remove calls to the list
+ * - Allows for finding ids associated with data such as IPs or public keys in a short time
+ * - Should only be used if there are relatively few add/remove calls to the list
*/
#include "list.h"
@@ -16,15 +16,16 @@
#include "ccompat.h"
-/** Basically, the elements in the list are placed in order so that they can be searched for easily
- * -each element is seen as a big-endian integer when ordering them
- * -the ids array is maintained so that each id always matches
- * -the search algorithm cuts down the time to find the id associated with a piece of data
+/**
+ * Basically, the elements in the list are placed in order so that they can be searched for easily
+ * - each element is seen as a big-endian integer when ordering them
+ * - the ids array is maintained so that each id always matches
+ * - the search algorithm cuts down the time to find the id associated with a piece of data
* at the cost of slow add/remove functions for large lists
- * -Starts at 1/2 of the array, compares the element in the array with the data,
- * then moves +/- 1/4 of the array depending on whether the value is greater or lower,
- * then +- 1/8, etc, until the value is matched or its position where it should be in the array is found
- * -some considerations since the array size is never perfect
+ * - Starts at `1/2` of the array, compares the element in the array with the data,
+ * then moves `+/- 1/4` of the array depending on whether the value is greater or lower,
+ * then `+- 1/8`, etc, until the value is matched or its position where it should be in the array is found
+ * - some considerations since the array size is never perfect
*/
static int32_t
@@ -33,13 +34,13 @@ list_index(uint32_t i)
return ~i;
}
-/** Find data in list
+/** @brief Find data in list
*
- * return value:
- * >= 0 : index of data in array
- * < 0 : no match, returns index (return value is list_index(index)) where
- * the data should be inserted
+ * @retval >=0 index of data in array
+ * @retval <0 no match, returns index (return value is `list_index(index)`) where
+ * the data should be inserted
*/
+non_null()
static int find(const BS_List *list, const uint8_t *data)
{
// should work well, but could be improved
@@ -50,15 +51,15 @@ static int find(const BS_List *list, const uint8_t *data)
uint32_t i = list->n / 2; // current position in the array
uint32_t delta = i / 2; // how much we move in the array
- if (!delta) {
+ if (delta == 0) {
delta = 1;
}
int d = -1; // used to determine if closest match is found
// closest match is found if we move back to where we have already been
- while (1) {
- int r = memcmp(data, list->data + list->element_size * i, list->element_size);
+ while (true) {
+ const int r = memcmp(data, list->data + list->element_size * i, list->element_size);
if (r == 0) {
return i;
@@ -105,6 +106,7 @@ static int find(const BS_List *list, const uint8_t *data)
*
* @return true on success.
*/
+non_null()
static bool resize(BS_List *list, uint32_t new_size)
{
if (new_size == 0) {
@@ -114,7 +116,7 @@ static bool resize(BS_List *list, uint32_t new_size)
uint8_t *data = (uint8_t *)realloc(list->data, list->element_size * new_size);
- if (!data) {
+ if (data == nullptr) {
return false;
}
@@ -122,7 +124,7 @@ static bool resize(BS_List *list, uint32_t new_size)
int *ids = (int *)realloc(list->ids, sizeof(int) * new_size);
- if (!ids) {
+ if (ids == nullptr) {
return false;
}
@@ -154,6 +156,10 @@ int bs_list_init(BS_List *list, uint32_t element_size, uint32_t initial_capacity
void bs_list_free(BS_List *list)
{
+ if (list == nullptr) {
+ return;
+ }
+
// free both arrays
free(list->data);
list->data = nullptr;
@@ -164,7 +170,7 @@ void bs_list_free(BS_List *list)
int bs_list_find(const BS_List *list, const uint8_t *data)
{
- int r = find(list, data);
+ const int r = find(list, data);
// return only -1 and positive values
if (r < 0) {
@@ -174,7 +180,7 @@ int bs_list_find(const BS_List *list, const uint8_t *data)
return list->ids[r];
}
-int bs_list_add(BS_List *list, const uint8_t *data, int id)
+bool bs_list_add(BS_List *list, const uint8_t *data, int id)
{
// find where the new element should be inserted
// see: return value of find()
@@ -182,7 +188,7 @@ int bs_list_add(BS_List *list, const uint8_t *data, int id)
if (i >= 0) {
// already in list
- return 0;
+ return false;
}
i = ~i;
@@ -193,7 +199,7 @@ int bs_list_add(BS_List *list, const uint8_t *data, int id)
const uint32_t new_capacity = list->n + list->n / 2 + 1;
if (!resize(list, new_capacity)) {
- return 0;
+ return false;
}
list->capacity = new_capacity;
@@ -211,20 +217,20 @@ int bs_list_add(BS_List *list, const uint8_t *data, int id)
// increase n
++list->n;
- return 1;
+ return true;
}
-int bs_list_remove(BS_List *list, const uint8_t *data, int id)
+bool bs_list_remove(BS_List *list, const uint8_t *data, int id)
{
- int i = find(list, data);
+ const int i = find(list, data);
if (i < 0) {
- return 0;
+ return false;
}
if (list->ids[i] != id) {
// this should never happen
- return 0;
+ return false;
}
// decrease the size of the arrays if needed
@@ -242,5 +248,5 @@ int bs_list_remove(BS_List *list, const uint8_t *data, int id)
(list->n - i) * list->element_size);
memmove(&list->ids[i], &list->ids[i + 1], (list->n - i) * sizeof(int));
- return 1;
+ return true;
}
diff --git a/protocols/Tox/libtox/src/toxcore/list.h b/protocols/Tox/libtox/src/toxcore/list.h
index 1cbf142282..a7c0e56c06 100644
--- a/protocols/Tox/libtox/src/toxcore/list.h
+++ b/protocols/Tox/libtox/src/toxcore/list.h
@@ -11,8 +11,11 @@
#ifndef C_TOXCORE_TOXCORE_LIST_H
#define C_TOXCORE_TOXCORE_LIST_H
+#include <stdbool.h>
#include <stdint.h>
+#include "attributes.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -25,41 +28,44 @@ typedef struct BS_List {
int *ids; // array of element ids
} BS_List;
-/** Initialize a list, element_size is the size of the elements in the list and
- * initial_capacity is the number of elements the memory will be initially allocated for
+/** @brief Initialize a list.
+ *
+ * @param element_size is the size of the elements in the list.
+ * @param initial_capacity is the number of elements the memory will be initially allocated for.
*
- * return value:
- * 1 : success
- * 0 : failure
+ * @retval 1 success
+ * @retval 0 failure
*/
+non_null()
int bs_list_init(BS_List *list, uint32_t element_size, uint32_t initial_capacity);
/** Free a list initiated with list_init */
+nullable(1)
void bs_list_free(BS_List *list);
-/** Retrieve the id of an element in the list
+/** @brief Retrieve the id of an element in the list
*
- * return value:
- * >= 0 : id associated with data
- * -1 : failure
+ * @retval >=0 id associated with data
+ * @retval -1 failure
*/
+non_null()
int bs_list_find(const BS_List *list, const uint8_t *data);
-/** Add an element with associated id to the list
+/** @brief Add an element with associated id to the list
*
- * return value:
- * 1 : success
- * 0 : failure (data already in list)
+ * @retval true success
+ * @retval false failure (data already in list)
*/
-int bs_list_add(BS_List *list, const uint8_t *data, int id);
+non_null()
+bool bs_list_add(BS_List *list, const uint8_t *data, int id);
-/** Remove element from the list
+/** @brief Remove element from the list
*
- * return value:
- * 1 : success
- * 0 : failure (element not found or id does not match)
+ * @retval true success
+ * @retval false failure (element not found or id does not match)
*/
-int bs_list_remove(BS_List *list, const uint8_t *data, int id);
+non_null()
+bool bs_list_remove(BS_List *list, const uint8_t *data, int id);
#ifdef __cplusplus
} // extern "C"
diff --git a/protocols/Tox/libtox/src/toxcore/logger.c b/protocols/Tox/libtox/src/toxcore/logger.c
index 67e4a3e5ce..d281a66085 100644
--- a/protocols/Tox/libtox/src/toxcore/logger.c
+++ b/protocols/Tox/libtox/src/toxcore/logger.c
@@ -14,6 +14,7 @@
#include <stdlib.h>
#include <string.h>
+#include "ccompat.h"
struct Logger {
logger_cb *callback;
@@ -21,7 +22,7 @@ struct Logger {
void *userdata;
};
-#ifdef USE_STDERR_LOGGER
+#ifndef NDEBUG
static const char *logger_level_name(Logger_Level level)
{
switch (level) {
@@ -43,12 +44,18 @@ static const char *logger_level_name(Logger_Level level)
return "<unknown>";
}
+#endif
+non_null(1, 3, 5, 6) nullable(7)
static void logger_stderr_handler(void *context, Logger_Level level, const char *file, int line, const char *func,
const char *message, void *userdata)
{
+#ifndef NDEBUG
// GL stands for "global logger".
fprintf(stderr, "[GL] %s %s:%d(%s): %s\n", logger_level_name(level), file, line, func, message);
+ fprintf(stderr, "Default stderr logger triggered; aborting program\n");
+ abort();
+#endif
}
static const Logger logger_stderr = {
@@ -56,7 +63,6 @@ static const Logger logger_stderr = {
nullptr,
nullptr,
};
-#endif
/*
* Public Functions
@@ -82,16 +88,11 @@ void logger_callback_log(Logger *log, logger_cb *function, void *context, void *
void logger_write(const Logger *log, Logger_Level level, const char *file, int line, const char *func,
const char *format, ...)
{
- if (!log) {
-#ifdef USE_STDERR_LOGGER
+ if (log == nullptr) {
log = &logger_stderr;
-#else
- fprintf(stderr, "NULL logger not permitted.\n");
- abort();
-#endif
}
- if (!log->callback) {
+ if (log->callback == nullptr) {
return;
}
@@ -104,7 +105,7 @@ void logger_write(const Logger *log, Logger_Level level, const char *file, int l
// On Windows, the path separator *may* be a backslash, so we look for that
// one too.
const char *windows_filename = strrchr(file, '\\');
- file = windows_filename ? windows_filename + 1 : file;
+ file = windows_filename != nullptr ? windows_filename + 1 : file;
#endif
// Format message
diff --git a/protocols/Tox/libtox/src/toxcore/logger.h b/protocols/Tox/libtox/src/toxcore/logger.h
index c3d8f66d61..ee5838ae73 100644
--- a/protocols/Tox/libtox/src/toxcore/logger.h
+++ b/protocols/Tox/libtox/src/toxcore/logger.h
@@ -11,7 +11,11 @@
#include <stdint.h>
-#include "ccompat.h"
+#include "attributes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
#ifndef MIN_LOGGER_LEVEL
#define MIN_LOGGER_LEVEL LOGGER_LEVEL_INFO
@@ -39,55 +43,61 @@ Logger *logger_new(void);
/**
* Frees all resources associated with the logger.
*/
+nullable(1)
void logger_kill(Logger *log);
/**
* Sets the logger callback. Disables logging if set to NULL.
* The context parameter is passed to the callback as first argument.
*/
+non_null(1) nullable(2, 3, 4)
void logger_callback_log(Logger *log, logger_cb *function, void *context, void *userdata);
-/**
- * Main write function. If logging is disabled, this does nothing.
+/** @brief Main write function. If logging is disabled, this does nothing.
+ *
+ * If the logger is NULL and `NDEBUG` is not defined, this writes to stderr.
+ * This behaviour should not be used in production code, but can be useful for
+ * temporarily debugging a function that does not have a logger available. It's
+ * essentially `fprintf(stderr, ...)`, but with source location.
*
- * If the logger is NULL, this writes to stderr. This behaviour should not be
- * used in production code, but can be useful for temporarily debugging a
- * function that does not have a logger available. It's essentially
- * fprintf(stderr, ...), but with timestamps and source location. Toxcore must
- * be built with -DUSE_STDERR_LOGGER for this to work. It will cause an
- * assertion failure otherwise.
+ * If `NDEBUG` is defined, the NULL logger does nothing.
*/
+non_null(3, 5, 6) nullable(1) GNU_PRINTF(6, 7)
void logger_write(
const Logger *log, Logger_Level level, const char *file, int line, const char *func,
- const char *format, ...) GNU_PRINTF(6, 7);
+ const char *format, ...);
-#define LOGGER_WRITE(log, level, ...) \
- do { \
- if (level >= MIN_LOGGER_LEVEL) { \
+#define LOGGER_WRITE(log, level, ...) \
+ do { \
+ if (level >= MIN_LOGGER_LEVEL) { \
logger_write(log, level, __FILE__, __LINE__, __func__, __VA_ARGS__); \
- } \
+ } \
} while (0)
/* To log with an logger */
-#define LOGGER_TRACE(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_TRACE , __VA_ARGS__)
-#define LOGGER_DEBUG(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_DEBUG , __VA_ARGS__)
-#define LOGGER_INFO(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_INFO , __VA_ARGS__)
+#define LOGGER_TRACE(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_TRACE, __VA_ARGS__)
+#define LOGGER_DEBUG(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_DEBUG, __VA_ARGS__)
+#define LOGGER_INFO(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_INFO, __VA_ARGS__)
#define LOGGER_WARNING(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_WARNING, __VA_ARGS__)
-#define LOGGER_ERROR(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_ERROR , __VA_ARGS__)
+#define LOGGER_ERROR(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_ERROR, __VA_ARGS__)
-#define LOGGER_FATAL(log, ...) \
- do { \
+#define LOGGER_FATAL(log, ...) \
+ do { \
LOGGER_ERROR(log, __VA_ARGS__); \
- abort(); \
- } while(0)
+ abort(); \
+ } while (0)
-#define LOGGER_ASSERT(log, cond, ...) \
- do { \
- if (!(cond)) { \
+#define LOGGER_ASSERT(log, cond, ...) \
+ do { \
+ if (!(cond)) { \
LOGGER_ERROR(log, "Assertion failed"); \
- LOGGER_FATAL(log, __VA_ARGS__); \
- } \
- } while(0)
+ LOGGER_FATAL(log, __VA_ARGS__); \
+ } \
+ } while (0)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
#endif // C_TOXCORE_TOXCORE_LOGGER_H
diff --git a/protocols/Tox/libtox/src/toxcore/mono_time.c b/protocols/Tox/libtox/src/toxcore/mono_time.c
index e207996df6..df64f2246c 100644
--- a/protocols/Tox/libtox/src/toxcore/mono_time.c
+++ b/protocols/Tox/libtox/src/toxcore/mono_time.c
@@ -26,21 +26,16 @@
#include <sys/time.h>
#endif
+#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include "ccompat.h"
-//!TOKSTYLE-
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-#include "../testing/fuzzing/fuzz_adapter.h"
-#endif
-//!TOKSTYLE+
-
/** don't call into system billions of times for no reason */
struct Mono_Time {
- uint64_t time;
+ uint64_t cur_time;
uint64_t base_time;
#ifdef OS_WIN32
/* protect `last_clock_update` and `last_clock_mono` from concurrent access */
@@ -49,27 +44,31 @@ struct Mono_Time {
bool last_clock_update;
#endif
+#ifndef ESP_PLATFORM
/* protect `time` from concurrent access */
pthread_rwlock_t *time_update_lock;
+#endif
mono_time_current_time_cb *current_time_callback;
void *user_data;
};
-static uint64_t current_time_monotonic_default(Mono_Time *mono_time, void *user_data)
-{
- uint64_t time = 0;
#ifdef OS_WIN32
+non_null()
+static uint64_t current_time_monotonic_default(void *user_data)
+{
+ Mono_Time *const mono_time = (Mono_Time *)user_data;
+
/* Must hold mono_time->last_clock_lock here */
/* GetTickCount provides only a 32 bit counter, but we can't use
* GetTickCount64 for backwards compatibility, so we handle wraparound
* ourselves.
*/
- uint32_t ticks = GetTickCount();
+ const uint32_t ticks = GetTickCount();
/* the higher 32 bits count the number of wrap arounds */
- uint64_t old_ovf = mono_time->time & ~((uint64_t)UINT32_MAX);
+ uint64_t old_ovf = mono_time->cur_time & ~((uint64_t)UINT32_MAX);
/* Check if time has decreased because of 32 bit wrap from GetTickCount() */
if (ticks < mono_time->last_clock_mono) {
@@ -83,10 +82,18 @@ static uint64_t current_time_monotonic_default(Mono_Time *mono_time, void *user_
}
/* splice the low and high bits back together */
- time = old_ovf + ticks;
-#else
+ return old_ovf + ticks;
+}
+#else // !OS_WIN32
+static uint64_t timespec_to_u64(struct timespec clock_mono)
+{
+ return 1000ULL * clock_mono.tv_sec + (clock_mono.tv_nsec / 1000000ULL);
+}
+#ifdef __APPLE__
+non_null()
+static uint64_t current_time_monotonic_default(void *user_data)
+{
struct timespec clock_mono;
-#if defined(__APPLE__)
clock_serv_t muhclock;
mach_timespec_t machtime;
@@ -96,23 +103,26 @@ static uint64_t current_time_monotonic_default(Mono_Time *mono_time, void *user_
clock_mono.tv_sec = machtime.tv_sec;
clock_mono.tv_nsec = machtime.tv_nsec;
-#else
- clock_gettime(CLOCK_MONOTONIC, &clock_mono);
-#endif
- time = 1000ULL * clock_mono.tv_sec + (clock_mono.tv_nsec / 1000000ULL);
-#endif
- return time;
+ return timespec_to_u64(clock_mono);
}
-
-
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-static uint64_t current_time_monotonic_dummy(Mono_Time *mono_time, void *user_data)
+#else // !__APPLE__
+non_null()
+static uint64_t current_time_monotonic_default(void *user_data)
{
- return fuzz_get_count();
-}
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ // This assert should always fail. If it does, the fuzzing harness didn't
+ // override the mono time callback.
+ assert(user_data == nullptr);
#endif
+ struct timespec clock_mono;
+ clock_gettime(CLOCK_MONOTONIC, &clock_mono);
+ return timespec_to_u64(clock_mono);
+}
+#endif // !__APPLE__
+#endif // !OS_WIN32
+
-Mono_Time *mono_time_new(void)
+Mono_Time *mono_time_new(mono_time_current_time_cb *current_time_callback, void *user_data)
{
Mono_Time *mono_time = (Mono_Time *)calloc(1, sizeof(Mono_Time));
@@ -120,6 +130,7 @@ Mono_Time *mono_time_new(void)
return nullptr;
}
+#ifndef ESP_PLATFORM
mono_time->time_update_lock = (pthread_rwlock_t *)calloc(1, sizeof(pthread_rwlock_t));
if (mono_time->time_update_lock == nullptr) {
@@ -132,13 +143,9 @@ Mono_Time *mono_time_new(void)
free(mono_time);
return nullptr;
}
-
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- mono_time->current_time_callback = current_time_monotonic_dummy;
-#else
- mono_time->current_time_callback = current_time_monotonic_default;
#endif
- mono_time->user_data = nullptr;
+
+ mono_time_set_current_time_callback(mono_time, current_time_callback, user_data);
#ifdef OS_WIN32
@@ -153,9 +160,10 @@ Mono_Time *mono_time_new(void)
#endif
- mono_time->time = 0;
+ mono_time->cur_time = 0;
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- mono_time->base_time = 0; // Maximum reproducibility
+ // Maximum reproducibility. Never return time = 0.
+ mono_time->base_time = 1;
#else
mono_time->base_time = (uint64_t)time(nullptr) - (current_time_monotonic(mono_time) / 1000ULL);
#endif
@@ -167,44 +175,56 @@ Mono_Time *mono_time_new(void)
void mono_time_free(Mono_Time *mono_time)
{
+ if (mono_time == nullptr) {
+ return;
+ }
#ifdef OS_WIN32
pthread_mutex_destroy(&mono_time->last_clock_lock);
#endif
+#ifndef ESP_PLATFORM
pthread_rwlock_destroy(mono_time->time_update_lock);
free(mono_time->time_update_lock);
+#endif
free(mono_time);
}
void mono_time_update(Mono_Time *mono_time)
{
- uint64_t time = 0;
+ uint64_t cur_time = 0;
#ifdef OS_WIN32
/* we actually want to update the overflow state of mono_time here */
pthread_mutex_lock(&mono_time->last_clock_lock);
mono_time->last_clock_update = true;
#endif
- time = mono_time->current_time_callback(mono_time, mono_time->user_data) / 1000ULL;
- time += mono_time->base_time;
+ cur_time = mono_time->current_time_callback(mono_time->user_data) / 1000ULL;
+ cur_time += mono_time->base_time;
#ifdef OS_WIN32
pthread_mutex_unlock(&mono_time->last_clock_lock);
#endif
+#ifndef ESP_PLATFORM
pthread_rwlock_wrlock(mono_time->time_update_lock);
- mono_time->time = time;
+#endif
+ mono_time->cur_time = cur_time;
+#ifndef ESP_PLATFORM
pthread_rwlock_unlock(mono_time->time_update_lock);
+#endif
}
uint64_t mono_time_get(const Mono_Time *mono_time)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
// Fuzzing is only single thread for now, no locking needed */
- return mono_time->time;
+ return mono_time->cur_time;
#else
- uint64_t time = 0;
+#ifndef ESP_PLATFORM
pthread_rwlock_rdlock(mono_time->time_update_lock);
- time = mono_time->time;
+#endif
+ const uint64_t cur_time = mono_time->cur_time;
+#ifndef ESP_PLATFORM
pthread_rwlock_unlock(mono_time->time_update_lock);
- return time;
+#endif
+ return cur_time;
#endif
}
@@ -218,7 +238,7 @@ void mono_time_set_current_time_callback(Mono_Time *mono_time,
{
if (current_time_callback == nullptr) {
mono_time->current_time_callback = current_time_monotonic_default;
- mono_time->user_data = nullptr;
+ mono_time->user_data = mono_time;
} else {
mono_time->current_time_callback = current_time_callback;
mono_time->user_data = user_data;
@@ -237,9 +257,9 @@ uint64_t current_time_monotonic(Mono_Time *mono_time)
* but must protect against other threads */
pthread_mutex_lock(&mono_time->last_clock_lock);
#endif
- uint64_t time = mono_time->current_time_callback(mono_time, mono_time->user_data);
+ const uint64_t cur_time = mono_time->current_time_callback(mono_time->user_data);
#ifdef OS_WIN32
pthread_mutex_unlock(&mono_time->last_clock_lock);
#endif
- return time;
+ return cur_time;
}
diff --git a/protocols/Tox/libtox/src/toxcore/mono_time.h b/protocols/Tox/libtox/src/toxcore/mono_time.h
index b27e9aaf13..5a36724715 100644
--- a/protocols/Tox/libtox/src/toxcore/mono_time.h
+++ b/protocols/Tox/libtox/src/toxcore/mono_time.h
@@ -8,6 +8,8 @@
#include <stdbool.h>
#include <stdint.h>
+#include "attributes.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -43,39 +45,47 @@ extern "C" {
*/
typedef struct Mono_Time Mono_Time;
-Mono_Time *mono_time_new(void);
+typedef uint64_t mono_time_current_time_cb(void *user_data);
+
+nullable(1, 2)
+Mono_Time *mono_time_new(mono_time_current_time_cb *current_time_callback, void *user_data);
+
+nullable(1)
void mono_time_free(Mono_Time *mono_time);
/**
* Update mono_time; subsequent calls to mono_time_get or mono_time_is_timeout
* will use the time at the call to mono_time_update.
*/
+non_null()
void mono_time_update(Mono_Time *mono_time);
/**
* Return unix time since epoch in seconds.
*/
+non_null()
uint64_t mono_time_get(const Mono_Time *mono_time);
/**
* Return true iff timestamp is at least timeout seconds in the past.
*/
+non_null()
bool mono_time_is_timeout(const Mono_Time *mono_time, uint64_t timestamp, uint64_t timeout);
/**
* Return current monotonic time in milliseconds (ms). The starting point is
* unspecified.
*/
+non_null()
uint64_t current_time_monotonic(Mono_Time *mono_time);
-typedef uint64_t mono_time_current_time_cb(Mono_Time *mono_time, void *user_data);
-
/**
- * Override implementation of current_time_monotonic() (for tests).
+ * Override implementation of `current_time_monotonic()` (for tests).
*
- * The caller is obligated to ensure that current_time_monotonic() continues
+ * The caller is obligated to ensure that `current_time_monotonic()` continues
* to increase monotonically.
*/
+non_null(1) nullable(2, 3)
void mono_time_set_current_time_callback(Mono_Time *mono_time,
mono_time_current_time_cb *current_time_callback, void *user_data);
diff --git a/protocols/Tox/libtox/src/toxcore/net_crypto.c b/protocols/Tox/libtox/src/toxcore/net_crypto.c
index 77ba2294ce..c34a45734c 100644
--- a/protocols/Tox/libtox/src/toxcore/net_crypto.c
+++ b/protocols/Tox/libtox/src/toxcore/net_crypto.c
@@ -14,6 +14,7 @@
#include <stdlib.h>
#include <string.h>
+#include "ccompat.h"
#include "list.h"
#include "mono_time.h"
#include "util.h"
@@ -112,7 +113,7 @@ typedef struct Crypto_Connection {
/* TCP_connection connection_number */
unsigned int connection_number_tcp;
- uint8_t maximum_speed_reached;
+ bool maximum_speed_reached;
/* Must be a pointer, because the struct is moved in memory */
pthread_mutex_t *mutex;
@@ -122,9 +123,13 @@ typedef struct Crypto_Connection {
uint32_t dht_pk_callback_number;
} Crypto_Connection;
+static const Crypto_Connection empty_crypto_connection = {{0}};
+
struct Net_Crypto {
const Logger *log;
+ const Random *rng;
Mono_Time *mono_time;
+ const Network *ns;
DHT *dht;
TCP_Connections *tcp_c;
@@ -173,6 +178,7 @@ DHT *nc_get_dht(const Net_Crypto *c)
return c->dht;
}
+non_null()
static bool crypt_connection_id_is_valid(const Net_Crypto *c, int crypt_connection_id)
{
if ((uint32_t)crypt_connection_id >= c->crypto_connections_length) {
@@ -185,11 +191,7 @@ static bool crypt_connection_id_is_valid(const Net_Crypto *c, int crypt_connecti
const Crypto_Conn_State status = c->crypto_connections[crypt_connection_id].status;
- if (status == CRYPTO_CONN_NO_CONNECTION || status == CRYPTO_CONN_FREE) {
- return false;
- }
-
- return true;
+ return status != CRYPTO_CONN_NO_CONNECTION && status != CRYPTO_CONN_FREE;
}
/** cookie timeout in seconds */
@@ -202,14 +204,16 @@ static bool crypt_connection_id_is_valid(const Net_Crypto *c, int crypt_connecti
#define COOKIE_REQUEST_LENGTH (uint16_t)(1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + COOKIE_REQUEST_PLAIN_LENGTH + CRYPTO_MAC_SIZE)
#define COOKIE_RESPONSE_LENGTH (uint16_t)(1 + CRYPTO_NONCE_SIZE + COOKIE_LENGTH + sizeof(uint64_t) + CRYPTO_MAC_SIZE)
-/** Create a cookie request packet and put it in packet.
+/** @brief Create a cookie request packet and put it in packet.
+ *
* dht_public_key is the dht public key of the other
*
* packet must be of size COOKIE_REQUEST_LENGTH or bigger.
*
- * return -1 on failure.
- * return COOKIE_REQUEST_LENGTH on success.
+ * @retval -1 on failure.
+ * @retval COOKIE_REQUEST_LENGTH on success.
*/
+non_null()
static int create_cookie_request(const Net_Crypto *c, uint8_t *packet, const uint8_t *dht_public_key,
uint64_t number, uint8_t *shared_key)
{
@@ -222,34 +226,35 @@ static int create_cookie_request(const Net_Crypto *c, uint8_t *packet, const uin
dht_get_shared_key_sent(c->dht, shared_key, dht_public_key);
uint8_t nonce[CRYPTO_NONCE_SIZE];
- random_nonce(nonce);
+ random_nonce(c->rng, nonce);
packet[0] = NET_PACKET_COOKIE_REQUEST;
memcpy(packet + 1, dht_get_self_public_key(c->dht), CRYPTO_PUBLIC_KEY_SIZE);
memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, nonce, CRYPTO_NONCE_SIZE);
- int len = encrypt_data_symmetric(shared_key, nonce, plain, sizeof(plain),
+ const int len = encrypt_data_symmetric(shared_key, nonce, plain, sizeof(plain),
packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE);
if (len != COOKIE_REQUEST_PLAIN_LENGTH + CRYPTO_MAC_SIZE) {
return -1;
}
- return (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + len);
+ return 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + len;
}
-/** Create cookie of length COOKIE_LENGTH from bytes of length COOKIE_DATA_LENGTH using encryption_key
+/** @brief Create cookie of length COOKIE_LENGTH from bytes of length COOKIE_DATA_LENGTH using encryption_key
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
-static int create_cookie(const Mono_Time *mono_time, uint8_t *cookie, const uint8_t *bytes,
+non_null()
+static int create_cookie(const Random *rng, const Mono_Time *mono_time, uint8_t *cookie, const uint8_t *bytes,
const uint8_t *encryption_key)
{
uint8_t contents[COOKIE_CONTENTS_LENGTH];
const uint64_t temp_time = mono_time_get(mono_time);
memcpy(contents, &temp_time, sizeof(temp_time));
memcpy(contents + sizeof(temp_time), bytes, COOKIE_DATA_LENGTH);
- random_nonce(cookie);
- int len = encrypt_data_symmetric(encryption_key, cookie, contents, sizeof(contents), cookie + CRYPTO_NONCE_SIZE);
+ random_nonce(rng, cookie);
+ const int len = encrypt_data_symmetric(encryption_key, cookie, contents, sizeof(contents), cookie + CRYPTO_NONCE_SIZE);
if (len != COOKIE_LENGTH - CRYPTO_NONCE_SIZE) {
return -1;
@@ -258,11 +263,12 @@ static int create_cookie(const Mono_Time *mono_time, uint8_t *cookie, const uint
return 0;
}
-/** Open cookie of length COOKIE_LENGTH to bytes of length COOKIE_DATA_LENGTH using encryption_key
+/** @brief Open cookie of length COOKIE_LENGTH to bytes of length COOKIE_DATA_LENGTH using encryption_key
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int open_cookie(const Mono_Time *mono_time, uint8_t *bytes, const uint8_t *cookie,
const uint8_t *encryption_key)
{
@@ -287,13 +293,14 @@ static int open_cookie(const Mono_Time *mono_time, uint8_t *bytes, const uint8_t
}
-/** Create a cookie response packet and put it in packet.
- * request_plain must be COOKIE_REQUEST_PLAIN_LENGTH bytes.
- * packet must be of size COOKIE_RESPONSE_LENGTH or bigger.
+/** @brief Create a cookie response packet and put it in packet.
+ * @param request_plain must be COOKIE_REQUEST_PLAIN_LENGTH bytes.
+ * @param packet must be of size COOKIE_RESPONSE_LENGTH or bigger.
*
- * return -1 on failure.
- * return COOKIE_RESPONSE_LENGTH on success.
+ * @retval -1 on failure.
+ * @retval COOKIE_RESPONSE_LENGTH on success.
*/
+non_null()
static int create_cookie_response(const Net_Crypto *c, uint8_t *packet, const uint8_t *request_plain,
const uint8_t *shared_key, const uint8_t *dht_public_key)
{
@@ -302,14 +309,14 @@ static int create_cookie_response(const Net_Crypto *c, uint8_t *packet, const ui
memcpy(cookie_plain + CRYPTO_PUBLIC_KEY_SIZE, dht_public_key, CRYPTO_PUBLIC_KEY_SIZE);
uint8_t plain[COOKIE_LENGTH + sizeof(uint64_t)];
- if (create_cookie(c->mono_time, plain, cookie_plain, c->secret_symmetric_key) != 0) {
+ if (create_cookie(c->rng, c->mono_time, plain, cookie_plain, c->secret_symmetric_key) != 0) {
return -1;
}
memcpy(plain + COOKIE_LENGTH, request_plain + COOKIE_DATA_LENGTH, sizeof(uint64_t));
packet[0] = NET_PACKET_COOKIE_RESPONSE;
- random_nonce(packet + 1);
- int len = encrypt_data_symmetric(shared_key, packet + 1, plain, sizeof(plain), packet + 1 + CRYPTO_NONCE_SIZE);
+ random_nonce(c->rng, packet + 1);
+ const int len = encrypt_data_symmetric(shared_key, packet + 1, plain, sizeof(plain), packet + 1 + CRYPTO_NONCE_SIZE);
if (len != COOKIE_RESPONSE_LENGTH - (1 + CRYPTO_NONCE_SIZE)) {
return -1;
@@ -318,13 +325,14 @@ static int create_cookie_response(const Net_Crypto *c, uint8_t *packet, const ui
return COOKIE_RESPONSE_LENGTH;
}
-/** Handle the cookie request packet of length length.
+/** @brief Handle the cookie request packet of length length.
* Put what was in the request in request_plain (must be of size COOKIE_REQUEST_PLAIN_LENGTH)
* Put the key used to decrypt the request into shared_key (of size CRYPTO_SHARED_KEY_SIZE) for use in the response.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int handle_cookie_request(const Net_Crypto *c, uint8_t *request_plain, uint8_t *shared_key,
uint8_t *dht_public_key, const uint8_t *packet, uint16_t length)
{
@@ -334,7 +342,7 @@ static int handle_cookie_request(const Net_Crypto *c, uint8_t *request_plain, ui
memcpy(dht_public_key, packet + 1, CRYPTO_PUBLIC_KEY_SIZE);
dht_get_shared_key_sent(c->dht, shared_key, dht_public_key);
- int len = decrypt_data_symmetric(shared_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE,
+ const int len = decrypt_data_symmetric(shared_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE,
packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, COOKIE_REQUEST_PLAIN_LENGTH + CRYPTO_MAC_SIZE,
request_plain);
@@ -345,8 +353,8 @@ static int handle_cookie_request(const Net_Crypto *c, uint8_t *request_plain, ui
return 0;
}
-/** Handle the cookie request packet (for raw UDP)
- */
+/** Handle the cookie request packet (for raw UDP) */
+non_null(1, 2, 3) nullable(5)
static int udp_handle_cookie_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
@@ -372,8 +380,8 @@ static int udp_handle_cookie_request(void *object, const IP_Port *source, const
return 0;
}
-/** Handle the cookie request packet (for TCP)
- */
+/** Handle the cookie request packet (for TCP) */
+non_null()
static int tcp_handle_cookie_request(const Net_Crypto *c, int connections_number, const uint8_t *packet,
uint16_t length)
{
@@ -391,12 +399,12 @@ static int tcp_handle_cookie_request(const Net_Crypto *c, int connections_number
return -1;
}
- int ret = send_packet_tcp_connection(c->tcp_c, connections_number, data, sizeof(data));
+ const int ret = send_packet_tcp_connection(c->tcp_c, connections_number, data, sizeof(data));
return ret;
}
-/** Handle the cookie request packet (for TCP oob packets)
- */
+/** Handle the cookie request packet (for TCP oob packets) */
+non_null()
static int tcp_oob_handle_cookie_request(const Net_Crypto *c, unsigned int tcp_connections_number,
const uint8_t *dht_public_key, const uint8_t *packet, uint16_t length)
{
@@ -408,7 +416,7 @@ static int tcp_oob_handle_cookie_request(const Net_Crypto *c, unsigned int tcp_c
return -1;
}
- if (public_key_cmp(dht_public_key, dht_public_key_temp) != 0) {
+ if (!pk_equal(dht_public_key, dht_public_key_temp)) {
return -1;
}
@@ -418,18 +426,19 @@ static int tcp_oob_handle_cookie_request(const Net_Crypto *c, unsigned int tcp_c
return -1;
}
- int ret = tcp_send_oob_packet(c->tcp_c, tcp_connections_number, dht_public_key, data, sizeof(data));
+ const int ret = tcp_send_oob_packet(c->tcp_c, tcp_connections_number, dht_public_key, data, sizeof(data));
return ret;
}
-/** Handle a cookie response packet of length encrypted with shared_key.
+/** @brief Handle a cookie response packet of length encrypted with shared_key.
* put the cookie in the response in cookie
*
- * cookie must be of length COOKIE_LENGTH.
+ * @param cookie must be of length COOKIE_LENGTH.
*
- * return -1 on failure.
- * return COOKIE_LENGTH on success.
+ * @retval -1 on failure.
+ * @retval COOKIE_LENGTH on success.
*/
+non_null()
static int handle_cookie_response(uint8_t *cookie, uint64_t *number,
const uint8_t *packet, uint16_t length,
const uint8_t *shared_key)
@@ -453,13 +462,14 @@ static int handle_cookie_response(uint8_t *cookie, uint64_t *number,
#define HANDSHAKE_PACKET_LENGTH (1 + COOKIE_LENGTH + CRYPTO_NONCE_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA512_SIZE + COOKIE_LENGTH + CRYPTO_MAC_SIZE)
-/** Create a handshake packet and put it in packet.
- * cookie must be COOKIE_LENGTH bytes.
- * packet must be of size HANDSHAKE_PACKET_LENGTH or bigger.
+/** @brief Create a handshake packet and put it in packet.
+ * @param cookie must be COOKIE_LENGTH bytes.
+ * @param packet must be of size HANDSHAKE_PACKET_LENGTH or bigger.
*
- * return -1 on failure.
- * return HANDSHAKE_PACKET_LENGTH on success.
+ * @retval -1 on failure.
+ * @retval HANDSHAKE_PACKET_LENGTH on success.
*/
+non_null()
static int create_crypto_handshake(const Net_Crypto *c, uint8_t *packet, const uint8_t *cookie, const uint8_t *nonce,
const uint8_t *session_pk, const uint8_t *peer_real_pk, const uint8_t *peer_dht_pubkey)
{
@@ -471,13 +481,13 @@ static int create_crypto_handshake(const Net_Crypto *c, uint8_t *packet, const u
memcpy(cookie_plain, peer_real_pk, CRYPTO_PUBLIC_KEY_SIZE);
memcpy(cookie_plain + CRYPTO_PUBLIC_KEY_SIZE, peer_dht_pubkey, CRYPTO_PUBLIC_KEY_SIZE);
- if (create_cookie(c->mono_time, plain + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA512_SIZE,
+ if (create_cookie(c->rng, c->mono_time, plain + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA512_SIZE,
cookie_plain, c->secret_symmetric_key) != 0) {
return -1;
}
- random_nonce(packet + 1 + COOKIE_LENGTH);
- int len = encrypt_data(peer_real_pk, c->self_secret_key, packet + 1 + COOKIE_LENGTH, plain, sizeof(plain),
+ random_nonce(c->rng, packet + 1 + COOKIE_LENGTH);
+ const int len = encrypt_data(peer_real_pk, c->self_secret_key, packet + 1 + COOKIE_LENGTH, plain, sizeof(plain),
packet + 1 + COOKIE_LENGTH + CRYPTO_NONCE_SIZE);
if (len != HANDSHAKE_PACKET_LENGTH - (1 + COOKIE_LENGTH + CRYPTO_NONCE_SIZE)) {
@@ -490,7 +500,7 @@ static int create_crypto_handshake(const Net_Crypto *c, uint8_t *packet, const u
return HANDSHAKE_PACKET_LENGTH;
}
-/** Handle a crypto handshake packet of length.
+/** @brief Handle a crypto handshake packet of length.
* put the nonce contained in the packet in nonce,
* the session public key in session_pk
* the real public key of the peer in peer_real_pk
@@ -505,40 +515,41 @@ static int create_crypto_handshake(const Net_Crypto *c, uint8_t *packet, const u
* peer_real_pk must be at least CRYPTO_PUBLIC_KEY_SIZE
* cookie must be at least COOKIE_LENGTH
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval false on failure.
+ * @retval true on success.
*/
-static int handle_crypto_handshake(const Net_Crypto *c, uint8_t *nonce, uint8_t *session_pk, uint8_t *peer_real_pk,
- uint8_t *dht_public_key, uint8_t *cookie, const uint8_t *packet, uint16_t length, const uint8_t *expected_real_pk)
+non_null(1, 2, 3, 4, 5, 6, 7) nullable(9)
+static bool handle_crypto_handshake(const Net_Crypto *c, uint8_t *nonce, uint8_t *session_pk, uint8_t *peer_real_pk,
+ uint8_t *dht_public_key, uint8_t *cookie, const uint8_t *packet, uint16_t length, const uint8_t *expected_real_pk)
{
if (length != HANDSHAKE_PACKET_LENGTH) {
- return -1;
+ return false;
}
uint8_t cookie_plain[COOKIE_DATA_LENGTH];
if (open_cookie(c->mono_time, cookie_plain, packet + 1, c->secret_symmetric_key) != 0) {
- return -1;
+ return false;
}
- if (expected_real_pk && public_key_cmp(cookie_plain, expected_real_pk) != 0) {
- return -1;
+ if (expected_real_pk != nullptr && !pk_equal(cookie_plain, expected_real_pk)) {
+ return false;
}
uint8_t cookie_hash[CRYPTO_SHA512_SIZE];
crypto_sha512(cookie_hash, packet + 1, COOKIE_LENGTH);
uint8_t plain[CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA512_SIZE + COOKIE_LENGTH];
- int len = decrypt_data(cookie_plain, c->self_secret_key, packet + 1 + COOKIE_LENGTH,
+ const int len = decrypt_data(cookie_plain, c->self_secret_key, packet + 1 + COOKIE_LENGTH,
packet + 1 + COOKIE_LENGTH + CRYPTO_NONCE_SIZE,
HANDSHAKE_PACKET_LENGTH - (1 + COOKIE_LENGTH + CRYPTO_NONCE_SIZE), plain);
if (len != sizeof(plain)) {
- return -1;
+ return false;
}
- if (crypto_sha512_cmp(cookie_hash, plain + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE) != 0) {
- return -1;
+ if (!crypto_sha512_eq(cookie_hash, plain + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE)) {
+ return false;
}
memcpy(nonce, plain, CRYPTO_NONCE_SIZE);
@@ -546,10 +557,11 @@ static int handle_crypto_handshake(const Net_Crypto *c, uint8_t *nonce, uint8_t
memcpy(cookie, plain + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA512_SIZE, COOKIE_LENGTH);
memcpy(peer_real_pk, cookie_plain, CRYPTO_PUBLIC_KEY_SIZE);
memcpy(dht_public_key, cookie_plain + CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_PUBLIC_KEY_SIZE);
- return 0;
+ return true;
}
+non_null()
static Crypto_Connection *get_crypto_connection(const Net_Crypto *c, int crypt_connection_id)
{
if (!crypt_connection_id_is_valid(c, crypt_connection_id)) {
@@ -560,11 +572,12 @@ static Crypto_Connection *get_crypto_connection(const Net_Crypto *c, int crypt_c
}
-/** Associate an ip_port to a connection.
+/** @brief Associate an ip_port to a connection.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int add_ip_port_connection(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip_port)
{
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
@@ -598,14 +611,15 @@ static int add_ip_port_connection(Net_Crypto *c, int crypt_connection_id, const
return -1;
}
-/** Return the IP_Port that should be used to send packets to the other peer.
+/** @brief Return the IP_Port that should be used to send packets to the other peer.
*
- * return IP_Port with family 0 on failure.
- * return IP_Port on success.
+ * @retval IP_Port with family 0 on failure.
+ * @return IP_Port on success.
*/
+non_null()
static IP_Port return_ip_port_connection(const Net_Crypto *c, int crypt_connection_id)
{
- const IP_Port empty = {0};
+ const IP_Port empty = {{{0}}};
const Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
@@ -614,15 +628,15 @@ static IP_Port return_ip_port_connection(const Net_Crypto *c, int crypt_connecti
}
const uint64_t current_time = mono_time_get(c->mono_time);
- bool v6 = 0;
- bool v4 = 0;
+ bool v6 = false;
+ bool v4 = false;
if ((UDP_DIRECT_TIMEOUT + conn->direct_lastrecv_timev4) > current_time) {
- v4 = 1;
+ v4 = true;
}
if ((UDP_DIRECT_TIMEOUT + conn->direct_lastrecv_timev6) > current_time) {
- v6 = 1;
+ v6 = true;
}
/* Prefer IP_Ports which haven't timed out to those which have.
@@ -655,11 +669,12 @@ static IP_Port return_ip_port_connection(const Net_Crypto *c, int crypt_connecti
return empty;
}
-/** Sends a packet to the peer using the fastest route.
+/** @brief Sends a packet to the peer using the fastest route.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int send_packet_to(Net_Crypto *c, int crypt_connection_id, const uint8_t *data, uint16_t length)
{
// TODO(irungentoo): TCP, etc...
@@ -669,14 +684,14 @@ static int send_packet_to(Net_Crypto *c, int crypt_connection_id, const uint8_t
return -1;
}
- int direct_send_attempt = 0;
+ bool direct_send_attempt = false;
pthread_mutex_lock(conn->mutex);
IP_Port ip_port = return_ip_port_connection(c, crypt_connection_id);
// TODO(irungentoo): on bad networks, direct connections might not last indefinitely.
if (!net_family_is_unspec(ip_port.ip.family)) {
- bool direct_connected = 0;
+ bool direct_connected = false;
// FIXME(sudden6): handle return value
crypto_connection_status(c, crypt_connection_id, &direct_connected, nullptr);
@@ -688,6 +703,7 @@ static int send_packet_to(Net_Crypto *c, int crypt_connection_id, const uint8_t
}
pthread_mutex_unlock(conn->mutex);
+ LOGGER_WARNING(c->log, "sending packet of length %d failed", length);
return -1;
}
@@ -697,7 +713,7 @@ static int send_packet_to(Net_Crypto *c, int crypt_connection_id, const uint8_t
if ((((UDP_DIRECT_TIMEOUT / 2) + conn->direct_send_attempt_time) < current_time && length < 96)
|| data[0] == NET_PACKET_COOKIE_REQUEST || data[0] == NET_PACKET_CRYPTO_HS) {
if ((uint32_t)sendpacket(dht_get_net(c->dht), &ip_port, data, length) == length) {
- direct_send_attempt = 1;
+ direct_send_attempt = true;
conn->direct_send_attempt_time = mono_time_get(c->mono_time);
}
}
@@ -705,7 +721,7 @@ static int send_packet_to(Net_Crypto *c, int crypt_connection_id, const uint8_t
pthread_mutex_unlock(conn->mutex);
pthread_mutex_lock(&c->tcp_mutex);
- int ret = send_packet_tcp_connection(c->tcp_c, conn->connection_number_tcp, data, length);
+ const int ret = send_packet_tcp_connection(c->tcp_c, conn->connection_number_tcp, data, length);
pthread_mutex_unlock(&c->tcp_mutex);
pthread_mutex_lock(conn->mutex);
@@ -716,38 +732,40 @@ static int send_packet_to(Net_Crypto *c, int crypt_connection_id, const uint8_t
pthread_mutex_unlock(conn->mutex);
- if (ret == 0 || direct_send_attempt) {
+ if (direct_send_attempt) {
return 0;
}
- return -1;
+ return ret;
}
/*** START: Array Related functions */
-/** Return number of packets in array
+/** @brief Return number of packets in array
* Note that holes are counted too.
*/
+non_null()
static uint32_t num_packets_array(const Packets_Array *array)
{
return array->buffer_end - array->buffer_start;
}
-/** Add data with packet number to array.
+/** @brief Add data with packet number to array.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int add_data_to_buffer(Packets_Array *array, uint32_t number, const Packet_Data *data)
{
if (number - array->buffer_start >= CRYPTO_PACKET_BUFFER_SIZE) {
return -1;
}
- uint32_t num = number % CRYPTO_PACKET_BUFFER_SIZE;
+ const uint32_t num = number % CRYPTO_PACKET_BUFFER_SIZE;
- if (array->buffer[num]) {
+ if (array->buffer[num] != nullptr) {
return -1;
}
@@ -767,12 +785,13 @@ static int add_data_to_buffer(Packets_Array *array, uint32_t number, const Packe
return 0;
}
-/** Get pointer of data with packet number.
+/** @brief Get pointer of data with packet number.
*
- * return -1 on failure.
- * return 0 if data at number is empty.
- * return 1 if data pointer was put in data.
+ * @retval -1 on failure.
+ * @retval 0 if data at number is empty.
+ * @retval 1 if data pointer was put in data.
*/
+non_null()
static int get_data_pointer(const Packets_Array *array, Packet_Data **data, uint32_t number)
{
const uint32_t num_spots = num_packets_array(array);
@@ -781,9 +800,9 @@ static int get_data_pointer(const Packets_Array *array, Packet_Data **data, uint
return -1;
}
- uint32_t num = number % CRYPTO_PACKET_BUFFER_SIZE;
+ const uint32_t num = number % CRYPTO_PACKET_BUFFER_SIZE;
- if (!array->buffer[num]) {
+ if (array->buffer[num] == nullptr) {
return 0;
}
@@ -791,37 +810,41 @@ static int get_data_pointer(const Packets_Array *array, Packet_Data **data, uint
return 1;
}
-/** Add data to end of array.
+/** @brief Add data to end of array.
*
- * return -1 on failure.
- * return packet number on success.
+ * @retval -1 on failure.
+ * @return packet number on success.
*/
-static int64_t add_data_end_of_buffer(Packets_Array *array, const Packet_Data *data)
+non_null()
+static int64_t add_data_end_of_buffer(const Logger *logger, Packets_Array *array, const Packet_Data *data)
{
const uint32_t num_spots = num_packets_array(array);
if (num_spots >= CRYPTO_PACKET_BUFFER_SIZE) {
+ LOGGER_WARNING(logger, "crypto packet buffer size exceeded; rejecting packet of length %d", data->length);
return -1;
}
Packet_Data *new_d = (Packet_Data *)calloc(1, sizeof(Packet_Data));
if (new_d == nullptr) {
+ LOGGER_ERROR(logger, "packet data allocation failed");
return -1;
}
*new_d = *data;
- uint32_t id = array->buffer_end;
+ const uint32_t id = array->buffer_end;
array->buffer[id % CRYPTO_PACKET_BUFFER_SIZE] = new_d;
++array->buffer_end;
return id;
}
-/** Read data from beginning of array.
+/** @brief Read data from beginning of array.
*
- * return -1 on failure.
- * return packet number on success.
+ * @retval -1 on failure.
+ * @return packet number on success.
*/
+non_null()
static int64_t read_data_beg_buffer(Packets_Array *array, Packet_Data *data)
{
if (array->buffer_end == array->buffer_start) {
@@ -830,23 +853,24 @@ static int64_t read_data_beg_buffer(Packets_Array *array, Packet_Data *data)
const uint32_t num = array->buffer_start % CRYPTO_PACKET_BUFFER_SIZE;
- if (!array->buffer[num]) {
+ if (array->buffer[num] == nullptr) {
return -1;
}
*data = *array->buffer[num];
- uint32_t id = array->buffer_start;
+ const uint32_t id = array->buffer_start;
++array->buffer_start;
free(array->buffer[num]);
array->buffer[num] = nullptr;
return id;
}
-/** Delete all packets in array before number (but not number)
+/** @brief Delete all packets in array before number (but not number)
*
- * return -1 on failure.
- * return 0 on success
+ * @retval -1 on failure.
+ * @retval 0 on success
*/
+non_null()
static int clear_buffer_until(Packets_Array *array, uint32_t number)
{
const uint32_t num_spots = num_packets_array(array);
@@ -858,9 +882,9 @@ static int clear_buffer_until(Packets_Array *array, uint32_t number)
uint32_t i;
for (i = array->buffer_start; i != number; ++i) {
- uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE;
+ const uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE;
- if (array->buffer[num]) {
+ if (array->buffer[num] != nullptr) {
free(array->buffer[num]);
array->buffer[num] = nullptr;
}
@@ -870,14 +894,15 @@ static int clear_buffer_until(Packets_Array *array, uint32_t number)
return 0;
}
+non_null()
static int clear_buffer(Packets_Array *array)
{
uint32_t i;
for (i = array->buffer_start; i != array->buffer_end; ++i) {
- uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE;
+ const uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE;
- if (array->buffer[num]) {
+ if (array->buffer[num] != nullptr) {
free(array->buffer[num]);
array->buffer[num] = nullptr;
}
@@ -887,11 +912,12 @@ static int clear_buffer(Packets_Array *array)
return 0;
}
-/** Set array buffer end to number.
+/** @brief Set array buffer end to number.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int set_buffer_end(Packets_Array *array, uint32_t number)
{
if (number - array->buffer_start > CRYPTO_PACKET_BUFFER_SIZE) {
@@ -906,12 +932,14 @@ static int set_buffer_end(Packets_Array *array, uint32_t number)
return 0;
}
-/** Create a packet request packet from recv_array and send_buffer_end into
- * data of length.
+/**
+ * @brief Create a packet request packet from recv_array and send_buffer_end into
+ * data of length.
*
- * return -1 on failure.
- * return length of packet on success.
+ * @retval -1 on failure.
+ * @return length of packet on success.
*/
+non_null()
static int generate_request_packet(uint8_t *data, uint16_t length, const Packets_Array *recv_array)
{
if (length == 0) {
@@ -933,9 +961,9 @@ static int generate_request_packet(uint8_t *data, uint16_t length, const Packets
uint32_t n = 1;
for (uint32_t i = recv_array->buffer_start; i != recv_array->buffer_end; ++i) {
- uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE;
+ const uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE;
- if (!recv_array->buffer[num]) {
+ if (recv_array->buffer[num] == nullptr) {
data[cur_len] = n;
n = 0;
++cur_len;
@@ -959,12 +987,13 @@ static int generate_request_packet(uint8_t *data, uint16_t length, const Packets
return cur_len;
}
-/** Handle a request data packet.
+/** @brief Handle a request data packet.
* Remove all the packets the other received from the array.
*
- * return -1 on failure.
- * return number of requested packets on success.
+ * @retval -1 on failure.
+ * @return number of requested packets on success.
*/
+non_null()
static int handle_request_packet(Mono_Time *mono_time, Packets_Array *send_array,
const uint8_t *data, uint16_t length,
uint64_t *latest_send_time, uint64_t rtt_time)
@@ -988,18 +1017,18 @@ static int handle_request_packet(Mono_Time *mono_time, Packets_Array *send_array
uint32_t requested = 0;
const uint64_t temp_time = current_time_monotonic(mono_time);
- uint64_t l_sent_time = -1;
+ uint64_t l_sent_time = 0;
for (uint32_t i = send_array->buffer_start; i != send_array->buffer_end; ++i) {
if (length == 0) {
break;
}
- uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE;
+ const uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE;
if (n == data[0]) {
- if (send_array->buffer[num]) {
- uint64_t sent_time = send_array->buffer[num]->sent_time;
+ if (send_array->buffer[num] != nullptr) {
+ const uint64_t sent_time = send_array->buffer[num]->sent_time;
if ((sent_time + rtt_time) < temp_time) {
send_array->buffer[num]->sent_time = 0;
@@ -1011,7 +1040,7 @@ static int handle_request_packet(Mono_Time *mono_time, Packets_Array *send_array
n = 0;
++requested;
} else {
- if (send_array->buffer[num]) {
+ if (send_array->buffer[num] != nullptr) {
l_sent_time = max_u64(l_sent_time, send_array->buffer[num]->sent_time);
free(send_array->buffer[num]);
@@ -1042,32 +1071,37 @@ static int handle_request_packet(Mono_Time *mono_time, Packets_Array *send_array
#define MAX_DATA_DATA_PACKET_SIZE (MAX_CRYPTO_PACKET_SIZE - (1 + sizeof(uint16_t) + CRYPTO_MAC_SIZE))
-/** Creates and sends a data packet to the peer using the fastest route.
+/** @brief Creates and sends a data packet to the peer using the fastest route.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int send_data_packet(Net_Crypto *c, int crypt_connection_id, const uint8_t *data, uint16_t length)
{
const uint16_t max_length = MAX_CRYPTO_PACKET_SIZE - (1 + sizeof(uint16_t) + CRYPTO_MAC_SIZE);
if (length == 0 || length > max_length) {
+ LOGGER_ERROR(c->log, "zero-length or too large data packet: %d (max: %d)", length, max_length);
return -1;
}
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
if (conn == nullptr) {
+ LOGGER_ERROR(c->log, "connection id %d not found", crypt_connection_id);
return -1;
}
pthread_mutex_lock(conn->mutex);
- VLA(uint8_t, packet, 1 + sizeof(uint16_t) + length + CRYPTO_MAC_SIZE);
+ const uint16_t packet_size = 1 + sizeof(uint16_t) + length + CRYPTO_MAC_SIZE;
+ VLA(uint8_t, packet, packet_size);
packet[0] = NET_PACKET_CRYPTO_DATA;
memcpy(packet + 1, conn->sent_nonce + (CRYPTO_NONCE_SIZE - sizeof(uint16_t)), sizeof(uint16_t));
const int len = encrypt_data_symmetric(conn->shared_key, conn->sent_nonce, data, length, packet + 1 + sizeof(uint16_t));
- if (len + 1 + sizeof(uint16_t) != SIZEOF_VLA(packet)) {
+ if (len + 1 + sizeof(uint16_t) != packet_size) {
+ LOGGER_ERROR(c->log, "encryption failed: %d", len);
pthread_mutex_unlock(conn->mutex);
return -1;
}
@@ -1078,21 +1112,23 @@ static int send_data_packet(Net_Crypto *c, int crypt_connection_id, const uint8_
return send_packet_to(c, crypt_connection_id, packet, SIZEOF_VLA(packet));
}
-/** Creates and sends a data packet with buffer_start and num to the peer using the fastest route.
+/** @brief Creates and sends a data packet with buffer_start and num to the peer using the fastest route.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int send_data_packet_helper(Net_Crypto *c, int crypt_connection_id, uint32_t buffer_start, uint32_t num,
const uint8_t *data, uint16_t length)
{
if (length == 0 || length > MAX_CRYPTO_DATA_SIZE) {
+ LOGGER_ERROR(c->log, "zero-length or too large data packet: %d (max: %d)", length, MAX_CRYPTO_PACKET_SIZE);
return -1;
}
num = net_htonl(num);
buffer_start = net_htonl(buffer_start);
- uint16_t padding_length = (MAX_CRYPTO_DATA_SIZE - length) % CRYPTO_MAX_PADDING;
+ const uint16_t padding_length = (MAX_CRYPTO_DATA_SIZE - length) % CRYPTO_MAX_PADDING;
VLA(uint8_t, packet, sizeof(uint32_t) + sizeof(uint32_t) + padding_length + length);
memcpy(packet, &buffer_start, sizeof(uint32_t));
memcpy(packet + sizeof(uint32_t), &num, sizeof(uint32_t));
@@ -1102,6 +1138,7 @@ static int send_data_packet_helper(Net_Crypto *c, int crypt_connection_id, uint3
return send_data_packet(c, crypt_connection_id, packet, SIZEOF_VLA(packet));
}
+non_null()
static int reset_max_speed_reached(Net_Crypto *c, int crypt_connection_id)
{
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
@@ -1126,19 +1163,23 @@ static int reset_max_speed_reached(Net_Crypto *c, int crypt_connection_id)
dt->sent_time = current_time_monotonic(c->mono_time);
}
- conn->maximum_speed_reached = 0;
+ conn->maximum_speed_reached = false;
}
return 0;
}
-/** return -1 if data could not be put in packet queue.
- * return positive packet number if data was put into the queue.
+/**
+ * @retval -1 if data could not be put in packet queue.
+ * @return positive packet number if data was put into the queue.
*/
+non_null()
static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, const uint8_t *data, uint16_t length,
- uint8_t congestion_control)
+ bool congestion_control)
{
if (length == 0 || length > MAX_CRYPTO_DATA_SIZE) {
+ LOGGER_ERROR(c->log, "rejecting too large (or empty) packet of size %d on crypt connection %d", length,
+ crypt_connection_id);
return -1;
}
@@ -1153,6 +1194,7 @@ static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, cons
reset_max_speed_reached(c, crypt_connection_id);
if (conn->maximum_speed_reached && congestion_control) {
+ LOGGER_INFO(c->log, "congestion control: maximum speed reached on crypt connection %d", crypt_connection_id);
return -1;
}
@@ -1161,7 +1203,7 @@ static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, cons
dt.length = length;
memcpy(dt.data, data, length);
pthread_mutex_lock(conn->mutex);
- int64_t packet_num = add_data_end_of_buffer(&conn->send_array, &dt);
+ const int64_t packet_num = add_data_end_of_buffer(c->log, &conn->send_array, &dt);
pthread_mutex_unlock(conn->mutex);
if (packet_num == -1) {
@@ -1179,16 +1221,18 @@ static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, cons
dt1->sent_time = current_time_monotonic(c->mono_time);
}
} else {
- conn->maximum_speed_reached = 1;
- LOGGER_DEBUG(c->log, "send_data_packet failed");
+ conn->maximum_speed_reached = true;
+ LOGGER_DEBUG(c->log, "send_data_packet failed (packet_num = %ld)", (long)packet_num);
}
return packet_num;
}
-/** Get the lowest 2 bytes from the nonce and convert
- * them to host byte format before returning them.
+/**
+ * @brief Get the lowest 2 bytes from the nonce and convert
+ * them to host byte format before returning them.
*/
+non_null()
static uint16_t get_nonce_uint16(const uint8_t *nonce)
{
uint16_t num;
@@ -1198,13 +1242,14 @@ static uint16_t get_nonce_uint16(const uint8_t *nonce)
#define DATA_NUM_THRESHOLD 21845
-/** Handle a data packet.
+/** @brief Handle a data packet.
* Decrypt packet of length and put it into data.
* data must be at least MAX_DATA_DATA_PACKET_SIZE big.
*
- * return -1 on failure.
- * return length of data on success.
+ * @retval -1 on failure.
+ * @return length of data on success.
*/
+non_null()
static int handle_data_packet(const Net_Crypto *c, int crypt_connection_id, uint8_t *data, const uint8_t *packet,
uint16_t length)
{
@@ -1222,12 +1267,12 @@ static int handle_data_packet(const Net_Crypto *c, int crypt_connection_id, uint
uint8_t nonce[CRYPTO_NONCE_SIZE];
memcpy(nonce, conn->recv_nonce, CRYPTO_NONCE_SIZE);
- uint16_t num_cur_nonce = get_nonce_uint16(nonce);
+ const uint16_t num_cur_nonce = get_nonce_uint16(nonce);
uint16_t num;
net_unpack_u16(packet + 1, &num);
- uint16_t diff = num - num_cur_nonce;
+ const uint16_t diff = num - num_cur_nonce;
increment_nonce_number(nonce, diff);
- int len = decrypt_data_symmetric(conn->shared_key, nonce, packet + 1 + sizeof(uint16_t),
+ const int len = decrypt_data_symmetric(conn->shared_key, nonce, packet + 1 + sizeof(uint16_t),
length - (1 + sizeof(uint16_t)), data);
if ((unsigned int)len != length - crypto_packet_overhead) {
@@ -1241,11 +1286,12 @@ static int handle_data_packet(const Net_Crypto *c, int crypt_connection_id, uint
return len;
}
-/** Send a request packet.
+/** @brief Send a request packet.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int send_request_packet(Net_Crypto *c, int crypt_connection_id)
{
const Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
@@ -1255,7 +1301,7 @@ static int send_request_packet(Net_Crypto *c, int crypt_connection_id)
}
uint8_t data[MAX_CRYPTO_DATA_SIZE];
- int len = generate_request_packet(data, sizeof(data), &conn->recv_array);
+ const int len = generate_request_packet(data, sizeof(data), &conn->recv_array);
if (len == -1) {
return -1;
@@ -1265,11 +1311,12 @@ static int send_request_packet(Net_Crypto *c, int crypt_connection_id)
len);
}
-/** Send up to max num previously requested data packets.
+/** @brief Send up to max num previously requested data packets.
*
- * return -1 on failure.
- * return number of packets sent on success.
+ * @retval -1 on failure.
+ * @return number of packets sent on success.
*/
+non_null()
static int send_requested_packets(Net_Crypto *c, int crypt_connection_id, uint32_t max_num)
{
if (max_num == 0) {
@@ -1299,7 +1346,7 @@ static int send_requested_packets(Net_Crypto *c, int crypt_connection_id, uint32
continue;
}
- if (dt->sent_time) {
+ if (dt->sent_time != 0) {
continue;
}
@@ -1318,11 +1365,12 @@ static int send_requested_packets(Net_Crypto *c, int crypt_connection_id, uint32
}
-/** Add a new temp packet to send repeatedly.
+/** @brief Add a new temp packet to send repeatedly.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int new_temp_packet(const Net_Crypto *c, int crypt_connection_id, const uint8_t *packet, uint16_t length)
{
if (length == 0 || length > MAX_CRYPTO_PACKET_SIZE) {
@@ -1341,7 +1389,7 @@ static int new_temp_packet(const Net_Crypto *c, int crypt_connection_id, const u
return -1;
}
- if (conn->temp_packet) {
+ if (conn->temp_packet != nullptr) {
free(conn->temp_packet);
}
@@ -1353,11 +1401,12 @@ static int new_temp_packet(const Net_Crypto *c, int crypt_connection_id, const u
return 0;
}
-/** Clear the temp packet.
+/** @brief Clear the temp packet.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int clear_temp_packet(const Net_Crypto *c, int crypt_connection_id)
{
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
@@ -1366,7 +1415,7 @@ static int clear_temp_packet(const Net_Crypto *c, int crypt_connection_id)
return -1;
}
- if (conn->temp_packet) {
+ if (conn->temp_packet != nullptr) {
free(conn->temp_packet);
}
@@ -1378,11 +1427,12 @@ static int clear_temp_packet(const Net_Crypto *c, int crypt_connection_id)
}
-/** Send the temp packet.
+/** @brief Send the temp packet.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int send_temp_packet(Net_Crypto *c, int crypt_connection_id)
{
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
@@ -1391,7 +1441,7 @@ static int send_temp_packet(Net_Crypto *c, int crypt_connection_id)
return -1;
}
- if (!conn->temp_packet) {
+ if (conn->temp_packet == nullptr) {
return -1;
}
@@ -1404,12 +1454,13 @@ static int send_temp_packet(Net_Crypto *c, int crypt_connection_id)
return 0;
}
-/** Create a handshake packet and set it as a temp packet.
- * cookie must be COOKIE_LENGTH.
+/** @brief Create a handshake packet and set it as a temp packet.
+ * @param cookie must be COOKIE_LENGTH.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int create_send_handshake(Net_Crypto *c, int crypt_connection_id, const uint8_t *cookie,
const uint8_t *dht_public_key)
{
@@ -1434,11 +1485,12 @@ static int create_send_handshake(Net_Crypto *c, int crypt_connection_id, const u
return 0;
}
-/** Send a kill packet.
+/** @brief Send a kill packet.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int send_kill_packet(Net_Crypto *c, int crypt_connection_id)
{
const Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
@@ -1452,6 +1504,7 @@ static int send_kill_packet(Net_Crypto *c, int crypt_connection_id)
&kill_packet, sizeof(kill_packet));
}
+non_null(1) nullable(3)
static void connection_kill(Net_Crypto *c, int crypt_connection_id, void *userdata)
{
const Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
@@ -1460,15 +1513,15 @@ static void connection_kill(Net_Crypto *c, int crypt_connection_id, void *userda
return;
}
- if (conn->connection_status_callback) {
- conn->connection_status_callback(conn->connection_status_callback_object, conn->connection_status_callback_id, 0,
- userdata);
+ if (conn->connection_status_callback != nullptr) {
+ conn->connection_status_callback(conn->connection_status_callback_object, conn->connection_status_callback_id,
+ false, userdata);
}
- while (1) { /* TODO(irungentoo): is this really the best way to do this? */
+ while (true) { /* TODO(irungentoo): is this really the best way to do this? */
pthread_mutex_lock(&c->connections_mutex);
- if (!c->connection_use_counter) {
+ if (c->connection_use_counter == 0) {
break;
}
@@ -1479,11 +1532,12 @@ static void connection_kill(Net_Crypto *c, int crypt_connection_id, void *userda
pthread_mutex_unlock(&c->connections_mutex);
}
-/** Handle a received data packet.
+/** @brief Handle a received data packet.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null(1, 3) nullable(6)
static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const uint8_t *packet, uint16_t length,
bool udp, void *userdata)
{
@@ -1498,7 +1552,7 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const
}
uint8_t data[MAX_DATA_DATA_PACKET_SIZE];
- int len = handle_data_packet(c, crypt_connection_id, data, packet, length);
+ const int len = handle_data_packet(c, crypt_connection_id, data, packet, length);
if (len <= (int)(sizeof(uint32_t) * 2)) {
return -1;
@@ -1546,9 +1600,9 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const
clear_temp_packet(c, crypt_connection_id);
conn->status = CRYPTO_CONN_ESTABLISHED;
- if (conn->connection_status_callback) {
- conn->connection_status_callback(conn->connection_status_callback_object, conn->connection_status_callback_id, 1,
- userdata);
+ if (conn->connection_status_callback != nullptr) {
+ conn->connection_status_callback(conn->connection_status_callback_object, conn->connection_status_callback_id,
+ true, userdata);
}
}
@@ -1561,7 +1615,7 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const
rtt_time = DEFAULT_TCP_PING_CONNECTION;
}
- int requested = handle_request_packet(c->mono_time, &conn->send_array,
+ const int requested = handle_request_packet(c->mono_time, &conn->send_array,
real_data, real_length,
&rtt_calc_time, rtt_time);
@@ -1579,7 +1633,7 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const
return -1;
}
- while (1) {
+ while (true) {
pthread_mutex_lock(conn->mutex);
const int ret = read_data_beg_buffer(&conn->recv_array, &dt);
pthread_mutex_unlock(conn->mutex);
@@ -1588,7 +1642,7 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const
break;
}
- if (conn->connection_data_callback) {
+ if (conn->connection_data_callback != nullptr) {
conn->connection_data_callback(conn->connection_data_callback_object, conn->connection_data_callback_id, dt.data,
dt.length, userdata);
}
@@ -1607,7 +1661,7 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const
set_buffer_end(&conn->recv_array, num);
- if (conn->connection_lossy_data_callback) {
+ if (conn->connection_lossy_data_callback != nullptr) {
conn->connection_lossy_data_callback(conn->connection_lossy_data_callback_object,
conn->connection_lossy_data_callback_id, real_data, real_length, userdata);
}
@@ -1626,6 +1680,7 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const
return 0;
}
+non_null()
static int handle_packet_cookie_response(Net_Crypto *c, int crypt_connection_id, const uint8_t *packet, uint16_t length)
{
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
@@ -1657,6 +1712,7 @@ static int handle_packet_cookie_response(Net_Crypto *c, int crypt_connection_id,
return 0;
}
+non_null(1, 3) nullable(5)
static int handle_packet_crypto_hs(Net_Crypto *c, int crypt_connection_id, const uint8_t *packet, uint16_t length,
void *userdata)
{
@@ -1676,12 +1732,12 @@ static int handle_packet_crypto_hs(Net_Crypto *c, int crypt_connection_id, const
uint8_t dht_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t cookie[COOKIE_LENGTH];
- if (handle_crypto_handshake(c, conn->recv_nonce, conn->peersessionpublic_key, peer_real_pk, dht_public_key, cookie,
- packet, length, conn->public_key) != 0) {
+ if (!handle_crypto_handshake(c, conn->recv_nonce, conn->peersessionpublic_key, peer_real_pk, dht_public_key, cookie,
+ packet, length, conn->public_key)) {
return -1;
}
- if (public_key_cmp(dht_public_key, conn->dht_public_key) == 0) {
+ if (pk_equal(dht_public_key, conn->dht_public_key)) {
encrypt_precompute(conn->peersessionpublic_key, conn->sessionsecret_key, conn->shared_key);
if (conn->status == CRYPTO_CONN_COOKIE_REQUESTING) {
@@ -1692,7 +1748,7 @@ static int handle_packet_crypto_hs(Net_Crypto *c, int crypt_connection_id, const
conn->status = CRYPTO_CONN_NOT_CONFIRMED;
} else {
- if (conn->dht_pk_callback) {
+ if (conn->dht_pk_callback != nullptr) {
conn->dht_pk_callback(conn->dht_pk_callback_object, conn->dht_pk_callback_number, dht_public_key, userdata);
}
}
@@ -1700,6 +1756,7 @@ static int handle_packet_crypto_hs(Net_Crypto *c, int crypt_connection_id, const
return 0;
}
+non_null(1, 3) nullable(6)
static int handle_packet_crypto_data(Net_Crypto *c, int crypt_connection_id, const uint8_t *packet, uint16_t length,
bool udp, void *userdata)
{
@@ -1716,11 +1773,12 @@ static int handle_packet_crypto_data(Net_Crypto *c, int crypt_connection_id, con
return handle_data_packet_core(c, crypt_connection_id, packet, length, udp, userdata);
}
-/** Handle a packet that was received for the connection.
+/** @brief Handle a packet that was received for the connection.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null(1, 3) nullable(6)
static int handle_packet_connection(Net_Crypto *c, int crypt_connection_id, const uint8_t *packet, uint16_t length,
bool udp, void *userdata)
{
@@ -1743,11 +1801,12 @@ static int handle_packet_connection(Net_Crypto *c, int crypt_connection_id, cons
}
}
-/** Set the size of the friend list to numfriends.
+/** @brief Set the size of the friend list to numfriends.
*
- * return -1 if realloc fails.
- * return 0 if it succeeds.
+ * @retval -1 if realloc fails.
+ * @retval 0 if it succeeds.
*/
+non_null()
static int realloc_cryptoconnection(Net_Crypto *c, uint32_t num)
{
if (num == 0) {
@@ -1768,17 +1827,18 @@ static int realloc_cryptoconnection(Net_Crypto *c, uint32_t num)
}
-/** Create a new empty crypto connection.
+/** @brief Create a new empty crypto connection.
*
- * return -1 on failure.
- * return connection id on success.
+ * @retval -1 on failure.
+ * @return connection id on success.
*/
+non_null()
static int create_crypto_connection(Net_Crypto *c)
{
- while (1) { /* TODO(irungentoo): is this really the best way to do this? */
+ while (true) { /* TODO(irungentoo): is this really the best way to do this? */
pthread_mutex_lock(&c->connections_mutex);
- if (!c->connection_use_counter) {
+ if (c->connection_use_counter == 0) {
break;
}
@@ -1798,7 +1858,7 @@ static int create_crypto_connection(Net_Crypto *c)
if (realloc_cryptoconnection(c, c->crypto_connections_length + 1) == 0) {
id = c->crypto_connections_length;
++c->crypto_connections_length;
- memset(&c->crypto_connections[id], 0, sizeof(Crypto_Connection));
+ c->crypto_connections[id] = empty_crypto_connection;
}
}
@@ -1829,11 +1889,12 @@ static int create_crypto_connection(Net_Crypto *c)
return id;
}
-/** Wipe a crypto connection.
+/** @brief Wipe a crypto connection.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null()
static int wipe_crypto_connection(Net_Crypto *c, int crypt_connection_id)
{
if ((uint32_t)crypt_connection_id >= c->crypto_connections_length) {
@@ -1871,11 +1932,12 @@ static int wipe_crypto_connection(Net_Crypto *c, int crypt_connection_id)
return 0;
}
-/** Get crypto connection id from public key of peer.
+/** @brief Get crypto connection id from public key of peer.
*
- * return -1 if there are no connections like we are looking for.
- * return id if it found it.
+ * @retval -1 if there are no connections like we are looking for.
+ * @return id if it found it.
*/
+non_null()
static int getcryptconnection_id(const Net_Crypto *c, const uint8_t *public_key)
{
for (uint32_t i = 0; i < c->crypto_connections_length; ++i) {
@@ -1883,7 +1945,7 @@ static int getcryptconnection_id(const Net_Crypto *c, const uint8_t *public_key)
continue;
}
- if (public_key_cmp(public_key, c->crypto_connections[i].public_key) == 0) {
+ if (pk_equal(public_key, c->crypto_connections[i].public_key)) {
return i;
}
}
@@ -1891,13 +1953,14 @@ static int getcryptconnection_id(const Net_Crypto *c, const uint8_t *public_key)
return -1;
}
-/** Add a source to the crypto connection.
+/** @brief Add a source to the crypto connection.
* This is to be used only when we have received a packet from that source.
*
- * return -1 on failure.
- * return positive number on success.
- * 0 if source was a direct UDP connection.
+ * @retval -1 on failure.
+ * @retval 0 if source was a direct UDP connection.
+ * @return positive number on success.
*/
+non_null()
static int crypto_connection_add_source(Net_Crypto *c, int crypt_connection_id, const IP_Port *source)
{
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
@@ -1920,8 +1983,10 @@ static int crypto_connection_add_source(Net_Crypto *c, int crypt_connection_id,
return 0;
}
- if (net_family_is_tcp_family(source->ip.family)) {
- if (add_tcp_number_relay_connection(c->tcp_c, conn->connection_number_tcp, source->ip.ip.v6.uint32[0]) == 0) {
+ unsigned int tcp_connections_number;
+
+ if (ip_port_to_tcp_connections_number(source, &tcp_connections_number)) {
+ if (add_tcp_number_relay_connection(c->tcp_c, conn->connection_number_tcp, tcp_connections_number) == 0) {
return 1;
}
}
@@ -1930,7 +1995,7 @@ static int crypto_connection_add_source(Net_Crypto *c, int crypt_connection_id,
}
-/** Set function to be called when someone requests a new connection to us.
+/** @brief Set function to be called when someone requests a new connection to us.
*
* The set function should return -1 on failure and 0 on success.
*
@@ -1942,12 +2007,13 @@ void new_connection_handler(Net_Crypto *c, new_connection_cb *new_connection_cal
c->new_connection_callback_object = object;
}
-/** Handle a handshake packet by someone who wants to initiate a new connection with us.
- * This calls the callback set by new_connection_handler() if the handshake is ok.
+/** @brief Handle a handshake packet by someone who wants to initiate a new connection with us.
+ * This calls the callback set by `new_connection_handler()` if the handshake is ok.
*
- * return -1 on failure.
- * return 0 on success.
+ * @retval -1 on failure.
+ * @retval 0 on success.
*/
+non_null(1, 2, 3) nullable(5)
static int handle_new_connection_handshake(Net_Crypto *c, const IP_Port *source, const uint8_t *data, uint16_t length,
void *userdata)
{
@@ -1961,8 +2027,8 @@ static int handle_new_connection_handshake(Net_Crypto *c, const IP_Port *source,
n_c.source = *source;
n_c.cookie_length = COOKIE_LENGTH;
- if (handle_crypto_handshake(c, n_c.recv_nonce, n_c.peersessionpublic_key, n_c.public_key, n_c.dht_public_key,
- n_c.cookie, data, length, nullptr) != 0) {
+ if (!handle_crypto_handshake(c, n_c.recv_nonce, n_c.peersessionpublic_key, n_c.public_key, n_c.dht_public_key,
+ n_c.cookie, data, length, nullptr)) {
free(n_c.cookie);
return -1;
}
@@ -1976,7 +2042,7 @@ static int handle_new_connection_handshake(Net_Crypto *c, const IP_Port *source,
return -1;
}
- if (public_key_cmp(n_c.dht_public_key, conn->dht_public_key) != 0) {
+ if (!pk_equal(n_c.dht_public_key, conn->dht_public_key)) {
connection_kill(c, crypt_connection_id, userdata);
} else {
if (conn->status != CRYPTO_CONN_COOKIE_REQUESTING && conn->status != CRYPTO_CONN_HANDSHAKE_SENT) {
@@ -2001,12 +2067,12 @@ static int handle_new_connection_handshake(Net_Crypto *c, const IP_Port *source,
}
}
- int ret = c->new_connection_callback(c->new_connection_callback_object, &n_c);
+ const int ret = c->new_connection_callback(c->new_connection_callback_object, &n_c);
free(n_c.cookie);
return ret;
}
-/** Accept a crypto connection.
+/** @brief Accept a crypto connection.
*
* return -1 on failure.
* return connection id on success.
@@ -2044,8 +2110,8 @@ int accept_crypto_connection(Net_Crypto *c, const New_Connection *n_c)
memcpy(conn->public_key, n_c->public_key, CRYPTO_PUBLIC_KEY_SIZE);
memcpy(conn->recv_nonce, n_c->recv_nonce, CRYPTO_NONCE_SIZE);
memcpy(conn->peersessionpublic_key, n_c->peersessionpublic_key, CRYPTO_PUBLIC_KEY_SIZE);
- random_nonce(conn->sent_nonce);
- crypto_new_keypair(conn->sessionpublic_key, conn->sessionsecret_key);
+ random_nonce(c->rng, conn->sent_nonce);
+ crypto_new_keypair(c->rng, conn->sessionpublic_key, conn->sessionsecret_key);
encrypt_precompute(conn->peersessionpublic_key, conn->sessionsecret_key, conn->shared_key);
conn->status = CRYPTO_CONN_NOT_CONFIRMED;
@@ -2066,7 +2132,7 @@ int accept_crypto_connection(Net_Crypto *c, const New_Connection *n_c)
return crypt_connection_id;
}
-/** Create a crypto connection.
+/** @brief Create a crypto connection.
* If one to that real public key already exists, return it.
*
* return -1 on failure.
@@ -2099,8 +2165,8 @@ int new_crypto_connection(Net_Crypto *c, const uint8_t *real_public_key, const u
conn->connection_number_tcp = connection_number_tcp;
memcpy(conn->public_key, real_public_key, CRYPTO_PUBLIC_KEY_SIZE);
- random_nonce(conn->sent_nonce);
- crypto_new_keypair(conn->sessionpublic_key, conn->sessionsecret_key);
+ random_nonce(c->rng, conn->sent_nonce);
+ crypto_new_keypair(c->rng, conn->sessionpublic_key, conn->sessionsecret_key);
conn->status = CRYPTO_CONN_COOKIE_REQUESTING;
conn->packet_send_rate = CRYPTO_PACKET_MIN_RATE;
conn->packet_send_rate_requested = CRYPTO_PACKET_MIN_RATE;
@@ -2108,7 +2174,7 @@ int new_crypto_connection(Net_Crypto *c, const uint8_t *real_public_key, const u
conn->rtt_time = DEFAULT_PING_CONNECTION;
memcpy(conn->dht_public_key, dht_public_key, CRYPTO_PUBLIC_KEY_SIZE);
- conn->cookie_request_number = random_u64();
+ conn->cookie_request_number = random_u64(c->rng);
uint8_t cookie_request[COOKIE_REQUEST_LENGTH];
if (create_cookie_request(c, cookie_request, conn->dht_public_key, conn->cookie_request_number,
@@ -2124,7 +2190,7 @@ int new_crypto_connection(Net_Crypto *c, const uint8_t *real_public_key, const u
return crypt_connection_id;
}
-/** Set the direct ip of the crypto connection.
+/** @brief Set the direct ip of the crypto connection.
*
* Connected is 0 if we are not sure we are connected to that person, 1 if we are sure.
*
@@ -2155,6 +2221,7 @@ int set_direct_ip_port(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip
}
+non_null(1, 3) nullable(5)
static int tcp_data_callback(void *object, int crypt_connection_id, const uint8_t *data, uint16_t length,
void *userdata)
{
@@ -2177,7 +2244,7 @@ static int tcp_data_callback(void *object, int crypt_connection_id, const uint8_
// This unlocks the mutex that at this point is locked by do_tcp before
// calling do_tcp_connections.
pthread_mutex_unlock(&c->tcp_mutex);
- int ret = handle_packet_connection(c, crypt_connection_id, data, length, 0, userdata);
+ const int ret = handle_packet_connection(c, crypt_connection_id, data, length, false, userdata);
pthread_mutex_lock(&c->tcp_mutex);
if (ret != 0) {
@@ -2188,6 +2255,7 @@ static int tcp_data_callback(void *object, int crypt_connection_id, const uint8_
return 0;
}
+non_null(1, 2, 4) nullable(6)
static int tcp_oob_callback(void *object, const uint8_t *public_key, unsigned int tcp_connections_number,
const uint8_t *data, uint16_t length, void *userdata)
{
@@ -2202,10 +2270,7 @@ static int tcp_oob_callback(void *object, const uint8_t *public_key, unsigned in
}
if (data[0] == NET_PACKET_CRYPTO_HS) {
- IP_Port source;
- source.port = 0;
- source.ip.family = net_family_tcp_family;
- source.ip.ip.v6.uint32[0] = tcp_connections_number;
+ IP_Port source = tcp_connections_number_to_ip_port(tcp_connections_number);
if (handle_new_connection_handshake(c, &source, data, length, userdata) != 0) {
return -1;
@@ -2217,7 +2282,7 @@ static int tcp_oob_callback(void *object, const uint8_t *public_key, unsigned in
return -1;
}
-/** Add a tcp relay, associating it to a crypt_connection_id.
+/** @brief Add a tcp relay, associating it to a crypt_connection_id.
*
* return 0 if it was added.
* return -1 if it wasn't.
@@ -2231,12 +2296,12 @@ int add_tcp_relay_peer(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip
}
pthread_mutex_lock(&c->tcp_mutex);
- int ret = add_tcp_relay_connection(c->tcp_c, conn->connection_number_tcp, ip_port, public_key);
+ const int ret = add_tcp_relay_connection(c->tcp_c, conn->connection_number_tcp, ip_port, public_key);
pthread_mutex_unlock(&c->tcp_mutex);
return ret;
}
-/** Add a tcp relay to the array.
+/** @brief Add a tcp relay to the array.
*
* return 0 if it was added.
* return -1 if it wasn't.
@@ -2244,12 +2309,12 @@ int add_tcp_relay_peer(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip
int add_tcp_relay(Net_Crypto *c, const IP_Port *ip_port, const uint8_t *public_key)
{
pthread_mutex_lock(&c->tcp_mutex);
- int ret = add_tcp_relay_global(c->tcp_c, ip_port, public_key);
+ const int ret = add_tcp_relay_global(c->tcp_c, ip_port, public_key);
pthread_mutex_unlock(&c->tcp_mutex);
return ret;
}
-/** Return a random TCP connection number for use in send_tcp_onion_request.
+/** @brief Return a random 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.
@@ -2260,13 +2325,27 @@ int add_tcp_relay(Net_Crypto *c, const IP_Port *ip_port, const uint8_t *public_k
int get_random_tcp_con_number(Net_Crypto *c)
{
pthread_mutex_lock(&c->tcp_mutex);
- int ret = get_random_tcp_onion_conn_number(c->tcp_c);
+ const int ret = get_random_tcp_onion_conn_number(c->tcp_c);
pthread_mutex_unlock(&c->tcp_mutex);
return ret;
}
-/** Send an onion packet via the TCP relay corresponding to tcp_connections_number.
+/** @brief Put IP_Port of a random onion TCP connection in ip_port.
+ *
+ * return true on success.
+ * return false on failure.
+ */
+bool get_random_tcp_conn_ip_port(Net_Crypto *c, IP_Port *ip_port)
+{
+ pthread_mutex_lock(&c->tcp_mutex);
+ const bool ret = tcp_get_random_conn_ip_port(c->tcp_c, ip_port);
+ pthread_mutex_unlock(&c->tcp_mutex);
+
+ return ret;
+}
+
+/** @brief Send an onion packet via the TCP relay corresponding to tcp_connections_number.
*
* return 0 on success.
* return -1 on failure.
@@ -2274,13 +2353,34 @@ int get_random_tcp_con_number(Net_Crypto *c)
int send_tcp_onion_request(Net_Crypto *c, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length)
{
pthread_mutex_lock(&c->tcp_mutex);
- int ret = tcp_send_onion_request(c->tcp_c, tcp_connections_number, data, length);
+ const int ret = tcp_send_onion_request(c->tcp_c, tcp_connections_number, data, length);
+ pthread_mutex_unlock(&c->tcp_mutex);
+
+ return ret;
+}
+
+/**
+ * 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.
+ */
+int send_tcp_forward_request(const Logger *logger, Net_Crypto *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)
+{
+ pthread_mutex_lock(&c->tcp_mutex);
+ const int ret = tcp_send_forward_request(logger, c->tcp_c, tcp_forwarder, dht_node,
+ chain_keys, chain_length, data, data_length);
pthread_mutex_unlock(&c->tcp_mutex);
return ret;
}
-/** Copy a maximum of num TCP relays we are connected to to tcp_relays.
+/** @brief Copy a maximum of num random 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.
@@ -2293,12 +2393,26 @@ unsigned int copy_connected_tcp_relays(Net_Crypto *c, Node_format *tcp_relays, u
}
pthread_mutex_lock(&c->tcp_mutex);
- unsigned int ret = tcp_copy_connected_relays(c->tcp_c, tcp_relays, num);
+ const unsigned int ret = tcp_copy_connected_relays(c->tcp_c, tcp_relays, num);
pthread_mutex_unlock(&c->tcp_mutex);
return ret;
}
+uint32_t copy_connected_tcp_relays_index(Net_Crypto *c, Node_format *tcp_relays, uint16_t num, uint32_t idx)
+{
+ if (num == 0) {
+ return 0;
+ }
+
+ pthread_mutex_lock(&c->tcp_mutex);
+ const uint32_t ret = tcp_copy_connected_relays_index(c->tcp_c, tcp_relays, num, idx);
+ pthread_mutex_unlock(&c->tcp_mutex);
+
+ return ret;
+}
+
+non_null()
static void do_tcp(Net_Crypto *c, void *userdata)
{
pthread_mutex_lock(&c->tcp_mutex);
@@ -2328,7 +2442,7 @@ static void do_tcp(Net_Crypto *c, void *userdata)
}
}
-/** Set function to be called when connection with crypt_connection_id goes connects/disconnects.
+/** @brief Set function to be called when connection with crypt_connection_id goes connects/disconnects.
*
* The set function should return -1 on failure and 0 on success.
* Note that if this function is set, the connection will clear itself on disconnect.
@@ -2353,7 +2467,7 @@ int connection_status_handler(const Net_Crypto *c, int crypt_connection_id,
return 0;
}
-/** Set function to be called when connection with crypt_connection_id receives a lossless data packet of length.
+/** @brief Set function to be called when connection with crypt_connection_id receives a lossless data packet of length.
*
* The set function should return -1 on failure and 0 on success.
* Object and id will be passed to this function untouched.
@@ -2376,7 +2490,7 @@ int connection_data_handler(const Net_Crypto *c, int crypt_connection_id,
return 0;
}
-/** Set function to be called when connection with crypt_connection_id receives a lossy data packet of length.
+/** @brief Set function to be called when connection with crypt_connection_id receives a lossy data packet of length.
*
* The set function should return -1 on failure and 0 on success.
* Object and id will be passed to this function untouched.
@@ -2401,7 +2515,7 @@ int connection_lossy_data_handler(const Net_Crypto *c, int crypt_connection_id,
}
-/** Set the function for this friend that will be callbacked with object and number if
+/** @brief Set the function for this friend that will be callbacked with object and number if
* the friend sends us a different dht public key than we have associated to him.
*
* If this function is called, the connection should be recreated with the new public key.
@@ -2425,11 +2539,12 @@ int nc_dht_pk_callback(const Net_Crypto *c, int crypt_connection_id, dht_pk_cb *
return 0;
}
-/** Get the crypto connection id from the ip_port.
+/** @brief Get the crypto connection id from the ip_port.
*
* return -1 on failure.
* return connection id on success.
*/
+non_null()
static int crypto_id_ip_port(const Net_Crypto *c, const IP_Port *ip_port)
{
return bs_list_find(&c->ip_port_list, (const uint8_t *)ip_port);
@@ -2437,7 +2552,7 @@ static int crypto_id_ip_port(const Net_Crypto *c, const IP_Port *ip_port)
#define CRYPTO_MIN_PACKET_SIZE (1 + sizeof(uint16_t) + CRYPTO_MAC_SIZE)
-/** Handle raw UDP packets coming directly from the socket.
+/** @brief Handle raw UDP packets coming directly from the socket.
*
* Handles:
* Cookie response packets.
@@ -2445,6 +2560,7 @@ static int crypto_id_ip_port(const Net_Crypto *c, const IP_Port *ip_port)
* Crypto data packets.
*
*/
+non_null(1, 2, 3) nullable(5)
static int udp_handle_packet(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
@@ -2468,7 +2584,7 @@ static int udp_handle_packet(void *object, const IP_Port *source, const uint8_t
return 0;
}
- if (handle_packet_connection(c, crypt_connection_id, packet, length, 1, userdata) != 0) {
+ if (handle_packet_connection(c, crypt_connection_id, packet, length, true, userdata) != 0) {
return 1;
}
@@ -2490,24 +2606,27 @@ static int udp_handle_packet(void *object, const IP_Port *source, const uint8_t
return 0;
}
-/** The dT for the average packet receiving rate calculations.
- * Also used as the */
+/** @brief The dT for the average packet receiving rate calculations.
+ * Also used as the
+ */
#define PACKET_COUNTER_AVERAGE_INTERVAL 50
-/** Ratio of recv queue size / recv packet rate (in seconds) times
+/** @brief Ratio of recv queue size / recv packet rate (in seconds) times
* the number of ms between request packets to send at that ratio
*/
#define REQUEST_PACKETS_COMPARE_CONSTANT (0.125 * 100.0)
-/** Timeout for increasing speed after congestion event (in ms). */
+/** @brief Timeout for increasing speed after congestion event (in ms). */
#define CONGESTION_EVENT_TIMEOUT 1000
-/** If the send queue is SEND_QUEUE_RATIO times larger than the
+/**
+ * If the send queue is SEND_QUEUE_RATIO times larger than the
* calculated link speed the packet send speed will be reduced
* by a value depending on this number.
*/
#define SEND_QUEUE_RATIO 2.0
+non_null()
static void send_crypto_packets(Net_Crypto *c)
{
const uint64_t temp_time = current_time_monotonic(c->mono_time);
@@ -2537,7 +2656,7 @@ static void send_crypto_packets(Net_Crypto *c)
double request_packet_interval = REQUEST_PACKETS_COMPARE_CONSTANT / ((num_packets_array(
&conn->recv_array) + 1.0) / (conn->packet_recv_rate + 1.0));
- double request_packet_interval2 = ((CRYPTO_PACKET_MIN_RATE / conn->packet_recv_rate) *
+ const double request_packet_interval2 = ((CRYPTO_PACKET_MIN_RATE / conn->packet_recv_rate) *
(double)CRYPTO_SEND_PACKET_INTERVAL) + (double)PACKET_COUNTER_AVERAGE_INTERVAL;
if (request_packet_interval2 < request_packet_interval) {
@@ -2564,37 +2683,37 @@ static void send_crypto_packets(Net_Crypto *c)
}
if ((PACKET_COUNTER_AVERAGE_INTERVAL + conn->packet_counter_set) < temp_time) {
- const double dt = temp_time - conn->packet_counter_set;
+ const double dt = (double)(temp_time - conn->packet_counter_set);
conn->packet_recv_rate = (double)conn->packet_counter / (dt / 1000.0);
conn->packet_counter = 0;
conn->packet_counter_set = temp_time;
- uint32_t packets_sent = conn->packets_sent;
+ const uint32_t packets_sent = conn->packets_sent;
conn->packets_sent = 0;
- uint32_t packets_resent = conn->packets_resent;
+ const uint32_t packets_resent = conn->packets_resent;
conn->packets_resent = 0;
/* conjestion control
* calculate a new value of conn->packet_send_rate based on some data
*/
- unsigned int pos = conn->last_sendqueue_counter % CONGESTION_QUEUE_ARRAY_SIZE;
+ const unsigned int pos = conn->last_sendqueue_counter % CONGESTION_QUEUE_ARRAY_SIZE;
conn->last_sendqueue_size[pos] = num_packets_array(&conn->send_array);
long signed int sum = 0;
sum = (long signed int)conn->last_sendqueue_size[pos] -
(long signed int)conn->last_sendqueue_size[(pos + 1) % CONGESTION_QUEUE_ARRAY_SIZE];
- unsigned int n_p_pos = conn->last_sendqueue_counter % CONGESTION_LAST_SENT_ARRAY_SIZE;
+ const unsigned int n_p_pos = conn->last_sendqueue_counter % CONGESTION_LAST_SENT_ARRAY_SIZE;
conn->last_num_packets_sent[n_p_pos] = packets_sent;
conn->last_num_packets_resent[n_p_pos] = packets_resent;
conn->last_sendqueue_counter = (conn->last_sendqueue_counter + 1) %
(CONGESTION_QUEUE_ARRAY_SIZE * CONGESTION_LAST_SENT_ARRAY_SIZE);
- bool direct_connected = 0;
+ bool direct_connected = false;
/* return value can be ignored since the `if` above ensures the connection is established */
crypto_connection_status(c, i, &direct_connected, nullptr);
@@ -2605,14 +2724,14 @@ static void send_crypto_packets(Net_Crypto *c)
// TODO(irungentoo): use real delay
unsigned int delay = (unsigned int)(((double)conn->rtt_time / PACKET_COUNTER_AVERAGE_INTERVAL) + 0.5);
- unsigned int packets_set_rem_array = CONGESTION_LAST_SENT_ARRAY_SIZE - CONGESTION_QUEUE_ARRAY_SIZE;
+ const unsigned int packets_set_rem_array = CONGESTION_LAST_SENT_ARRAY_SIZE - CONGESTION_QUEUE_ARRAY_SIZE;
if (delay > packets_set_rem_array) {
delay = packets_set_rem_array;
}
for (unsigned j = 0; j < CONGESTION_QUEUE_ARRAY_SIZE; ++j) {
- unsigned int ind = (j + (packets_set_rem_array - delay) + n_p_pos) % CONGESTION_LAST_SENT_ARRAY_SIZE;
+ const unsigned int ind = (j + (packets_set_rem_array - delay) + n_p_pos) % CONGESTION_LAST_SENT_ARRAY_SIZE;
total_sent += conn->last_num_packets_sent[ind];
total_resent += conn->last_num_packets_resent[ind];
}
@@ -2626,18 +2745,18 @@ static void send_crypto_packets(Net_Crypto *c)
}
/* if queue is too big only allow resending packets. */
- uint32_t npackets = num_packets_array(&conn->send_array);
+ const uint32_t npackets = num_packets_array(&conn->send_array);
double min_speed = 1000.0 * (((double)total_sent) / ((double)CONGESTION_QUEUE_ARRAY_SIZE *
PACKET_COUNTER_AVERAGE_INTERVAL));
- double min_speed_request = 1000.0 * (((double)(total_sent + total_resent)) / (
+ const double min_speed_request = 1000.0 * (((double)(total_sent + total_resent)) / (
(double)CONGESTION_QUEUE_ARRAY_SIZE * PACKET_COUNTER_AVERAGE_INTERVAL));
if (min_speed < CRYPTO_PACKET_MIN_RATE) {
min_speed = CRYPTO_PACKET_MIN_RATE;
}
- double send_array_ratio = (double)npackets / min_speed;
+ const double send_array_ratio = (double)npackets / min_speed;
// TODO(irungentoo): Improve formula?
if (send_array_ratio > SEND_QUEUE_RATIO && CRYPTO_MIN_QUEUE_LENGTH < npackets) {
@@ -2670,8 +2789,8 @@ static void send_crypto_packets(Net_Crypto *c)
double n_packets = conn->packet_send_rate * (((double)(temp_time - conn->last_packets_left_set)) / 1000.0);
n_packets += conn->last_packets_left_rem;
- uint32_t num_packets = n_packets;
- double rem = n_packets - (double)num_packets;
+ const uint32_t num_packets = n_packets;
+ const double rem = n_packets - (double)num_packets;
if (conn->packets_left > num_packets * 4 + CRYPTO_MIN_QUEUE_LENGTH) {
conn->packets_left = num_packets * 4 + CRYPTO_MIN_QUEUE_LENGTH;
@@ -2702,7 +2821,7 @@ static void send_crypto_packets(Net_Crypto *c)
}
}
- int ret = send_requested_packets(c, i, conn->packets_left_requested);
+ const int ret = send_requested_packets(c, i, conn->packets_left_requested);
if (ret != -1) {
conn->packets_left_requested -= ret;
@@ -2730,7 +2849,7 @@ static void send_crypto_packets(Net_Crypto *c)
}
if (total_send_rate > CRYPTO_PACKET_MIN_RATE) {
- sleep_time = (1000.0 / total_send_rate);
+ sleep_time = 1000.0 / total_send_rate;
if (c->current_sleep_time > sleep_time) {
c->current_sleep_time = sleep_time + 1;
@@ -2744,16 +2863,18 @@ static void send_crypto_packets(Net_Crypto *c)
}
}
-/** Return 1 if max speed was reached for this connection (no more data can be physically through the pipe).
- * Return 0 if it wasn't reached.
+/**
+ * @retval 1 if max speed was reached for this connection (no more data can be physically through the pipe).
+ * @retval 0 if it wasn't reached.
*/
bool max_speed_reached(Net_Crypto *c, int crypt_connection_id)
{
return reset_max_speed_reached(c, crypt_connection_id) != 0;
}
-/** returns the number of packet slots left in the sendbuffer.
- * return 0 if failure.
+/**
+ * @return the number of packet slots left in the sendbuffer.
+ * @retval 0 if failure.
*/
uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connection_id)
{
@@ -2763,7 +2884,7 @@ uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connecti
return 0;
}
- uint32_t max_packets = CRYPTO_PACKET_BUFFER_SIZE - num_packets_array(&conn->send_array);
+ const uint32_t max_packets = CRYPTO_PACKET_BUFFER_SIZE - num_packets_array(&conn->send_array);
if (conn->packets_left < max_packets) {
return conn->packets_left;
@@ -2772,7 +2893,7 @@ uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connecti
return max_packets;
}
-/** Sends a lossless cryptopacket.
+/** @brief Sends a lossless cryptopacket.
*
* return -1 if data could not be put in packet queue.
* return positive packet number if data was put into the queue.
@@ -2782,31 +2903,38 @@ uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connecti
* congestion_control: should congestion control apply to this packet?
*/
int64_t write_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t *data, uint16_t length,
- uint8_t congestion_control)
+ bool congestion_control)
{
if (length == 0) {
+ // We need at least a packet id.
+ LOGGER_ERROR(c->log, "rejecting empty packet for crypto connection %d", crypt_connection_id);
return -1;
}
if (data[0] < PACKET_ID_RANGE_LOSSLESS_START || data[0] > PACKET_ID_RANGE_LOSSLESS_END) {
+ LOGGER_ERROR(c->log, "rejecting lossless packet with out-of-range id %d", data[0]);
return -1;
}
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
if (conn == nullptr) {
+ LOGGER_WARNING(c->log, "invalid crypt connection id %d", crypt_connection_id);
return -1;
}
if (conn->status != CRYPTO_CONN_ESTABLISHED) {
+ LOGGER_WARNING(c->log, "attempted to send packet to non-established connection %d", crypt_connection_id);
return -1;
}
if (congestion_control && conn->packets_left == 0) {
+ LOGGER_ERROR(c->log, "congestion control: rejecting packet of length %d on crypt connection %d", length,
+ crypt_connection_id);
return -1;
}
- int64_t ret = send_lossless_packet(c, crypt_connection_id, data, length, congestion_control);
+ const int64_t ret = send_lossless_packet(c, crypt_connection_id, data, length, congestion_control);
if (ret == -1) {
return -1;
@@ -2821,7 +2949,7 @@ int64_t write_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t
return ret;
}
-/** Check if packet_number was received by the other side.
+/** @brief Check if packet_number was received by the other side.
*
* packet_number must be a valid packet number of a packet sent on this connection.
*
@@ -2843,8 +2971,8 @@ int cryptpacket_received(const Net_Crypto *c, int crypt_connection_id, uint32_t
return -1;
}
- uint32_t num = num_packets_array(&conn->send_array);
- uint32_t num1 = packet_number - conn->send_array.buffer_start;
+ const uint32_t num = num_packets_array(&conn->send_array);
+ const uint32_t num1 = packet_number - conn->send_array.buffer_start;
if (num >= num1) {
return -1;
@@ -2853,7 +2981,7 @@ int cryptpacket_received(const Net_Crypto *c, int crypt_connection_id, uint32_t
return 0;
}
-/** Sends a lossy cryptopacket.
+/** @brief Sends a lossy cryptopacket.
*
* return -1 on failure.
* return 0 on success.
@@ -2878,10 +3006,10 @@ int send_lossy_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t
int ret = -1;
- if (conn) {
+ if (conn != nullptr) {
pthread_mutex_lock(conn->mutex);
- uint32_t buffer_start = conn->recv_array.buffer_start;
- uint32_t buffer_end = conn->send_array.buffer_end;
+ const uint32_t buffer_start = conn->recv_array.buffer_start;
+ const uint32_t buffer_end = conn->send_array.buffer_end;
pthread_mutex_unlock(conn->mutex);
ret = send_data_packet_helper(c, crypt_connection_id, buffer_start, buffer_end, data, length);
}
@@ -2893,7 +3021,7 @@ int send_lossy_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t
return ret;
}
-/** Kill a crypto connection.
+/** @brief Kill a crypto connection.
*
* return -1 on failure.
* return 0 on success.
@@ -2904,7 +3032,7 @@ int crypto_kill(Net_Crypto *c, int crypt_connection_id)
int ret = -1;
- if (conn) {
+ if (conn != nullptr) {
if (conn->status == CRYPTO_CONN_ESTABLISHED) {
send_kill_packet(c, crypt_connection_id);
}
@@ -2925,7 +3053,7 @@ int crypto_kill(Net_Crypto *c, int crypt_connection_id)
}
bool crypto_connection_status(const Net_Crypto *c, int crypt_connection_id, bool *direct_connected,
- unsigned int *online_tcp_relays)
+ uint32_t *online_tcp_relays)
{
const Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
@@ -2933,19 +3061,19 @@ bool crypto_connection_status(const Net_Crypto *c, int crypt_connection_id, bool
return false;
}
- if (direct_connected) {
- *direct_connected = 0;
+ if (direct_connected != nullptr) {
+ *direct_connected = false;
const uint64_t current_time = mono_time_get(c->mono_time);
if ((UDP_DIRECT_TIMEOUT + conn->direct_lastrecv_timev4) > current_time) {
- *direct_connected = 1;
+ *direct_connected = true;
} else if ((UDP_DIRECT_TIMEOUT + conn->direct_lastrecv_timev6) > current_time) {
- *direct_connected = 1;
+ *direct_connected = true;
}
}
- if (online_tcp_relays) {
+ if (online_tcp_relays != nullptr) {
*online_tcp_relays = tcp_connection_to_online_tcp_relays(c->tcp_c, conn->connection_number_tcp);
}
@@ -2954,11 +3082,11 @@ bool crypto_connection_status(const Net_Crypto *c, int crypt_connection_id, bool
void new_keys(Net_Crypto *c)
{
- crypto_new_keypair(c->self_public_key, c->self_secret_key);
+ crypto_new_keypair(c->rng, c->self_public_key, c->self_secret_key);
}
-/** Save the public and private keys to the keys array.
- * Length must be CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SECRET_KEY_SIZE.
+/** @brief Save the public and private keys to the keys array.
+ * Length must be CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SECRET_KEY_SIZE.
*
* TODO(irungentoo): Save only secret key.
*/
@@ -2968,7 +3096,7 @@ void save_keys(const Net_Crypto *c, uint8_t *keys)
memcpy(keys + CRYPTO_PUBLIC_KEY_SIZE, c->self_secret_key, CRYPTO_SECRET_KEY_SIZE);
}
-/** Load the secret key.
+/** @brief Load the secret key.
* Length must be CRYPTO_SECRET_KEY_SIZE.
*/
void load_secret_key(Net_Crypto *c, const uint8_t *sk)
@@ -2977,10 +3105,10 @@ void load_secret_key(Net_Crypto *c, const uint8_t *sk)
crypto_derive_public_key(c->self_public_key, c->self_secret_key);
}
-/** Create new instance of Net_Crypto.
- * Sets all the global connection variables to their default values.
+/** @brief Create new instance of Net_Crypto.
+ * Sets all the global connection variables to their default values.
*/
-Net_Crypto *new_net_crypto(const Logger *log, Mono_Time *mono_time, DHT *dht, const TCP_Proxy_Info *proxy_info)
+Net_Crypto *new_net_crypto(const Logger *log, const Random *rng, const Network *ns, Mono_Time *mono_time, DHT *dht, const TCP_Proxy_Info *proxy_info)
{
if (dht == nullptr) {
return nullptr;
@@ -2993,9 +3121,11 @@ Net_Crypto *new_net_crypto(const Logger *log, Mono_Time *mono_time, DHT *dht, co
}
temp->log = log;
+ temp->rng = rng;
temp->mono_time = mono_time;
+ temp->ns = ns;
- temp->tcp_c = new_tcp_connections(log, mono_time, dht_get_self_secret_key(dht), proxy_info);
+ temp->tcp_c = new_tcp_connections(log, rng, ns, mono_time, dht_get_self_secret_key(dht), proxy_info);
if (temp->tcp_c == nullptr) {
free(temp);
@@ -3015,7 +3145,7 @@ Net_Crypto *new_net_crypto(const Logger *log, Mono_Time *mono_time, DHT *dht, co
temp->dht = dht;
new_keys(temp);
- new_symmetric_key(temp->secret_symmetric_key);
+ new_symmetric_key(rng, temp->secret_symmetric_key);
temp->current_sleep_time = CRYPTO_SEND_PACKET_INTERVAL;
@@ -3029,6 +3159,7 @@ Net_Crypto *new_net_crypto(const Logger *log, Mono_Time *mono_time, DHT *dht, co
return temp;
}
+non_null(1) nullable(2)
static void kill_timedout(Net_Crypto *c, void *userdata)
{
for (uint32_t i = 0; i < c->crypto_connections_length; ++i) {
@@ -3051,15 +3182,14 @@ static void kill_timedout(Net_Crypto *c, void *userdata)
if (conn->status == CRYPTO_CONN_ESTABLISHED) {
// TODO(irungentoo): add a timeout here?
- do_timeout_here();
+ /* do_timeout_here(); */
}
#endif
}
}
-/** return the optimal interval in ms for running do_net_crypto.
- */
+/** return the optimal interval in ms for running do_net_crypto. */
uint32_t crypto_run_interval(const Net_Crypto *c)
{
return c->current_sleep_time;
@@ -3075,6 +3205,10 @@ void do_net_crypto(Net_Crypto *c, void *userdata)
void kill_net_crypto(Net_Crypto *c)
{
+ if (c == nullptr) {
+ return;
+ }
+
for (uint32_t i = 0; i < c->crypto_connections_length; ++i) {
crypto_kill(c, i);
}
diff --git a/protocols/Tox/libtox/src/toxcore/net_crypto.h b/protocols/Tox/libtox/src/toxcore/net_crypto.h
index 9a3bd1e21c..f6e062f3be 100644
--- a/protocols/Tox/libtox/src/toxcore/net_crypto.h
+++ b/protocols/Tox/libtox/src/toxcore/net_crypto.h
@@ -20,7 +20,7 @@
/*** Ranges. */
-/** Packets in this range are reserved for net_crypto internal use. */
+/** Packets in this range are reserved for net_crypto events_alloc use. */
#define PACKET_ID_RANGE_RESERVED_START 0
#define PACKET_ID_RANGE_RESERVED_END 15
/** Packets in this range are reserved for Messenger use. */
@@ -86,8 +86,10 @@
/** Interval in ms between sending cookie request/handshake packets. */
#define CRYPTO_SEND_PACKET_INTERVAL 1000
-/** The maximum number of times we try to send the cookie request and handshake
- * before giving up. */
+/**
+ * The maximum number of times we try to send the cookie request and handshake
+ * before giving up.
+ */
#define MAX_NUM_SENDPACKET_TRIES 8
/** The timeout of no received UDP packets before the direct UDP connection is considered dead. */
@@ -99,8 +101,10 @@
/** All packets will be padded a number of bytes based on this number. */
#define CRYPTO_MAX_PADDING 8
-/** Base current transfer speed on last CONGESTION_QUEUE_ARRAY_SIZE number of points taken
- * at the dT defined in net_crypto.c */
+/**
+ * Base current transfer speed on last CONGESTION_QUEUE_ARRAY_SIZE number of points taken
+ * at the dT defined in net_crypto.c
+ */
#define CONGESTION_QUEUE_ARRAY_SIZE 12
#define CONGESTION_LAST_SENT_ARRAY_SIZE (CONGESTION_QUEUE_ARRAY_SIZE * 2)
@@ -110,10 +114,10 @@
typedef struct Net_Crypto Net_Crypto;
-const uint8_t *nc_get_self_public_key(const Net_Crypto *c);
-const uint8_t *nc_get_self_secret_key(const Net_Crypto *c);
-TCP_Connections *nc_get_tcp_c(const Net_Crypto *c);
-DHT *nc_get_dht(const Net_Crypto *c);
+non_null() const uint8_t *nc_get_self_public_key(const Net_Crypto *c);
+non_null() const uint8_t *nc_get_self_secret_key(const Net_Crypto *c);
+non_null() TCP_Connections *nc_get_tcp_c(const Net_Crypto *c);
+non_null() DHT *nc_get_dht(const Net_Crypto *c);
typedef struct New_Connection {
IP_Port source;
@@ -125,45 +129,49 @@ typedef struct New_Connection {
uint8_t cookie_length;
} New_Connection;
-typedef int connection_status_cb(void *object, int id, uint8_t status, void *userdata);
+typedef int connection_status_cb(void *object, int id, bool status, void *userdata);
typedef int connection_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata);
typedef int connection_lossy_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata);
typedef void dht_pk_cb(void *data, int32_t number, const uint8_t *dht_public_key, void *userdata);
typedef int new_connection_cb(void *object, const New_Connection *n_c);
-/** Set function to be called when someone requests a new connection to us.
+/** @brief Set function to be called when someone requests a new connection to us.
*
* The set function should return -1 on failure and 0 on success.
*
* n_c is only valid for the duration of the function call.
*/
+non_null()
void new_connection_handler(Net_Crypto *c, new_connection_cb *new_connection_callback, void *object);
-/** Accept a crypto connection.
+/** @brief Accept a crypto connection.
*
* return -1 on failure.
* return connection id on success.
*/
+non_null()
int accept_crypto_connection(Net_Crypto *c, const New_Connection *n_c);
-/** Create a crypto connection.
+/** @brief Create a crypto connection.
* If one to that real public key already exists, return it.
*
* return -1 on failure.
* return connection id on success.
*/
+non_null()
int new_crypto_connection(Net_Crypto *c, const uint8_t *real_public_key, const uint8_t *dht_public_key);
-/** Set the direct ip of the crypto connection.
+/** @brief Set the direct ip of the crypto connection.
*
* Connected is 0 if we are not sure we are connected to that person, 1 if we are sure.
*
* return -1 on failure.
* return 0 on success.
*/
+non_null()
int set_direct_ip_port(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip_port, bool connected);
-/** Set function to be called when connection with crypt_connection_id goes connects/disconnects.
+/** @brief Set function to be called when connection with crypt_connection_id goes connects/disconnects.
*
* The set function should return -1 on failure and 0 on success.
* Note that if this function is set, the connection will clear itself on disconnect.
@@ -173,10 +181,11 @@ int set_direct_ip_port(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip
* return -1 on failure.
* return 0 on success.
*/
+non_null()
int connection_status_handler(const Net_Crypto *c, int crypt_connection_id,
connection_status_cb *connection_status_callback, void *object, int id);
-/** Set function to be called when connection with crypt_connection_id receives a lossless data packet of length.
+/** @brief Set function to be called when connection with crypt_connection_id receives a lossless data packet of length.
*
* The set function should return -1 on failure and 0 on success.
* Object and id will be passed to this function untouched.
@@ -184,11 +193,12 @@ int connection_status_handler(const Net_Crypto *c, int crypt_connection_id,
* return -1 on failure.
* return 0 on success.
*/
+non_null()
int connection_data_handler(const Net_Crypto *c, int crypt_connection_id,
connection_data_cb *connection_data_callback, void *object, int id);
-/** Set function to be called when connection with crypt_connection_id receives a lossy data packet of length.
+/** @brief Set function to be called when connection with crypt_connection_id receives a lossy data packet of length.
*
* The set function should return -1 on failure and 0 on success.
* Object and id will be passed to this function untouched.
@@ -196,10 +206,11 @@ int connection_data_handler(const Net_Crypto *c, int crypt_connection_id,
* return -1 on failure.
* return 0 on success.
*/
+non_null()
int connection_lossy_data_handler(const Net_Crypto *c, int crypt_connection_id,
connection_lossy_data_cb *connection_lossy_data_callback, void *object, int id);
-/** Set the function for this friend that will be callbacked with object and number if
+/** @brief Set the function for this friend that will be callbacked with object and number if
* the friend sends us a different dht public key than we have associated to him.
*
* If this function is called, the connection should be recreated with the new public key.
@@ -209,20 +220,25 @@ int connection_lossy_data_handler(const Net_Crypto *c, int crypt_connection_id,
* return -1 on failure.
* return 0 on success.
*/
-int nc_dht_pk_callback(const Net_Crypto *c, int crypt_connection_id, dht_pk_cb *function, void *object,
- uint32_t number);
+non_null()
+int nc_dht_pk_callback(const Net_Crypto *c, int crypt_connection_id,
+ dht_pk_cb *function, void *object, uint32_t number);
-/** returns the number of packet slots left in the sendbuffer.
- * return 0 if failure.
+/**
+ * @return the number of packet slots left in the sendbuffer.
+ * @retval 0 if failure.
*/
+non_null()
uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connection_id);
-/** Return 1 if max speed was reached for this connection (no more data can be physically through the pipe).
- * Return 0 if it wasn't reached.
+/**
+ * @retval 1 if max speed was reached for this connection (no more data can be physically through the pipe).
+ * @retval 0 if it wasn't reached.
*/
+non_null()
bool max_speed_reached(Net_Crypto *c, int crypt_connection_id);
-/** Sends a lossless cryptopacket.
+/** @brief Sends a lossless cryptopacket.
*
* return -1 if data could not be put in packet queue.
* return positive packet number if data was put into the queue.
@@ -231,10 +247,11 @@ bool max_speed_reached(Net_Crypto *c, int crypt_connection_id);
*
* congestion_control: should congestion control apply to this packet?
*/
-int64_t write_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t *data, uint16_t length,
- uint8_t congestion_control);
+non_null()
+int64_t write_cryptpacket(Net_Crypto *c, int crypt_connection_id,
+ const uint8_t *data, uint16_t length, bool congestion_control);
-/** Check if packet_number was received by the other side.
+/** @brief Check if packet_number was received by the other side.
*
* packet_number must be a valid packet number of a packet sent on this connection.
*
@@ -248,32 +265,37 @@ int64_t write_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t
* It CANNOT be simplified to `packet_number < buffer_start`, as it will fail
* when `buffer_end < buffer_start`.
*/
+non_null()
int cryptpacket_received(const Net_Crypto *c, int crypt_connection_id, uint32_t packet_number);
-/** Sends a lossy cryptopacket.
+/** @brief Sends a lossy cryptopacket.
*
* return -1 on failure.
* return 0 on success.
*
* The first byte of data must be in the PACKET_ID_RANGE_LOSSY.
*/
+non_null()
int send_lossy_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t *data, uint16_t length);
-/** Add a tcp relay, associating it to a crypt_connection_id.
+/** @brief Add a tcp relay, associating it to a crypt_connection_id.
*
* return 0 if it was added.
* return -1 if it wasn't.
*/
-int add_tcp_relay_peer(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip_port, const uint8_t *public_key);
+non_null()
+int add_tcp_relay_peer(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip_port,
+ const uint8_t *public_key);
-/** Add a tcp relay to the array.
+/** @brief Add a tcp relay to the array.
*
* return 0 if it was added.
* return -1 if it wasn't.
*/
+non_null()
int add_tcp_relay(Net_Crypto *c, const IP_Port *ip_port, const uint8_t *public_key);
-/** Return a random TCP connection number for use in send_tcp_onion_request.
+/** @brief Return a random 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.
@@ -281,69 +303,111 @@ int add_tcp_relay(Net_Crypto *c, const IP_Port *ip_port, const uint8_t *public_k
* return TCP connection number on success.
* return -1 on failure.
*/
+non_null()
int get_random_tcp_con_number(Net_Crypto *c);
-/** Send an onion packet via the TCP relay corresponding to tcp_connections_number.
+/** @brief Put IP_Port of a random onion TCP connection in ip_port.
+ *
+ * return true on success.
+ * return false on failure.
+ */
+non_null()
+bool get_random_tcp_conn_ip_port(Net_Crypto *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 send_tcp_onion_request(Net_Crypto *c, unsigned int tcp_connections_number,
+ const uint8_t *data, uint16_t length);
+
+/**
+ * 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.
*/
-int send_tcp_onion_request(Net_Crypto *c, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length);
+non_null()
+int send_tcp_forward_request(const Logger *logger, Net_Crypto *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);
-/** Copy a maximum of num TCP relays we are connected to to tcp_relays.
+/** @brief Copy a maximum of num random 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()
unsigned int copy_connected_tcp_relays(Net_Crypto *c, Node_format *tcp_relays, uint16_t num);
-/** Kill a crypto connection.
+/**
+ * Copy a maximum of `max_num` TCP relays we are connected to starting at the index in the TCP relay array
+ * for `tcp_c` designated by `idx`. If idx is greater than the array length a modulo operation is performed.
+ *
+ * Returns the number of relays successfully copied.
+ */
+non_null()
+uint32_t copy_connected_tcp_relays_index(Net_Crypto *c, Node_format *tcp_relays, uint16_t num, uint32_t idx);
+
+/** @brief Kill a crypto connection.
*
* return -1 on failure.
* return 0 on success.
*/
+non_null()
int crypto_kill(Net_Crypto *c, int crypt_connection_id);
-/** return true if connection is valid, false otherwise
+/**
+ * @retval true if connection is valid, false otherwise
*
* sets direct_connected to 1 if connection connects directly to other, 0 if it isn't.
* sets online_tcp_relays to the number of connected tcp relays this connection has.
*/
-bool crypto_connection_status(const Net_Crypto *c, int crypt_connection_id, bool *direct_connected,
- unsigned int *online_tcp_relays);
+non_null(1, 3) nullable(4)
+bool crypto_connection_status(
+ const Net_Crypto *c, int crypt_connection_id, bool *direct_connected, uint32_t *online_tcp_relays);
-/** Generate our public and private keys.
- * Only call this function the first time the program starts.
+/** @brief Generate our public and private keys.
+ * Only call this function the first time the program starts.
*/
+non_null()
void new_keys(Net_Crypto *c);
-/** Save the public and private keys to the keys array.
- * Length must be CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SECRET_KEY_SIZE.
+/** @brief Save the public and private keys to the keys array.
+ * Length must be CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SECRET_KEY_SIZE.
*
* TODO(irungentoo): Save only secret key.
*/
+non_null()
void save_keys(const Net_Crypto *c, uint8_t *keys);
-/** Load the secret key.
+/** @brief Load the secret key.
* Length must be CRYPTO_SECRET_KEY_SIZE.
*/
+non_null()
void load_secret_key(Net_Crypto *c, const uint8_t *sk);
-/** Create new instance of Net_Crypto.
- * Sets all the global connection variables to their default values.
+/** @brief Create new instance of Net_Crypto.
+ * Sets all the global connection variables to their default values.
*/
-Net_Crypto *new_net_crypto(const Logger *log, Mono_Time *mono_time, DHT *dht, const TCP_Proxy_Info *proxy_info);
+non_null()
+Net_Crypto *new_net_crypto(const Logger *log, const Random *rng, const Network *ns, Mono_Time *mono_time, DHT *dht, const TCP_Proxy_Info *proxy_info);
-/** return the optimal interval in ms for running do_net_crypto.
- */
+/** return the optimal interval in ms for running do_net_crypto. */
+non_null()
uint32_t crypto_run_interval(const Net_Crypto *c);
/** Main loop. */
+non_null(1) nullable(2)
void do_net_crypto(Net_Crypto *c, void *userdata);
+nullable(1)
void kill_net_crypto(Net_Crypto *c);
-
-
#endif
diff --git a/protocols/Tox/libtox/src/toxcore/network.c b/protocols/Tox/libtox/src/toxcore/network.c
index 8569fc17e5..35893b73dc 100644
--- a/protocols/Tox/libtox/src/toxcore/network.c
+++ b/protocols/Tox/libtox/src/toxcore/network.c
@@ -92,16 +92,11 @@
#include <sodium.h>
#endif
+#include "ccompat.h"
#include "logger.h"
#include "mono_time.h"
#include "util.h"
-//!TOKSTYLE-
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-#include "../testing/fuzzing/fuzz_adapter.h"
-#endif
-//!TOKSTYLE+
-
// Disable MSG_NOSIGNAL on systems not supporting it, e.g. Windows, FreeBSD
#if !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
@@ -113,6 +108,12 @@
#endif
#endif
+static_assert(sizeof(IP4) == SIZE_IP4, "IP4 size must be 4");
+
+// TODO(iphydf): Stop relying on this. We memcpy this struct (and IP4 above)
+// into packets but really should be serialising it properly.
+static_assert(sizeof(IP6) == SIZE_IP6, "IP6 size must be 16");
+
#if !defined(OS_WIN32)
static bool should_ignore_recv_error(int err)
@@ -120,21 +121,30 @@ static bool should_ignore_recv_error(int err)
return err == EWOULDBLOCK;
}
+static bool should_ignore_connect_error(int err)
+{
+ return err == EWOULDBLOCK || err == EINPROGRESS;
+}
+
+non_null()
static const char *inet_ntop4(const struct in_addr *addr, char *buf, size_t bufsize)
{
return inet_ntop(AF_INET, addr, buf, bufsize);
}
+non_null()
static const char *inet_ntop6(const struct in6_addr *addr, char *buf, size_t bufsize)
{
return inet_ntop(AF_INET6, addr, buf, bufsize);
}
+non_null()
static int inet_pton4(const char *addrString, struct in_addr *addrbuf)
{
return inet_pton(AF_INET, addrString, addrbuf);
}
+non_null()
static int inet_pton6(const char *addrString, struct in6_addr *addrbuf)
{
return inet_pton(AF_INET6, addrString, addrbuf);
@@ -152,6 +162,12 @@ static bool should_ignore_recv_error(int err)
return err == WSAEWOULDBLOCK || err == WSAECONNRESET;
}
+static bool should_ignore_connect_error(int err)
+{
+ return err == WSAEWOULDBLOCK || err == WSAEINPROGRESS;
+}
+
+non_null()
static const char *inet_ntop4(const struct in_addr *addr, char *buf, size_t bufsize)
{
struct sockaddr_in saddr = {0};
@@ -168,6 +184,7 @@ static const char *inet_ntop4(const struct in_addr *addr, char *buf, size_t bufs
return buf;
}
+non_null()
static const char *inet_ntop6(const struct in6_addr *addr, char *buf, size_t bufsize)
{
struct sockaddr_in6 saddr = {0};
@@ -184,6 +201,7 @@ static const char *inet_ntop6(const struct in6_addr *addr, char *buf, size_t buf
return buf;
}
+non_null()
static int inet_pton4(const char *addrString, struct in_addr *addrbuf)
{
struct sockaddr_in saddr = {0};
@@ -199,6 +217,7 @@ static int inet_pton4(const char *addrString, struct in_addr *addrbuf)
return 1;
}
+non_null()
static int inet_pton6(const char *addrString, struct in6_addr *addrbuf)
{
struct sockaddr_in6 saddr = {0};
@@ -221,7 +240,6 @@ static_assert(TOX_INET6_ADDRSTRLEN >= INET6_ADDRSTRLEN,
static_assert(TOX_INET_ADDRSTRLEN >= INET_ADDRSTRLEN,
"TOX_INET_ADDRSTRLEN should be greater or equal to INET_ADDRSTRLEN (#INET_ADDRSTRLEN)");
-#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
static int make_proto(int proto)
{
switch (proto) {
@@ -249,7 +267,6 @@ static int make_socktype(int type)
return type;
}
}
-#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
static int make_family(Family tox_family)
{
@@ -268,42 +285,58 @@ static int make_family(Family tox_family)
}
}
+static const Family family_unspec = {TOX_AF_UNSPEC};
+static const Family family_ipv4 = {TOX_AF_INET};
+static const Family family_ipv6 = {TOX_AF_INET6};
+static const Family family_tcp_server = {TCP_SERVER_FAMILY};
+static const Family family_tcp_client = {TCP_CLIENT_FAMILY};
+static const Family family_tcp_ipv4 = {TCP_INET};
+static const Family family_tcp_ipv6 = {TCP_INET6};
+static const Family family_tox_tcp_ipv4 = {TOX_TCP_INET};
+static const Family family_tox_tcp_ipv6 = {TOX_TCP_INET6};
+
static const Family *make_tox_family(int family)
{
switch (family) {
case AF_INET:
- return &net_family_ipv4;
+ return &family_ipv4;
case AF_INET6:
- return &net_family_ipv6;
+ return &family_ipv6;
case AF_UNSPEC:
- return &net_family_unspec;
+ return &family_unspec;
default:
return nullptr;
}
}
+non_null()
static void get_ip4(IP4 *result, const struct in_addr *addr)
{
+ static_assert(sizeof(result->uint32) == sizeof(addr->s_addr),
+ "Tox and operating system don't agree on size of IPv4 addresses");
result->uint32 = addr->s_addr;
}
+non_null()
static void get_ip6(IP6 *result, const struct in6_addr *addr)
{
- assert(sizeof(result->uint8) == sizeof(addr->s6_addr));
+ static_assert(sizeof(result->uint8) == sizeof(addr->s6_addr),
+ "Tox and operating system don't agree on size of IPv6 addresses");
memcpy(result->uint8, addr->s6_addr, sizeof(result->uint8));
}
+non_null()
static void fill_addr4(const IP4 *ip, struct in_addr *addr)
{
addr->s_addr = ip->uint32;
}
+non_null()
static void fill_addr6(const IP6 *ip, struct in6_addr *addr)
{
- assert(sizeof(ip->uint8) == sizeof(addr->s6_addr));
memcpy(addr->s6_addr, ip->uint8, sizeof(ip->uint8));
}
@@ -311,6 +344,9 @@ static void fill_addr6(const IP6 *ip, struct in6_addr *addr)
#define INADDR_LOOPBACK 0x7f000001
#endif
+static const IP empty_ip = {{0}};
+const IP_Port empty_ip_port = {{{0}}};
+
const IP4 ip4_broadcast = { INADDR_BROADCAST };
const IP6 ip6_broadcast = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
@@ -326,7 +362,12 @@ IP4 get_ip4_loopback(void)
IP6 get_ip6_loopback(void)
{
IP6 loopback;
+#ifdef ESP_PLATFORM
+ loopback = empty_ip_port.ip.ip.v6;
+ loopback.uint8[15] = 1;
+#else
get_ip6(&loopback, &in6addr_loopback);
+#endif
return loopback;
}
@@ -336,134 +377,262 @@ IP6 get_ip6_loopback(void)
const Socket net_invalid_socket = { (int)INVALID_SOCKET };
-const Family net_family_unspec = {TOX_AF_UNSPEC};
-const Family net_family_ipv4 = {TOX_AF_INET};
-const Family net_family_ipv6 = {TOX_AF_INET6};
-const Family net_family_tcp_family = {TCP_FAMILY};
-const Family net_family_tcp_onion = {TCP_ONION_FAMILY};
-const Family net_family_tcp_ipv4 = {TCP_INET};
-const Family net_family_tcp_ipv6 = {TCP_INET6};
-const Family net_family_tox_tcp_ipv4 = {TOX_TCP_INET};
-const Family net_family_tox_tcp_ipv6 = {TOX_TCP_INET6};
+Family net_family_unspec()
+{
+ return family_unspec;
+}
+
+Family net_family_ipv4()
+{
+ return family_ipv4;
+}
+
+Family net_family_ipv6()
+{
+ return family_ipv6;
+}
+
+Family net_family_tcp_server()
+{
+ return family_tcp_server;
+}
+
+Family net_family_tcp_client()
+{
+ return family_tcp_client;
+}
+
+Family net_family_tcp_ipv4()
+{
+ return family_tcp_ipv4;
+}
+
+Family net_family_tcp_ipv6()
+{
+ return family_tcp_ipv6;
+}
+
+Family net_family_tox_tcp_ipv4()
+{
+ return family_tox_tcp_ipv4;
+}
+
+Family net_family_tox_tcp_ipv6()
+{
+ return family_tox_tcp_ipv6;
+}
bool net_family_is_unspec(Family family)
{
- return family.value == net_family_unspec.value;
+ return family.value == family_unspec.value;
}
bool net_family_is_ipv4(Family family)
{
- return family.value == net_family_ipv4.value;
+ return family.value == family_ipv4.value;
}
bool net_family_is_ipv6(Family family)
{
- return family.value == net_family_ipv6.value;
+ return family.value == family_ipv6.value;
}
-bool net_family_is_tcp_family(Family family)
+bool net_family_is_tcp_server(Family family)
{
- return family.value == net_family_tcp_family.value;
+ return family.value == family_tcp_server.value;
}
-bool net_family_is_tcp_onion(Family family)
+bool net_family_is_tcp_client(Family family)
{
- return family.value == net_family_tcp_onion.value;
+ return family.value == family_tcp_client.value;
}
bool net_family_is_tcp_ipv4(Family family)
{
- return family.value == net_family_tcp_ipv4.value;
+ return family.value == family_tcp_ipv4.value;
}
bool net_family_is_tcp_ipv6(Family family)
{
- return family.value == net_family_tcp_ipv6.value;
+ return family.value == family_tcp_ipv6.value;
}
bool net_family_is_tox_tcp_ipv4(Family family)
{
- return family.value == net_family_tox_tcp_ipv4.value;
+ return family.value == family_tox_tcp_ipv4.value;
}
bool net_family_is_tox_tcp_ipv6(Family family)
{
- return family.value == net_family_tox_tcp_ipv6.value;
+ return family.value == family_tox_tcp_ipv6.value;
}
bool sock_valid(Socket sock)
{
- return sock.socket != net_invalid_socket.socket;
+ return sock.sock != net_invalid_socket.sock;
}
-/** Close the socket.
- */
-void kill_sock(Socket sock)
+struct Network_Addr {
+ struct sockaddr_storage addr;
+ size_t size;
+};
+
+non_null()
+static int sys_close(void *obj, int sock)
+{
+#if defined(OS_WIN32)
+ return closesocket(sock);
+#else // !OS_WIN32
+ return close(sock);
+#endif
+}
+
+non_null()
+static int sys_accept(void *obj, int sock)
+{
+ return accept(sock, nullptr, nullptr);
+}
+
+non_null()
+static int sys_bind(void *obj, int sock, const Network_Addr *addr)
+{
+ return bind(sock, (const struct sockaddr *)&addr->addr, addr->size);
+}
+
+non_null()
+static int sys_listen(void *obj, int sock, int backlog)
+{
+ return listen(sock, backlog);
+}
+
+non_null()
+static int sys_recvbuf(void *obj, int sock)
{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- return;
-#else
#ifdef OS_WIN32
- closesocket(sock.socket);
+ u_long count = 0;
+ ioctlsocket(sock, FIONREAD, &count);
#else
- close(sock.socket);
-#endif /* OS_WIN32 */
-#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+ int count = 0;
+ ioctl(sock, FIONREAD, &count);
+#endif
+
+ return count;
}
-bool set_socket_nonblock(Socket sock)
+non_null()
+static int sys_recv(void *obj, int sock, uint8_t *buf, size_t len)
+{
+ return recv(sock, (char *)buf, len, MSG_NOSIGNAL);
+}
+
+non_null()
+static int sys_send(void *obj, int sock, const uint8_t *buf, size_t len)
+{
+ return send(sock, (const char *)buf, len, MSG_NOSIGNAL);
+}
+
+non_null()
+static int sys_sendto(void *obj, int sock, const uint8_t *buf, size_t len, const Network_Addr *addr) {
+ return sendto(sock, (const char *)buf, len, 0, (const struct sockaddr *)&addr->addr, addr->size);
+}
+
+non_null()
+static int sys_recvfrom(void *obj, int sock, uint8_t *buf, size_t len, Network_Addr *addr) {
+ socklen_t size = addr->size;
+ const int ret = recvfrom(sock, (char *)buf, len, 0, (struct sockaddr *)&addr->addr, &size);
+ addr->size = size;
+ return ret;
+}
+
+non_null()
+static int sys_socket(void *obj, int domain, int type, int proto)
+{
+ return (int)socket(domain, type, proto);
+}
+
+non_null()
+static int sys_socket_nonblock(void *obj, int sock, bool nonblock)
{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- return true;
-#else
#ifdef OS_WIN32
- u_long mode = 1;
- return ioctlsocket(sock.socket, FIONBIO, &mode) == 0;
+ u_long mode = nonblock ? 1 : 0;
+ return ioctlsocket(sock, FIONBIO, &mode);
#else
- return fcntl(sock.socket, F_SETFL, O_NONBLOCK, 1) == 0;
+ return fcntl(sock, F_SETFL, O_NONBLOCK, nonblock ? 1 : 0);
#endif /* OS_WIN32 */
-#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
}
-bool set_socket_nosigpipe(Socket sock)
+non_null()
+static int sys_getsockopt(void *obj, int sock, int level, int optname, void *optval, size_t *optlen)
{
-#if defined(__APPLE__)
- int set = 1;
- return setsockopt(sock.socket, SOL_SOCKET, SO_NOSIGPIPE, (const char *)&set, sizeof(int)) == 0;
-#else
- return true;
-#endif
+ socklen_t len = *optlen;
+ const int ret = getsockopt(sock, level, optname, optval, &len);
+ *optlen = len;
+ return ret;
}
-bool set_socket_reuseaddr(Socket sock)
+non_null()
+static int sys_setsockopt(void *obj, int sock, int level, int optname, const void *optval, size_t optlen)
{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- return false;
-#else
- int set = 1;
- return setsockopt(sock.socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&set, sizeof(set)) == 0;
-#endif
+ return setsockopt(sock, level, optname, optval, optlen);
}
-bool set_socket_dualstack(Socket sock)
+static const Network_Funcs system_network_funcs = {
+ sys_close,
+ sys_accept,
+ sys_bind,
+ sys_listen,
+ sys_recvbuf,
+ sys_recv,
+ sys_recvfrom,
+ sys_send,
+ sys_sendto,
+ sys_socket,
+ sys_socket_nonblock,
+ sys_getsockopt,
+ sys_setsockopt,
+};
+static const Network system_network_obj = {&system_network_funcs};
+
+const Network *system_network(void)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- return false;
-#else
- int ipv6only = 0;
- socklen_t optsize = sizeof(ipv6only);
- int res = getsockopt(sock.socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&ipv6only, &optsize);
+ if ((true)) {
+ return nullptr;
+ }
+#endif
+#ifdef OS_WIN32
+ WSADATA wsaData;
- if ((res == 0) && (ipv6only == 0)) {
- return true;
+ if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) {
+ return nullptr;
}
+#endif
+ return &system_network_obj;
+}
- ipv6only = 0;
- return setsockopt(sock.socket, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&ipv6only, sizeof(ipv6only)) == 0;
+#if 0
+/* TODO(iphydf): Call this from functions that use `system_network()`. */
+void system_network_deinit(const Network *ns)
+{
+#ifdef OS_WIN32
+ WSACleanup();
+#endif
+}
#endif
+
+non_null()
+static int net_setsockopt(const Network *ns, Socket sock, int level, int optname, const void *optval, size_t optlen)
+{
+ return ns->funcs->setsockopt(ns->obj, sock.sock, level, optname, optval, optlen);
}
+non_null()
+static int net_getsockopt(const Network *ns, Socket sock, int level, int optname, void *optval, size_t *optlen)
+{
+ return ns->funcs->getsockopt(ns->obj, sock.sock, level, optname, optval, optlen);
+}
+non_null()
static uint32_t data_0(uint16_t buflen, const uint8_t *buffer)
{
uint32_t data = 0;
@@ -474,43 +643,136 @@ static uint32_t data_0(uint16_t buflen, const uint8_t *buffer)
return data;
}
+non_null()
static uint32_t data_1(uint16_t buflen, const uint8_t *buffer)
{
uint32_t data = 0;
- if (buflen > 7) {
+ if (buflen > 8) {
net_unpack_u32(buffer + 5, &data);
}
return data;
}
+non_null()
static void loglogdata(const Logger *log, const char *message, const uint8_t *buffer,
uint16_t buflen, const IP_Port *ip_port, long res)
{
- char ip_str[IP_NTOA_LEN];
-
if (res < 0) { /* Windows doesn't necessarily know `%zu` */
- int error = net_error();
+ Ip_Ntoa ip_str;
+ const int error = net_error();
char *strerror = net_new_strerror(error);
LOGGER_TRACE(log, "[%2u] %s %3u%c %s:%u (%u: %s) | %08x%08x...%02x",
buffer[0], message, min_u16(buflen, 999), 'E',
- ip_ntoa(&ip_port->ip, ip_str, sizeof(ip_str)), net_ntohs(ip_port->port), error,
+ net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port), error,
strerror, data_0(buflen, buffer), data_1(buflen, buffer), buffer[buflen - 1]);
net_kill_strerror(strerror);
} else if ((res > 0) && ((size_t)res <= buflen)) {
+ Ip_Ntoa ip_str;
LOGGER_TRACE(log, "[%2u] %s %3u%c %s:%u (%u: %s) | %08x%08x...%02x",
buffer[0], message, min_u16(res, 999), (size_t)res < buflen ? '<' : '=',
- ip_ntoa(&ip_port->ip, ip_str, sizeof(ip_str)), net_ntohs(ip_port->port), 0, "OK",
+ net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port), 0, "OK",
data_0(buflen, buffer), data_1(buflen, buffer), buffer[buflen - 1]);
} else { /* empty or overwrite */
+ Ip_Ntoa ip_str;
LOGGER_TRACE(log, "[%2u] %s %lu%c%u %s:%u (%u: %s) | %08x%08x...%02x",
- buffer[0], message, res, !res ? '!' : '>', buflen,
- ip_ntoa(&ip_port->ip, ip_str, sizeof(ip_str)), net_ntohs(ip_port->port), 0, "OK",
+ buffer[0], message, res, res == 0 ? '!' : '>', buflen,
+ net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port), 0, "OK",
data_0(buflen, buffer), data_1(buflen, buffer), buffer[buflen - 1]);
}
}
+int net_send(const Network *ns, const Logger *log,
+ Socket sock, const uint8_t *buf, size_t len, const IP_Port *ip_port)
+{
+ const int res = ns->funcs->send(ns->obj, sock.sock, buf, len);
+ loglogdata(log, "T=>", buf, len, ip_port, res);
+ return res;
+}
+
+non_null()
+static int net_sendto(
+ const Network *ns,
+ Socket sock, const uint8_t *buf, size_t len, const Network_Addr *addr, const IP_Port *ip_port)
+{
+ return ns->funcs->sendto(ns->obj, sock.sock, buf, len, addr);
+}
+
+int net_recv(const Network *ns, const Logger *log,
+ Socket sock, uint8_t *buf, size_t len, const IP_Port *ip_port)
+{
+ const int res = ns->funcs->recv(ns->obj, sock.sock, buf, len);
+ loglogdata(log, "=>T", buf, len, ip_port, res);
+ return res;
+}
+
+non_null()
+static int net_recvfrom(const Network *ns,
+ Socket sock, uint8_t *buf, size_t len, Network_Addr *addr)
+{
+ return ns->funcs->recvfrom(ns->obj, sock.sock, buf, len, addr);
+}
+
+int net_listen(const Network *ns, Socket sock, int backlog)
+{
+ return ns->funcs->listen(ns->obj, sock.sock, backlog);
+}
+
+non_null()
+static int net_bind(const Network *ns, Socket sock, const Network_Addr *addr)
+{
+ return ns->funcs->bind(ns->obj, sock.sock, addr);
+}
+
+Socket net_accept(const Network *ns, Socket sock)
+{
+ const Socket newsock = {ns->funcs->accept(ns->obj, sock.sock)};
+ return newsock;
+}
+
+/** Close the socket. */
+void kill_sock(const Network *ns, Socket sock)
+{
+ ns->funcs->close(ns->obj, sock.sock);
+}
+
+bool set_socket_nonblock(const Network *ns, Socket sock)
+{
+ return ns->funcs->socket_nonblock(ns->obj, sock.sock, true) == 0;
+}
+
+bool set_socket_nosigpipe(const Network *ns, Socket sock)
+{
+#if defined(__APPLE__)
+ int set = 1;
+ return net_setsockopt(ns, sock, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof(int)) == 0;
+#else
+ return true;
+#endif
+}
+
+bool set_socket_reuseaddr(const Network *ns, Socket sock)
+{
+ int set = 1;
+ return net_setsockopt(ns, sock, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set)) == 0;
+}
+
+bool set_socket_dualstack(const Network *ns, Socket sock)
+{
+ int ipv6only = 0;
+ size_t optsize = sizeof(ipv6only);
+ const int res = net_getsockopt(ns, sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, &optsize);
+
+ if ((res == 0) && (ipv6only == 0)) {
+ return true;
+ }
+
+ ipv6only = 0;
+ return net_setsockopt(ns, sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)) == 0;
+}
+
+
typedef struct Packet_Handler {
packet_handler_cb *function;
void *object;
@@ -519,6 +781,7 @@ typedef struct Packet_Handler {
struct Networking_Core {
const Logger *log;
Packet_Handler packethandlers[256];
+ const Network *ns;
Family family;
uint16_t port;
@@ -543,6 +806,12 @@ int send_packet(const Networking_Core *net, const IP_Port *ip_port, Packet packe
{
IP_Port ipp_copy = *ip_port;
+ if (net_family_is_unspec(ip_port->ip.family)) {
+ // TODO(iphydf): Make this an error. Currently this fails sometimes when
+ // called from DHT.c:do_ping_and_sendnode_requests.
+ return -1;
+ }
+
if (net_family_is_unspec(net->family)) { /* Socket not initialized */
// TODO(iphydf): Make this an error. Currently, the onion client calls
// this via DHT getnodes.
@@ -570,25 +839,23 @@ int send_packet(const Networking_Core *net, const IP_Port *ip_port, Packet packe
ip6.uint32[2] = net_htonl(0xFFFF);
ip6.uint32[3] = ipp_copy.ip.ip.v4.uint32;
- ipp_copy.ip.family = net_family_ipv6;
+ ipp_copy.ip.family = net_family_ipv6();
ipp_copy.ip.ip.v6 = ip6;
}
- struct sockaddr_storage addr;
-
- size_t addrsize;
+ Network_Addr addr;
if (net_family_is_ipv4(ipp_copy.ip.family)) {
- struct sockaddr_in *const addr4 = (struct sockaddr_in *)&addr;
+ struct sockaddr_in *const addr4 = (struct sockaddr_in *)&addr.addr;
- addrsize = sizeof(struct sockaddr_in);
+ addr.size = sizeof(struct sockaddr_in);
addr4->sin_family = AF_INET;
addr4->sin_port = ipp_copy.port;
fill_addr4(&ipp_copy.ip.ip.v4, &addr4->sin_addr);
} else if (net_family_is_ipv6(ipp_copy.ip.family)) {
- struct sockaddr_in6 *const addr6 = (struct sockaddr_in6 *)&addr;
+ struct sockaddr_in6 *const addr6 = (struct sockaddr_in6 *)&addr.addr;
- addrsize = sizeof(struct sockaddr_in6);
+ addr.size = sizeof(struct sockaddr_in6);
addr6->sin6_family = AF_INET6;
addr6->sin6_port = ipp_copy.port;
fill_addr6(&ipp_copy.ip.ip.v6, &addr6->sin6_addr);
@@ -596,21 +863,12 @@ int send_packet(const Networking_Core *net, const IP_Port *ip_port, Packet packe
addr6->sin6_flowinfo = 0;
addr6->sin6_scope_id = 0;
} else {
- // TODO(iphydf): Make this an error. Currently this fails sometimes when
- // called from DHT.c:do_ping_and_sendnode_requests.
- LOGGER_WARNING(net->log, "unknown address type: %d", ipp_copy.ip.family.value);
+ LOGGER_ERROR(net->log, "unknown address type: %d", ipp_copy.ip.family.value);
return -1;
}
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- const long res = fuzz_sendto(net->sock.socket, (const char *)packet.data, packet.length, 0,
- (struct sockaddr *)&addr, addrsize);
-#else
- const long res = sendto(net->sock.socket, (const char *)packet.data, packet.length, 0,
- (struct sockaddr *)&addr, addrsize);
-#endif
-
- loglogdata(net->log, "O=>", packet.data, packet.length, &ipp_copy, res);
+ const long res = net_sendto(net->ns, net->sock, packet.data, packet.length, &addr, &ipp_copy);
+ loglogdata(net->log, "O=>", packet.data, packet.length, ip_port, res);
assert(res <= INT_MAX);
return (int)res;
@@ -627,34 +885,27 @@ int sendpacket(const Networking_Core *net, const IP_Port *ip_port, const uint8_t
return send_packet(net, ip_port, packet);
}
-/** Function to receive data
- * ip and port of sender is put into ip_port.
- * Packet data is put into data.
- * Packet length is put into length.
+/** @brief Function to receive data
+ * ip and port of sender is put into ip_port.
+ * Packet data is put into data.
+ * Packet length is put into length.
*/
-static int receivepacket(const Logger *log, Socket sock, IP_Port *ip_port, uint8_t *data, uint32_t *length)
+non_null()
+static int receivepacket(const Network *ns, const Logger *log, Socket sock, IP_Port *ip_port, uint8_t *data, uint32_t *length)
{
memset(ip_port, 0, sizeof(IP_Port));
- struct sockaddr_storage addr;
-#ifdef OS_WIN32
- int addrlen = sizeof(addr);
-#else
- socklen_t addrlen = sizeof(addr);
-#endif
+ Network_Addr addr = {{0}};
+ addr.size = sizeof(addr.addr);
*length = 0;
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- int fail_or_len = fuzz_recvfrom(sock.socket, (char *) data, MAX_UDP_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrlen);
-#else
- int fail_or_len = recvfrom(sock.socket, (char *) data, MAX_UDP_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrlen);
-#endif
+ const int fail_or_len = net_recvfrom(ns, sock, data, MAX_UDP_PACKET_SIZE, &addr);
if (fail_or_len < 0) {
- int error = net_error();
+ const int error = net_error();
if (!should_ignore_recv_error(error)) {
char *strerror = net_new_strerror(error);
- LOGGER_ERROR(log, "Unexpected error reading from socket: %u, %s", error, strerror);
+ LOGGER_ERROR(log, "unexpected error reading from socket: %u, %s", error, strerror);
net_kill_strerror(strerror);
}
@@ -663,8 +914,8 @@ static int receivepacket(const Logger *log, Socket sock, IP_Port *ip_port, uint8
*length = (uint32_t)fail_or_len;
- if (addr.ss_family == AF_INET) {
- const struct sockaddr_in *addr_in = (const struct sockaddr_in *)&addr;
+ if (addr.addr.ss_family == AF_INET) {
+ const struct sockaddr_in *addr_in = (const struct sockaddr_in *)&addr.addr;
const Family *const family = make_tox_family(addr_in->sin_family);
assert(family != nullptr);
@@ -676,8 +927,8 @@ static int receivepacket(const Logger *log, Socket sock, IP_Port *ip_port, uint8
ip_port->ip.family = *family;
get_ip4(&ip_port->ip.ip.v4, &addr_in->sin_addr);
ip_port->port = addr_in->sin_port;
- } else if (addr.ss_family == AF_INET6) {
- const struct sockaddr_in6 *addr_in6 = (const struct sockaddr_in6 *)&addr;
+ } else if (addr.addr.ss_family == AF_INET6) {
+ const struct sockaddr_in6 *addr_in6 = (const struct sockaddr_in6 *)&addr.addr;
const Family *const family = make_tox_family(addr_in6->sin6_family);
assert(family != nullptr);
@@ -690,7 +941,7 @@ static int receivepacket(const Logger *log, Socket sock, IP_Port *ip_port, uint8
ip_port->port = addr_in6->sin6_port;
if (ipv6_ipv4_in_v6(&ip_port->ip.ip.v6)) {
- ip_port->ip.family = net_family_ipv4;
+ ip_port->ip.family = net_family_ipv4();
ip_port->ip.ip.v4.uint32 = ip_port->ip.ip.v6.uint32[3];
}
} else {
@@ -719,89 +970,37 @@ void networking_poll(const Networking_Core *net, void *userdata)
uint8_t data[MAX_UDP_PACKET_SIZE];
uint32_t length;
- while (receivepacket(net->log, net->sock, &ip_port, data, &length) != -1) {
+ while (receivepacket(net->ns, net->log, net->sock, &ip_port, data, &length) != -1) {
if (length < 1) {
continue;
}
- packet_handler_cb *const cb = net->packethandlers[data[0]].function;
- void *const object = net->packethandlers[data[0]].object;
+ const Packet_Handler *const handler = &net->packethandlers[data[0]];
- if (cb == nullptr) {
- LOGGER_WARNING(net->log, "[%02u] -- Packet has no handler", data[0]);
+ if (handler->function == nullptr) {
+ // TODO(https://github.com/TokTok/c-toxcore/issues/1115): Make this
+ // a warning or error again.
+ LOGGER_DEBUG(net->log, "[%02u] -- Packet has no handler", data[0]);
continue;
}
- cb(object, &ip_port, data, length, userdata);
- }
-}
-
-//!TOKSTYLE-
-// Global mutable state is not allowed in Tokstyle.
-static uint8_t at_startup_ran = 0;
-//!TOKSTYLE+
-int networking_at_startup(void)
-{
- if (at_startup_ran != 0) {
- return 0;
- }
-
-#ifndef VANILLA_NACL
-
-#ifdef USE_RANDOMBYTES_STIR
- randombytes_stir();
-#else
-
- if (sodium_init() == -1) {
- return -1;
- }
-
-#endif /*USE_RANDOMBYTES_STIR*/
-
-#endif/*VANILLA_NACL*/
-
-#ifdef OS_WIN32
- WSADATA wsaData;
-
- if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) {
- return -1;
+ handler->function(handler->object, &ip_port, data, length, userdata);
}
-
-#endif
- at_startup_ran = 1;
- return 0;
-}
-
-/* TODO(irungentoo): Put this somewhere */
-#if 0
-static void at_shutdown(void)
-{
-#ifdef OS_WIN32
- WSACleanup();
-#endif
-}
-#endif
-
-/** Initialize networking.
- * Added for reverse compatibility with old new_networking calls.
- */
-Networking_Core *new_networking(const Logger *log, const IP *ip, uint16_t port)
-{
- return new_networking_ex(log, ip, port, port + (TOX_PORTRANGE_TO - TOX_PORTRANGE_FROM), nullptr);
}
-/** Initialize networking.
+/** @brief Initialize networking.
* Bind to ip and port.
* ip must be in network order EX: 127.0.0.1 = (7F000001).
* port is in host byte order (this means don't worry about it).
*
- * return Networking_Core object if no problems
- * return NULL if there are problems.
+ * @return Networking_Core object if no problems
+ * @retval NULL if there are problems.
*
* If error is non NULL it is set to 0 if no issues, 1 if socket related error, 2 if other.
*/
-Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t port_from, uint16_t port_to,
- unsigned int *error)
+Networking_Core *new_networking_ex(
+ const Logger *log, const Network *ns, const IP *ip,
+ uint16_t port_from, uint16_t port_to, unsigned int *error)
{
/* If both from and to are 0, use default port range
* If one is 0 and the other is non-0, use the non-0 value as only port
@@ -815,12 +1014,12 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por
} else if (port_from != 0 && port_to == 0) {
port_to = port_from;
} else if (port_from > port_to) {
- uint16_t temp = port_from;
+ const uint16_t temp_port = port_from;
port_from = port_to;
- port_to = temp;
+ port_to = temp_port;
}
- if (error) {
+ if (error != nullptr) {
*error = 2;
}
@@ -830,69 +1029,60 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por
return nullptr;
}
- if (networking_at_startup() != 0) {
- return nullptr;
- }
-
Networking_Core *temp = (Networking_Core *)calloc(1, sizeof(Networking_Core));
if (temp == nullptr) {
return nullptr;
}
+ temp->ns = ns;
temp->log = log;
temp->family = ip->family;
temp->port = 0;
/* Initialize our socket. */
/* add log message what we're creating */
- temp->sock = net_socket(temp->family, TOX_SOCK_DGRAM, TOX_PROTO_UDP);
+ temp->sock = net_socket(ns, temp->family, TOX_SOCK_DGRAM, TOX_PROTO_UDP);
/* Check for socket error. */
if (!sock_valid(temp->sock)) {
- int neterror = net_error();
+ const int neterror = net_error();
char *strerror = net_new_strerror(neterror);
- LOGGER_ERROR(log, "Failed to get a socket?! %d, %s", neterror, strerror);
+ LOGGER_ERROR(log, "failed to get a socket?! %d, %s", neterror, strerror);
net_kill_strerror(strerror);
free(temp);
- if (error) {
+ if (error != nullptr) {
*error = 1;
}
return nullptr;
}
-#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
/* Functions to increase the size of the send and receive UDP buffers.
*/
int n = 1024 * 1024 * 2;
- if (setsockopt(temp->sock.socket, SOL_SOCKET, SO_RCVBUF, (const char *)&n, sizeof(n)) != 0) {
- LOGGER_WARNING(log, "Failed to set socket option %d", SO_RCVBUF);
+ if (net_setsockopt(ns, temp->sock, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) != 0) {
+ LOGGER_ERROR(log, "failed to set socket option %d", SO_RCVBUF);
}
- if (setsockopt(temp->sock.socket, SOL_SOCKET, SO_SNDBUF, (const char *)&n, sizeof(n)) != 0) {
- LOGGER_WARNING(log, "Failed to set socket option %d", SO_SNDBUF);
+ if (net_setsockopt(ns, temp->sock, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) != 0) {
+ LOGGER_ERROR(log, "failed to set socket option %d", SO_SNDBUF);
}
-#endif
-
-#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
/* Enable broadcast on socket */
int broadcast = 1;
- if (setsockopt(temp->sock.socket, SOL_SOCKET, SO_BROADCAST, (const char *)&broadcast, sizeof(broadcast)) != 0) {
- LOGGER_WARNING(log, "Failed to set socket option %d", SO_BROADCAST);
+ if (net_setsockopt(ns, temp->sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) != 0) {
+ LOGGER_ERROR(log, "failed to set socket option %d", SO_BROADCAST);
}
-#endif
-
/* iOS UDP sockets are weird and apparently can SIGPIPE */
- if (!set_socket_nosigpipe(temp->sock)) {
+ if (!set_socket_nosigpipe(ns, temp->sock)) {
kill_networking(temp);
- if (error) {
+ if (error != nullptr) {
*error = 1;
}
@@ -900,10 +1090,10 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por
}
/* Set socket nonblocking. */
- if (!set_socket_nonblock(temp->sock)) {
+ if (!set_socket_nonblock(ns, temp->sock)) {
kill_networking(temp);
- if (error) {
+ if (error != nullptr) {
*error = 1;
}
@@ -912,24 +1102,23 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por
/* Bind our socket to port PORT and the given IP address (usually 0.0.0.0 or ::) */
uint16_t *portptr = nullptr;
- struct sockaddr_storage addr;
- size_t addrsize;
+ Network_Addr addr;
- memset(&addr, 0, sizeof(struct sockaddr_storage));
+ memset(&addr.addr, 0, sizeof(struct sockaddr_storage));
if (net_family_is_ipv4(temp->family)) {
- struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr;
+ struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr.addr;
- addrsize = sizeof(struct sockaddr_in);
+ addr.size = sizeof(struct sockaddr_in);
addr4->sin_family = AF_INET;
addr4->sin_port = 0;
fill_addr4(&ip->ip.v4, &addr4->sin_addr);
portptr = &addr4->sin_port;
} else if (net_family_is_ipv6(temp->family)) {
- struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr;
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr.addr;
- addrsize = sizeof(struct sockaddr_in6);
+ addr.size = sizeof(struct sockaddr_in6);
addr6->sin6_family = AF_INET6;
addr6->sin6_port = 0;
fill_addr6(&ip->ip.v6, &addr6->sin6_addr);
@@ -943,12 +1132,16 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por
return nullptr;
}
-#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-
if (net_family_is_ipv6(ip->family)) {
- const int is_dualstack = set_socket_dualstack(temp->sock);
- LOGGER_DEBUG(log, "Dual-stack socket: %s",
- is_dualstack ? "enabled" : "Failed to enable, won't be able to receive from/send to IPv4 addresses");
+ const bool is_dualstack = set_socket_dualstack(ns, temp->sock);
+
+ if (is_dualstack) {
+ LOGGER_TRACE(log, "Dual-stack socket: enabled");
+ } else {
+ LOGGER_ERROR(log, "Dual-stack socket failed to enable, won't be able to receive from/send to IPv4 addresses");
+ }
+
+#ifndef ESP_PLATFORM
/* multicast local nodes */
struct ipv6_mreq mreq;
memset(&mreq, 0, sizeof(mreq));
@@ -957,21 +1150,20 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por
mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01;
mreq.ipv6mr_interface = 0;
- const int res = setsockopt(temp->sock.socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (const char *)&mreq, sizeof(mreq));
+ const int res = net_setsockopt(ns, temp->sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
int neterror = net_error();
char *strerror = net_new_strerror(neterror);
if (res < 0) {
- LOGGER_DEBUG(log, "Failed to activate local multicast membership. (%d, %s)", neterror, strerror);
+ LOGGER_INFO(log, "Failed to activate local multicast membership in FF02::1. (%d, %s)", neterror, strerror);
} else {
- LOGGER_DEBUG(log, "Local multicast group FF02::1 joined successfully. (%d, %s)", neterror, strerror);
+ LOGGER_TRACE(log, "Local multicast group joined successfully. (%d, %s)", neterror, strerror);
}
net_kill_strerror(strerror);
- }
-
#endif
+ }
/* A hanging program or a different user might block the standard port.
* As long as it isn't a parameter coming from the commandline,
@@ -993,17 +1185,13 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por
*portptr = net_htons(port_to_try);
for (uint16_t tries = port_from; tries <= port_to; ++tries) {
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- int res = 0;
-#else
- int res = bind(temp->sock.socket, (struct sockaddr *)&addr, addrsize);
-#endif
+ const int res = net_bind(ns, temp->sock, &addr);
- if (!res) {
+ if (res == 0) {
temp->port = *portptr;
- char ip_str[IP_NTOA_LEN];
- LOGGER_DEBUG(log, "Bound successfully to %s:%u", ip_ntoa(ip, ip_str, sizeof(ip_str)),
+ Ip_Ntoa ip_str;
+ LOGGER_DEBUG(log, "Bound successfully to %s:%u", net_ip_ntoa(ip, &ip_str),
net_ntohs(temp->port));
/* errno isn't reset on success, only set on failure, the failed
@@ -1013,7 +1201,7 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por
errno = 0;
}
- if (error) {
+ if (error != nullptr) {
*error = 0;
}
@@ -1029,27 +1217,23 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por
*portptr = net_htons(port_to_try);
}
- char ip_str[IP_NTOA_LEN];
+ Ip_Ntoa ip_str;
int neterror = net_error();
char *strerror = net_new_strerror(neterror);
- LOGGER_ERROR(log, "Failed to bind socket: %d, %s IP: %s port_from: %u port_to: %u", neterror, strerror,
- ip_ntoa(ip, ip_str, sizeof(ip_str)), port_from, port_to);
+ LOGGER_ERROR(log, "failed to bind socket: %d, %s IP: %s port_from: %u port_to: %u", neterror, strerror,
+ net_ip_ntoa(ip, &ip_str), port_from, port_to);
net_kill_strerror(strerror);
kill_networking(temp);
- if (error) {
+ if (error != nullptr) {
*error = 1;
}
return nullptr;
}
-Networking_Core *new_networking_no_udp(const Logger *log)
+Networking_Core *new_networking_no_udp(const Logger *log, const Network *ns)
{
- if (networking_at_startup() != 0) {
- return nullptr;
- }
-
/* this is the easiest way to completely disable UDP without changing too much code. */
Networking_Core *net = (Networking_Core *)calloc(1, sizeof(Networking_Core));
@@ -1057,6 +1241,7 @@ Networking_Core *new_networking_no_udp(const Logger *log)
return nullptr;
}
+ net->ns = ns;
net->log = log;
return net;
@@ -1065,13 +1250,13 @@ Networking_Core *new_networking_no_udp(const Logger *log)
/** Function to cleanup networking stuff (doesn't do much right now). */
void kill_networking(Networking_Core *net)
{
- if (!net) {
+ if (net == nullptr) {
return;
}
if (!net_family_is_unspec(net->family)) {
/* Socket is initialized, so we close it. */
- kill_sock(net->sock);
+ kill_sock(net->ns, net->sock);
}
free(net);
@@ -1080,7 +1265,7 @@ void kill_networking(Networking_Core *net)
bool ip_equal(const IP *a, const IP *b)
{
- if (!a || !b) {
+ if (a == nullptr || b == nullptr) {
return false;
}
@@ -1122,11 +1307,11 @@ bool ip_equal(const IP *a, const IP *b)
bool ipport_equal(const IP_Port *a, const IP_Port *b)
{
- if (!a || !b) {
+ if (a == nullptr || b == nullptr) {
return false;
}
- if (!a->port || (a->port != b->port)) {
+ if (a->port == 0 || (a->port != b->port)) {
return false;
}
@@ -1136,38 +1321,38 @@ bool ipport_equal(const IP_Port *a, const IP_Port *b)
/** nulls out ip */
void ip_reset(IP *ip)
{
- if (!ip) {
+ if (ip == nullptr) {
return;
}
- memset(ip, 0, sizeof(IP));
+ *ip = empty_ip;
}
/** nulls out ip_port */
void ipport_reset(IP_Port *ipport)
{
- if (!ipport) {
+ if (ipport == nullptr) {
return;
}
- memset(ipport, 0, sizeof(IP_Port));
+ *ipport = empty_ip_port;
}
/** nulls out ip, sets family according to flag */
void ip_init(IP *ip, bool ipv6enabled)
{
- if (!ip) {
+ if (ip == nullptr) {
return;
}
- memset(ip, 0, sizeof(IP));
- ip->family = ipv6enabled ? net_family_ipv6 : net_family_ipv4;
+ *ip = empty_ip;
+ ip->family = ipv6enabled ? net_family_ipv6() : net_family_ipv4();
}
/** checks if ip is valid */
bool ip_isset(const IP *ip)
{
- if (!ip) {
+ if (ip == nullptr) {
return false;
}
@@ -1177,96 +1362,82 @@ bool ip_isset(const IP *ip)
/** checks if ip is valid */
bool ipport_isset(const IP_Port *ipport)
{
- if (!ipport) {
+ if (ipport == nullptr) {
return false;
}
- if (!ipport->port) {
+ if (ipport->port == 0) {
return false;
}
return ip_isset(&ipport->ip);
}
-/** copies an ip structure (careful about direction!) */
+/** copies an ip structure (careful about direction) */
void ip_copy(IP *target, const IP *source)
{
- if (!source || !target) {
+ if (source == nullptr || target == nullptr) {
return;
}
*target = *source;
}
-/** copies an ip_port structure (careful about direction!) */
+/** copies an ip_port structure (careful about direction) */
void ipport_copy(IP_Port *target, const IP_Port *source)
{
- if (!source || !target) {
+ if (source == nullptr || target == nullptr) {
return;
}
*target = *source;
}
-/** ip_ntoa
- * converts ip into a string
- * ip_str must be of length at least IP_NTOA_LEN
+/** @brief Converts IP into a string.
+ *
+ * Writes error message into the buffer on error.
*
- * writes error message into the buffer on error
+ * @param ip_str contains a buffer of the required size.
*
- * returns ip_str
+ * @return Pointer to the buffer inside `ip_str` containing the IP string.
*/
-const char *ip_ntoa(const IP *ip, char *ip_str, size_t length)
+const char *net_ip_ntoa(const IP *ip, Ip_Ntoa *ip_str)
{
- if (length < IP_NTOA_LEN) {
- snprintf(ip_str, length, "Bad buf length");
- return ip_str;
- }
-
- if (ip) {
- if (net_family_is_ipv4(ip->family)) {
- /* returns standard quad-dotted notation */
- struct in_addr addr;
- fill_addr4(&ip->ip.v4, &addr);
+ assert(ip_str != nullptr);
- ip_str[0] = '\0';
- assert(make_family(ip->family) == AF_INET);
- inet_ntop4(&addr, ip_str, length);
- } else if (net_family_is_ipv6(ip->family)) {
- /* returns hex-groups enclosed into square brackets */
- struct in6_addr addr;
- fill_addr6(&ip->ip.v6, &addr);
+ if (ip == nullptr) {
+ snprintf(ip_str->buf, sizeof(ip_str->buf), "(IP invalid: NULL)");
+ return ip_str->buf;
+ }
- assert(make_family(ip->family) == AF_INET6);
- inet_ntop6(&addr, ip_str, length);
- } else {
- snprintf(ip_str, length, "(IP invalid, family %u)", ip->family.value);
- }
- } else {
- snprintf(ip_str, length, "(IP invalid: NULL)");
+ if (!ip_parse_addr(ip, ip_str->buf, sizeof(ip_str->buf))) {
+ snprintf(ip_str->buf, sizeof(ip_str->buf), "(IP invalid, family %u)", ip->family.value);
+ return ip_str->buf;
}
/* brute force protection against lacking termination */
- ip_str[length - 1] = '\0';
- return ip_str;
+ ip_str->buf[sizeof(ip_str->buf) - 1] = '\0';
+ return ip_str->buf;
}
bool ip_parse_addr(const IP *ip, char *address, size_t length)
{
- if (!address || !ip) {
+ if (address == nullptr || ip == nullptr) {
return false;
}
if (net_family_is_ipv4(ip->family)) {
- const struct in_addr *addr = (const struct in_addr *)&ip->ip.v4;
+ struct in_addr addr;
assert(make_family(ip->family) == AF_INET);
- return inet_ntop4(addr, address, length) != nullptr;
+ fill_addr4(&ip->ip.v4, &addr);
+ return inet_ntop4(&addr, address, length) != nullptr;
}
if (net_family_is_ipv6(ip->family)) {
- const struct in6_addr *addr = (const struct in6_addr *)&ip->ip.v6;
+ struct in6_addr addr;
assert(make_family(ip->family) == AF_INET6);
- return inet_ntop6(addr, address, length) != nullptr;
+ fill_addr6(&ip->ip.v6, &addr);
+ return inet_ntop6(&addr, address, length) != nullptr;
}
return false;
@@ -1274,14 +1445,14 @@ bool ip_parse_addr(const IP *ip, char *address, size_t length)
bool addr_parse_ip(const char *address, IP *to)
{
- if (!address || !to) {
+ if (address == nullptr || to == nullptr) {
return false;
}
struct in_addr addr4;
if (inet_pton4(address, &addr4) == 1) {
- to->family = net_family_ipv4;
+ to->family = net_family_ipv4();
get_ip4(&to->ip.v4, &addr4);
return true;
}
@@ -1289,7 +1460,7 @@ bool addr_parse_ip(const char *address, IP *to)
struct in6_addr addr6;
if (inet_pton6(address, &addr6) == 1) {
- to->family = net_family_ipv6;
+ to->family = net_family_ipv6();
get_ip6(&to->ip.v6, &addr6);
return true;
}
@@ -1297,28 +1468,48 @@ bool addr_parse_ip(const char *address, IP *to)
return false;
}
-int addr_resolve(const char *address, IP *to, IP *extra)
+/** addr_resolve return values */
+#define TOX_ADDR_RESOLVE_INET 1
+#define TOX_ADDR_RESOLVE_INET6 2
+
+/**
+ * Uses getaddrinfo to resolve an address into an IP address.
+ *
+ * Uses the first IPv4/IPv6 addresses returned by getaddrinfo.
+ *
+ * @param address a hostname (or something parseable to an IP address)
+ * @param to to.family MUST be initialized, either set to a specific IP version
+ * (TOX_AF_INET/TOX_AF_INET6) or to the unspecified TOX_AF_UNSPEC (0), if both
+ * IP versions are acceptable
+ * @param extra can be NULL and is only set in special circumstances, see returns
+ *
+ * Returns in `*to` a valid IPAny (v4/v6),
+ * prefers v6 if `ip.family` was TOX_AF_UNSPEC and both available
+ * Returns in `*extra` an IPv4 address, if family was TOX_AF_UNSPEC and `*to` is TOX_AF_INET6
+ *
+ * @return 0 on failure, `TOX_ADDR_RESOLVE_*` on success.
+ */
+non_null(1, 2, 3) nullable(4)
+static int addr_resolve(const Network *ns, const char *address, IP *to, IP *extra)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- return false;
-#else
+ if ((true)) {
+ return 0;
+ }
+#endif
- if (!address || !to) {
+ if (address == nullptr || to == nullptr) {
return 0;
}
- Family tox_family = to->family;
- int family = make_family(tox_family);
+ const Family tox_family = to->family;
+ const int family = make_family(tox_family);
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM; // type of socket Tox uses.
- if (networking_at_startup() != 0) {
- return 0;
- }
-
struct addrinfo *server = nullptr;
const int rc = getaddrinfo(address, nullptr, &hints, &server);
@@ -1329,9 +1520,9 @@ int addr_resolve(const char *address, IP *to, IP *extra)
}
IP ip4;
- ip_init(&ip4, 0); // ipv6enabled = 0
+ ip_init(&ip4, false); // ipv6enabled = false
IP ip6;
- ip_init(&ip6, 1); // ipv6enabled = 1
+ ip_init(&ip6, true); // ipv6enabled = true
int result = 0;
bool done = false;
@@ -1340,12 +1531,12 @@ int addr_resolve(const char *address, IP *to, IP *extra)
switch (walker->ai_family) {
case AF_INET: {
if (walker->ai_family == family) { /* AF_INET requested, done */
- const struct sockaddr_in *addr = (const struct sockaddr_in *)(void *)walker->ai_addr;
+ const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)walker->ai_addr;
get_ip4(&to->ip.v4, &addr->sin_addr);
result = TOX_ADDR_RESOLVE_INET;
done = true;
- } else if (!(result & TOX_ADDR_RESOLVE_INET)) { /* AF_UNSPEC requested, store away */
- const struct sockaddr_in *addr = (const struct sockaddr_in *)(void *)walker->ai_addr;
+ } else if ((result & TOX_ADDR_RESOLVE_INET) == 0) { /* AF_UNSPEC requested, store away */
+ const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)walker->ai_addr;
get_ip4(&ip4.ip.v4, &addr->sin_addr);
result |= TOX_ADDR_RESOLVE_INET;
}
@@ -1361,7 +1552,7 @@ int addr_resolve(const char *address, IP *to, IP *extra)
result = TOX_ADDR_RESOLVE_INET6;
done = true;
}
- } else if (!(result & TOX_ADDR_RESOLVE_INET6)) { /* AF_UNSPEC requested, store away */
+ } else if ((result & TOX_ADDR_RESOLVE_INET6) == 0) { /* AF_UNSPEC requested, store away */
if (walker->ai_addrlen == sizeof(struct sockaddr_in6)) {
const struct sockaddr_in6 *addr = (const struct sockaddr_in6 *)(void *)walker->ai_addr;
get_ip6(&ip6.ip.v6, &addr->sin6_addr);
@@ -1375,13 +1566,13 @@ int addr_resolve(const char *address, IP *to, IP *extra)
}
if (family == AF_UNSPEC) {
- if (result & TOX_ADDR_RESOLVE_INET6) {
+ if ((result & TOX_ADDR_RESOLVE_INET6) != 0) {
ip_copy(to, &ip6);
- if ((result & TOX_ADDR_RESOLVE_INET) && (extra != nullptr)) {
+ if ((result & TOX_ADDR_RESOLVE_INET) != 0 && (extra != nullptr)) {
ip_copy(extra, &ip4);
}
- } else if (result & TOX_ADDR_RESOLVE_INET) {
+ } else if ((result & TOX_ADDR_RESOLVE_INET) != 0) {
ip_copy(to, &ip4);
} else {
result = 0;
@@ -1390,12 +1581,11 @@ int addr_resolve(const char *address, IP *to, IP *extra)
freeaddrinfo(server);
return result;
-#endif
}
-bool addr_resolve_or_parse_ip(const char *address, IP *to, IP *extra)
+bool addr_resolve_or_parse_ip(const Network *ns, const char *address, IP *to, IP *extra)
{
- if (!addr_resolve(address, to, extra)) {
+ if (addr_resolve(ns, address, to, extra) == 0) {
if (!addr_parse_ip(address, to)) {
return false;
}
@@ -1404,7 +1594,7 @@ bool addr_resolve_or_parse_ip(const char *address, IP *to, IP *extra)
return true;
}
-int net_connect(const Logger *log, Socket sock, const IP_Port *ip_port)
+bool net_connect(const Logger *log, Socket sock, const IP_Port *ip_port)
{
struct sockaddr_storage addr = {0};
size_t addrsize;
@@ -1424,30 +1614,55 @@ int net_connect(const Logger *log, Socket sock, const IP_Port *ip_port)
fill_addr6(&ip_port->ip.ip.v6, &addr6->sin6_addr);
addr6->sin6_port = ip_port->port;
} else {
- return 0;
+ Ip_Ntoa ip_str;
+ LOGGER_ERROR(log, "cannot connect to %s:%d which is neither IPv4 nor IPv6",
+ net_ip_ntoa(&ip_port->ip, &ip_str), ip_port->port);
+ return false;
}
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- return 0;
-#else
- LOGGER_DEBUG(log, "connecting socket %d", (int)sock.socket);
- return connect(sock.socket, (struct sockaddr *)&addr, addrsize);
+ if ((true)) {
+ return true;
+ }
#endif
+
+ Ip_Ntoa ip_str;
+ LOGGER_DEBUG(log, "connecting socket %d to %s:%d",
+ (int)sock.sock, net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port));
+ errno = 0;
+
+ if (connect(sock.sock, (struct sockaddr *)&addr, addrsize) == -1) {
+ const int error = net_error();
+
+ // Non-blocking socket: "Operation in progress" means it's connecting.
+ if (!should_ignore_connect_error(error)) {
+ char *net_strerror = net_new_strerror(error);
+ LOGGER_ERROR(log, "failed to connect to %s:%d: %d (%s)",
+ net_ip_ntoa(&ip_port->ip, &ip_str), ip_port->port, error, net_strerror);
+ net_kill_strerror(net_strerror);
+ return false;
+ }
+ }
+
+ return true;
}
int32_t net_getipport(const char *node, IP_Port **res, int tox_type)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- *res = (IP_Port *)calloc(1, sizeof(IP_Port));
- assert(*res != nullptr);
- IP_Port *ip_port = *res;
- ip_port->ip.ip.v4.uint32 = 0x7F000003; // 127.0.0.3
- ip_port->ip.family = *make_tox_family(AF_INET);
+ if ((true)) {
+ *res = (IP_Port *)calloc(1, sizeof(IP_Port));
+ assert(*res != nullptr);
+ IP_Port *ip_port = *res;
+ ip_port->ip.ip.v4.uint32 = 0x7F000003; // 127.0.0.3
+ ip_port->ip.family = *make_tox_family(AF_INET);
+
+ return 1;
+ }
+#endif
- return 1;
-#else
// Try parsing as IP address first.
- IP_Port parsed = {0};
+ IP_Port parsed = {{{0}}};
if (addr_parse_ip(node, &parsed.ip)) {
IP_Port *tmp = (IP_Port *)calloc(1, sizeof(IP_Port));
@@ -1534,7 +1749,6 @@ int32_t net_getipport(const char *node, IP_Port **res, int tox_type)
freeaddrinfo(infos);
return count;
-#endif
}
void net_freeipport(IP_Port *ip_ports)
@@ -1542,106 +1756,42 @@ void net_freeipport(IP_Port *ip_ports)
free(ip_ports);
}
-bool bind_to_port(Socket sock, Family family, uint16_t port)
+bool bind_to_port(const Network *ns, Socket sock, Family family, uint16_t port)
{
- struct sockaddr_storage addr = {0};
- size_t addrsize;
+ Network_Addr addr = {{0}};
if (net_family_is_ipv4(family)) {
- struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr;
+ struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr.addr;
- addrsize = sizeof(struct sockaddr_in);
+ addr.size = sizeof(struct sockaddr_in);
addr4->sin_family = AF_INET;
addr4->sin_port = net_htons(port);
} else if (net_family_is_ipv6(family)) {
- struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr;
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr.addr;
- addrsize = sizeof(struct sockaddr_in6);
+ addr.size = sizeof(struct sockaddr_in6);
addr6->sin6_family = AF_INET6;
addr6->sin6_port = net_htons(port);
} else {
return false;
}
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- return true;
-#else
- return bind(sock.socket, (struct sockaddr *)&addr, addrsize) == 0;
-#endif
+ return net_bind(ns, sock, &addr) == 0;
}
-Socket net_socket(Family domain, int type, int protocol)
+Socket net_socket(const Network *ns, Family domain, int type, int protocol)
{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- const Socket sock = {1};
- return sock;
-#else
const int platform_domain = make_family(domain);
const int platform_type = make_socktype(type);
const int platform_prot = make_proto(protocol);
- const Socket sock = {(int)socket(platform_domain, platform_type, platform_prot)};
+ const Socket sock = {ns->funcs->socket(ns->obj, platform_domain, platform_type, platform_prot)};
return sock;
-#endif
-}
-
-int net_send(const Logger *log, Socket sock, const uint8_t *buf, size_t len, const IP_Port *ip_port)
-{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- int res = fuzz_send(sock.socket, (const char *)buf, len, MSG_NOSIGNAL);
-#else
- int res = send(sock.socket, (const char *)buf, len, MSG_NOSIGNAL);
-#endif
- loglogdata(log, "T=>", buf, len, ip_port, res);
- return res;
-}
-
-int net_recv(const Logger *log, Socket sock, uint8_t *buf, size_t len, const IP_Port *ip_port)
-{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- int res = fuzz_recv(sock.socket, (char *)buf, len, MSG_NOSIGNAL);
-#else
- int res = recv(sock.socket, (char *)buf, len, MSG_NOSIGNAL);
-#endif
- loglogdata(log, "=>T", buf, len, ip_port, res);
- return res;
-}
-
-int net_listen(Socket sock, int backlog)
-{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- return 0;
-#else
- return listen(sock.socket, backlog);
-#endif
-}
-
-Socket net_accept(Socket sock)
-{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- const Socket newsock = {2};
- return newsock;
-#else
- const Socket newsock = {accept(sock.socket, nullptr, nullptr)};
- return newsock;
-#endif
}
-uint16_t net_socket_data_recv_buffer(Socket sock)
+uint16_t net_socket_data_recv_buffer(const Network *ns, Socket sock)
{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- return 0;
-#else
-
-#ifdef OS_WIN32
- u_long count = 0;
- ioctlsocket(sock.socket, FIONREAD, &count);
-#else
- int count = 0;
- ioctl(sock.socket, FIONREAD, &count);
-#endif
-
- return (uint16_t)count;
-#endif
+ const int count = ns->funcs->recvbuf(ns->obj, sock.sock);
+ return (uint16_t)max_s32(0, min_s32(count, UINT16_MAX));
}
uint32_t net_htonl(uint32_t hostlong)
@@ -1689,8 +1839,8 @@ size_t net_pack_u64(uint8_t *bytes, uint64_t v)
size_t net_unpack_u16(const uint8_t *bytes, uint16_t *v)
{
- uint8_t hi = bytes[0];
- uint8_t lo = bytes[1];
+ const uint8_t hi = bytes[0];
+ const uint8_t lo = bytes[1];
*v = ((uint16_t)hi << 8) | lo;
return sizeof(*v);
}
@@ -1731,9 +1881,9 @@ int net_error(void)
#endif
}
+#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
char *net_new_strerror(int error)
{
-#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
char *str = nullptr;
// Windows API is weird. The 5th function arg is of char* type, but we
// have to pass char** so that it could assign new memory block to our
@@ -1745,29 +1895,42 @@ char *net_new_strerror(int error)
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
error, 0, (char *)&str, 0, nullptr);
return str;
+}
#else
- char tmp[256];
-
- errno = 0;
-
#ifdef _GNU_SOURCE
- const char *retstr = strerror_r(error, tmp, sizeof(tmp));
+non_null()
+static const char *net_strerror_r(int error, char *tmp, size_t tmp_size)
+{
+ const char *retstr = strerror_r(error, tmp, tmp_size);
if (errno != 0) {
- snprintf(tmp, sizeof(tmp), "error %d (strerror_r failed with errno %d)", error, errno);
+ snprintf(tmp, tmp_size, "error %d (strerror_r failed with errno %d)", error, errno);
}
+ return retstr;
+}
#else
- const int fmt_error = strerror_r(error, tmp, sizeof(tmp));
+non_null()
+static const char *net_strerror_r(int error, char *tmp, size_t tmp_size)
+{
+ const int fmt_error = strerror_r(error, tmp, tmp_size);
if (fmt_error != 0) {
- snprintf(tmp, sizeof(tmp), "error %d (strerror_r failed with error %d, errno %d)", error, fmt_error, errno);
+ snprintf(tmp, tmp_size, "error %d (strerror_r failed with error %d, errno %d)", error, fmt_error, errno);
}
- const char *retstr = tmp;
+ return tmp;
+}
#endif
+char *net_new_strerror(int error)
+{
+ char tmp[256];
+
+ errno = 0;
+ const char *retstr = net_strerror_r(error, tmp, sizeof(tmp));
const size_t retstr_len = strlen(retstr);
+
char *str = (char *)malloc(retstr_len + 1);
if (str == nullptr) {
@@ -1777,8 +1940,8 @@ char *net_new_strerror(int error)
memcpy(str, retstr, retstr_len + 1);
return str;
-#endif
}
+#endif
void net_kill_strerror(char *strerror)
{
diff --git a/protocols/Tox/libtox/src/toxcore/network.h b/protocols/Tox/libtox/src/toxcore/network.h
index 47a93bf0fa..a1fc6a74fc 100644
--- a/protocols/Tox/libtox/src/toxcore/network.h
+++ b/protocols/Tox/libtox/src/toxcore/network.h
@@ -19,6 +19,57 @@
extern "C" {
#endif
+/**
+ * @brief Wrapper for sockaddr_storage and size.
+ */
+typedef struct Network_Addr Network_Addr;
+
+typedef int net_close_cb(void *obj, int sock);
+typedef int net_accept_cb(void *obj, int sock);
+typedef int net_bind_cb(void *obj, int sock, const Network_Addr *addr);
+typedef int net_listen_cb(void *obj, int sock, int backlog);
+typedef int net_recvbuf_cb(void *obj, int sock);
+typedef int net_recv_cb(void *obj, int sock, uint8_t *buf, size_t len);
+typedef int net_recvfrom_cb(void *obj, int sock, uint8_t *buf, size_t len, Network_Addr *addr);
+typedef int net_send_cb(void *obj, int sock, const uint8_t *buf, size_t len);
+typedef int net_sendto_cb(void *obj, int sock, const uint8_t *buf, size_t len, const Network_Addr *addr);
+typedef int net_socket_cb(void *obj, int domain, int type, int proto);
+typedef int net_socket_nonblock_cb(void *obj, int sock, bool nonblock);
+typedef int net_getsockopt_cb(void *obj, int sock, int level, int optname, void *optval, size_t *optlen);
+typedef int net_setsockopt_cb(void *obj, int sock, int level, int optname, const void *optval, size_t optlen);
+typedef int net_getaddrinfo_cb(void *obj, int family, Network_Addr **addrs);
+typedef int net_freeaddrinfo_cb(void *obj, Network_Addr *addrs);
+
+/** @brief Functions wrapping POSIX network functions.
+ *
+ * Refer to POSIX man pages for documentation of what these functions are
+ * expected to do when providing alternative Network implementations.
+ */
+typedef struct Network_Funcs {
+ net_close_cb *close;
+ net_accept_cb *accept;
+ net_bind_cb *bind;
+ net_listen_cb *listen;
+ net_recvbuf_cb *recvbuf;
+ net_recv_cb *recv;
+ net_recvfrom_cb *recvfrom;
+ net_send_cb *send;
+ net_sendto_cb *sendto;
+ net_socket_cb *socket;
+ net_socket_nonblock_cb *socket_nonblock;
+ net_getsockopt_cb *getsockopt;
+ net_setsockopt_cb *setsockopt;
+ net_getaddrinfo_cb *getaddrinfo;
+ net_freeaddrinfo_cb *freeaddrinfo;
+} Network_Funcs;
+
+typedef struct Network {
+ const Network_Funcs *funcs;
+ void *obj;
+} Network;
+
+const Network *system_network(void);
+
typedef struct Family {
uint8_t value;
} Family;
@@ -26,25 +77,65 @@ typedef struct Family {
bool net_family_is_unspec(Family family);
bool net_family_is_ipv4(Family family);
bool net_family_is_ipv6(Family family);
-bool net_family_is_tcp_family(Family family);
-bool net_family_is_tcp_onion(Family family);
+bool net_family_is_tcp_server(Family family);
+bool net_family_is_tcp_client(Family family);
bool net_family_is_tcp_ipv4(Family family);
bool net_family_is_tcp_ipv6(Family family);
bool net_family_is_tox_tcp_ipv4(Family family);
bool net_family_is_tox_tcp_ipv6(Family family);
-extern const Family net_family_unspec;
-extern const Family net_family_ipv4;
-extern const Family net_family_ipv6;
-extern const Family net_family_tcp_family;
-extern const Family net_family_tcp_onion;
-extern const Family net_family_tcp_ipv4;
-extern const Family net_family_tcp_ipv6;
-extern const Family net_family_tox_tcp_ipv4;
-extern const Family net_family_tox_tcp_ipv6;
+Family net_family_unspec(void);
+Family net_family_ipv4(void);
+Family net_family_ipv6(void);
+Family net_family_tcp_server(void);
+Family net_family_tcp_client(void);
+Family net_family_tcp_ipv4(void);
+Family net_family_tcp_ipv6(void);
+Family net_family_tox_tcp_ipv4(void);
+Family net_family_tox_tcp_ipv6(void);
#define MAX_UDP_PACKET_SIZE 2048
+#ifdef USE_TEST_NETWORK
+typedef enum Net_Packet_Type {
+ NET_PACKET_PING_REQUEST = 0x05, /* Ping request packet ID. */
+ NET_PACKET_PING_RESPONSE = 0x06, /* Ping response packet ID. */
+ NET_PACKET_GET_NODES = 0x07, /* Get nodes request packet ID. */
+ NET_PACKET_SEND_NODES_IPV6 = 0x08, /* Send nodes response packet ID for other addresses. */
+ NET_PACKET_COOKIE_REQUEST = 0x1c, /* Cookie request packet */
+ NET_PACKET_COOKIE_RESPONSE = 0x1d, /* Cookie response packet */
+ NET_PACKET_CRYPTO_HS = 0x1e, /* Crypto handshake packet */
+ NET_PACKET_CRYPTO_DATA = 0x1f, /* Crypto data packet */
+ NET_PACKET_CRYPTO = 0x24, /* Encrypted data packet ID. */
+ NET_PACKET_LAN_DISCOVERY = 0x25, /* LAN discovery packet ID. */
+
+ // TODO(Jfreegman): Uncomment these when we merge the rest of new groupchats
+ // NET_PACKET_GC_HANDSHAKE = 0x62, /* Group chat handshake packet ID */
+ // NET_PACKET_GC_LOSSLESS = 0x63, /* Group chat lossless packet ID */
+ // NET_PACKET_GC_LOSSY = 0x64, /* Group chat lossy packet ID */
+
+ /* See: `docs/Prevent_Tracking.txt` and `onion.{c,h}` */
+ NET_PACKET_ONION_SEND_INITIAL = 0x8f,
+ NET_PACKET_ONION_SEND_1 = 0x90,
+ NET_PACKET_ONION_SEND_2 = 0x91,
+
+ NET_PACKET_ANNOUNCE_REQUEST = 0x92,
+ NET_PACKET_ANNOUNCE_RESPONSE = 0x93,
+ NET_PACKET_ONION_DATA_REQUEST = 0x94,
+ NET_PACKET_ONION_DATA_RESPONSE = 0x95,
+
+ NET_PACKET_ANNOUNCE_REQUEST_OLD = 0x96, /* TODO: DEPRECATE */
+ NET_PACKET_ANNOUNCE_RESPONSE_OLD = 0x97, /* TODO: DEPRECATE */
+
+ NET_PACKET_ONION_RECV_3 = 0x9b,
+ NET_PACKET_ONION_RECV_2 = 0x9c,
+ NET_PACKET_ONION_RECV_1 = 0x9d,
+
+ BOOTSTRAP_INFO_PACKET_ID = 0xf1, /* Only used for bootstrap nodes */
+
+ NET_PACKET_MAX = 0xff, /* This type must remain within a single uint8. */
+} Net_Packet_Type;
+#else
typedef enum Net_Packet_Type {
NET_PACKET_PING_REQUEST = 0x00, /* Ping request packet ID. */
NET_PACKET_PING_RESPONSE = 0x01, /* Ping response packet ID. */
@@ -57,24 +148,44 @@ typedef enum Net_Packet_Type {
NET_PACKET_CRYPTO = 0x20, /* Encrypted data packet ID. */
NET_PACKET_LAN_DISCOVERY = 0x21, /* LAN discovery packet ID. */
+ // TODO(Jfreegman): Uncomment these when we merge the rest of new groupchats
+ // NET_PACKET_GC_HANDSHAKE = 0x5a, /* Group chat handshake packet ID */
+ // NET_PACKET_GC_LOSSLESS = 0x5b, /* Group chat lossless packet ID */
+ // NET_PACKET_GC_LOSSY = 0x5c, /* Group chat lossy packet ID */
+
/* See: `docs/Prevent_Tracking.txt` and `onion.{c,h}` */
NET_PACKET_ONION_SEND_INITIAL = 0x80,
NET_PACKET_ONION_SEND_1 = 0x81,
NET_PACKET_ONION_SEND_2 = 0x82,
- NET_PACKET_ANNOUNCE_REQUEST = 0x83,
- NET_PACKET_ANNOUNCE_RESPONSE = 0x84,
+ NET_PACKET_ANNOUNCE_REQUEST_OLD = 0x83, /* TODO: DEPRECATE */
+ NET_PACKET_ANNOUNCE_RESPONSE_OLD = 0x84, /* TODO: DEPRECATE */
+
NET_PACKET_ONION_DATA_REQUEST = 0x85,
NET_PACKET_ONION_DATA_RESPONSE = 0x86,
+ NET_PACKET_ANNOUNCE_REQUEST = 0x87,
+ NET_PACKET_ANNOUNCE_RESPONSE = 0x88,
NET_PACKET_ONION_RECV_3 = 0x8c,
NET_PACKET_ONION_RECV_2 = 0x8d,
NET_PACKET_ONION_RECV_1 = 0x8e,
+ NET_PACKET_FORWARD_REQUEST = 0x90,
+ NET_PACKET_FORWARDING = 0x91,
+ NET_PACKET_FORWARD_REPLY = 0x92,
+
+ NET_PACKET_DATA_SEARCH_REQUEST = 0x93,
+ NET_PACKET_DATA_SEARCH_RESPONSE = 0x94,
+ NET_PACKET_DATA_RETRIEVE_REQUEST = 0x95,
+ NET_PACKET_DATA_RETRIEVE_RESPONSE = 0x96,
+ NET_PACKET_STORE_ANNOUNCE_REQUEST = 0x97,
+ NET_PACKET_STORE_ANNOUNCE_RESPONSE = 0x98,
+
BOOTSTRAP_INFO_PACKET_ID = 0xf0, /* Only used for bootstrap nodes */
NET_PACKET_MAX = 0xff, /* This type must remain within a single uint8. */
} Net_Packet_Type;
+#endif // test network
#define TOX_PORTRANGE_FROM 33445
@@ -95,10 +206,10 @@ typedef enum Net_Packet_Type {
#define TOX_PROTO_UDP 2
/** TCP related */
-#define TCP_ONION_FAMILY (TOX_AF_INET6 + 1)
+#define TCP_CLIENT_FAMILY (TOX_AF_INET6 + 1)
#define TCP_INET (TOX_AF_INET6 + 2)
#define TCP_INET6 (TOX_AF_INET6 + 3)
-#define TCP_FAMILY (TOX_AF_INET6 + 4)
+#define TCP_SERVER_FAMILY (TOX_AF_INET6 + 4)
#define SIZE_IP4 4
#define SIZE_IP6 16
@@ -112,8 +223,6 @@ typedef union IP4 {
uint8_t uint8[4];
} IP4;
-static_assert(sizeof(IP4) == SIZE_IP4, "IP4 size must be 4");
-
IP4 get_ip4_loopback(void);
extern const IP4 ip4_broadcast;
@@ -124,10 +233,6 @@ typedef union IP6 {
uint64_t uint64[2];
} IP6;
-// TODO(iphydf): Stop relying on this. We memcpy this struct (and IP4 above)
-// into packets but really should be serialising it properly.
-static_assert(sizeof(IP6) == SIZE_IP6, "IP6 size must be 16");
-
IP6 get_ip6_loopback(void);
extern const IP6 ip6_broadcast;
@@ -146,11 +251,14 @@ typedef struct IP_Port {
uint16_t port;
} IP_Port;
+extern const IP_Port empty_ip_port;
+
typedef struct Socket {
- int socket;
+ int sock;
} Socket;
-Socket net_socket(Family domain, int type, int protocol);
+non_null()
+Socket net_socket(const Network *ns, Family domain, int type, int protocol);
/**
* Check if socket is valid.
@@ -164,64 +272,77 @@ extern const Socket net_invalid_socket;
/**
* Calls send(sockfd, buf, len, MSG_NOSIGNAL).
*/
-int net_send(const Logger *log, Socket sock, const uint8_t *buf, size_t len, const IP_Port *ip_port);
+non_null()
+int net_send(const Network *ns, const Logger *log, Socket sock, const uint8_t *buf, size_t len, const IP_Port *ip_port);
/**
* Calls recv(sockfd, buf, len, MSG_NOSIGNAL).
*/
-int net_recv(const Logger *log, Socket sock, uint8_t *buf, size_t len, const IP_Port *ip_port);
+non_null()
+int net_recv(const Network *ns, const Logger *log, Socket sock, uint8_t *buf, size_t len, const IP_Port *ip_port);
/**
* Calls listen(sockfd, backlog).
*/
-int net_listen(Socket sock, int backlog);
+non_null()
+int net_listen(const Network *ns, Socket sock, int backlog);
/**
* Calls accept(sockfd, nullptr, nullptr).
*/
-Socket net_accept(Socket sock);
+non_null()
+Socket net_accept(const Network *ns, Socket sock);
/**
* return the size of data in the tcp recv buffer.
* return 0 on failure.
*/
-uint16_t net_socket_data_recv_buffer(Socket sock);
+non_null()
+uint16_t net_socket_data_recv_buffer(const Network *ns, Socket sock);
-/** Convert values between host and network byte order.
- */
+/** Convert values between host and network byte order. */
uint32_t net_htonl(uint32_t hostlong);
uint16_t net_htons(uint16_t hostshort);
uint32_t net_ntohl(uint32_t hostlong);
uint16_t net_ntohs(uint16_t hostshort);
+non_null()
size_t net_pack_u16(uint8_t *bytes, uint16_t v);
+non_null()
size_t net_pack_u32(uint8_t *bytes, uint32_t v);
+non_null()
size_t net_pack_u64(uint8_t *bytes, uint64_t v);
+non_null()
size_t net_unpack_u16(const uint8_t *bytes, uint16_t *v);
+non_null()
size_t net_unpack_u32(const uint8_t *bytes, uint32_t *v);
+non_null()
size_t net_unpack_u64(const uint8_t *bytes, uint64_t *v);
/** Does the IP6 struct a contain an IPv4 address in an IPv6 one? */
+non_null()
bool ipv6_ipv4_in_v6(const IP6 *a);
#define TOX_ENABLE_IPV6_DEFAULT true
-/** addr_resolve return values */
-#define TOX_ADDR_RESOLVE_INET 1
-#define TOX_ADDR_RESOLVE_INET6 2
-
#define TOX_INET6_ADDRSTRLEN 66
#define TOX_INET_ADDRSTRLEN 22
/** this would be TOX_INET6_ADDRSTRLEN, but it might be too short for the error message */
#define IP_NTOA_LEN 96 // TODO(irungentoo): magic number. Why not INET6_ADDRSTRLEN ?
-/** ip_ntoa
- * converts ip into a string
- * ip_str must be of length at least IP_NTOA_LEN
+
+typedef struct Ip_Ntoa {
+ char buf[IP_NTOA_LEN];
+} Ip_Ntoa;
+
+/** @brief Converts IP into a string.
+ *
+ * Writes error message into the buffer on error.
*
- * writes error message into the buffer on error
+ * @param ip_str contains a buffer of the required size.
*
- * returns ip_str
+ * @return Pointer to the buffer inside `ip_str` containing the IP string.
*/
-const char *ip_ntoa(const IP *ip, char *ip_str, size_t length);
+non_null()
+const char *net_ip_ntoa(const IP *ip, Ip_Ntoa *ip_str);
/**
* Parses IP structure into an address string.
@@ -235,6 +356,7 @@ const char *ip_ntoa(const IP *ip, char *ip_str, size_t length);
*
* @return true on success, false on failure.
*/
+non_null()
bool ip_parse_addr(const IP *ip, char *address, size_t length);
/**
@@ -247,6 +369,7 @@ bool ip_parse_addr(const IP *ip, char *address, size_t length);
*
* @return true on success, false on failure.
*/
+non_null()
bool addr_parse_ip(const char *address, IP *to);
/**
@@ -256,6 +379,7 @@ bool addr_parse_ip(const char *address, IP *to);
*
* @return false when not equal or when uninitialized.
*/
+nullable(1, 2)
bool ip_equal(const IP *a, const IP *b);
/**
@@ -265,59 +389,49 @@ bool ip_equal(const IP *a, const IP *b);
*
* @return false when not equal or when uninitialized.
*/
+nullable(1, 2)
bool ipport_equal(const IP_Port *a, const IP_Port *b);
/** nulls out ip */
+non_null()
void ip_reset(IP *ip);
/** nulls out ip_port */
+non_null()
void ipport_reset(IP_Port *ipport);
/** nulls out ip, sets family according to flag */
+non_null()
void ip_init(IP *ip, bool ipv6enabled);
/** checks if ip is valid */
+non_null()
bool ip_isset(const IP *ip);
/** checks if ip is valid */
+non_null()
bool ipport_isset(const IP_Port *ipport);
-/** copies an ip structure (careful about direction!) */
+/** copies an ip structure (careful about direction) */
+non_null()
void ip_copy(IP *target, const IP *source);
-/** copies an ip_port structure (careful about direction!) */
+/** copies an ip_port structure (careful about direction) */
+non_null()
void ipport_copy(IP_Port *target, const IP_Port *source);
/**
- * Uses getaddrinfo to resolve an address into an IP address.
- *
- * Uses the first IPv4/IPv6 addresses returned by getaddrinfo.
- *
- * @param address a hostname (or something parseable to an IP address)
- * @param to to.family MUST be initialized, either set to a specific IP version
- * (TOX_AF_INET/TOX_AF_INET6) or to the unspecified TOX_AF_UNSPEC (= 0), if both
- * IP versions are acceptable
- * @param extra can be NULL and is only set in special circumstances, see returns
- *
- * returns in `*to` a valid IPAny (v4/v6),
- * prefers v6 if `ip.family` was TOX_AF_UNSPEC and both available
- * returns in `*extra` an IPv4 address, if family was TOX_AF_UNSPEC and `*to` is TOX_AF_INET6
- *
- * @return 0 on failure, `TOX_ADDR_RESOLVE_*` on success.
- */
-int addr_resolve(const char *address, IP *to, IP *extra);
-
-/**
* Resolves string into an IP address
*
* @param address a hostname (or something parseable to an IP address)
* @param to to.family MUST be initialized, either set to a specific IP version
- * (TOX_AF_INET/TOX_AF_INET6) or to the unspecified TOX_AF_UNSPEC (= 0), if both
- * IP versions are acceptable
+ * (TOX_AF_INET/TOX_AF_INET6) or to the unspecified TOX_AF_UNSPEC (0), if both
+ * IP versions are acceptable
* @param extra can be NULL and is only set in special circumstances, see returns
*
- * returns in `*to` a matching address (IPv6 or IPv4)
- * returns in `*extra`, if not NULL, an IPv4 address, if `to->family` was TOX_AF_UNSPEC
+ * Returns in `*to` a matching address (IPv6 or IPv4)
+ * Returns in `*extra`, if not NULL, an IPv4 address, if `to->family` was TOX_AF_UNSPEC
*
* @return true on success, false on failure
*/
-bool addr_resolve_or_parse_ip(const char *address, IP *to, IP *extra);
+non_null(1, 2, 3) nullable(4)
+bool addr_resolve_or_parse_ip(const Network *ns, const char *address, IP *to, IP *extra);
-/** Function to receive data, ip and port of sender is put into ip_port.
+/** @brief Function to receive data, ip and port of sender is put into ip_port.
* Packet data is put into data.
* Packet length is put into length.
*/
@@ -325,47 +439,46 @@ typedef int packet_handler_cb(void *object, const IP_Port *ip_port, const uint8_
typedef struct Networking_Core Networking_Core;
+non_null()
Family net_family(const Networking_Core *net);
+non_null()
uint16_t net_port(const Networking_Core *net);
-/** Run this before creating sockets.
- *
- * return 0 on success
- * return -1 on failure
- */
-int networking_at_startup(void);
-
-/** Close the socket.
- */
-void kill_sock(Socket sock);
+/** Close the socket. */
+non_null()
+void kill_sock(const Network *ns, Socket sock);
/**
* Set socket as nonblocking
*
* @return true on success, false on failure.
*/
-bool set_socket_nonblock(Socket sock);
+non_null()
+bool set_socket_nonblock(const Network *ns, Socket sock);
/**
* Set socket to not emit SIGPIPE
*
* @return true on success, false on failure.
*/
-bool set_socket_nosigpipe(Socket sock);
+non_null()
+bool set_socket_nosigpipe(const Network *ns, Socket sock);
/**
* Enable SO_REUSEADDR on socket.
*
* @return true on success, false on failure.
*/
-bool set_socket_reuseaddr(Socket sock);
+non_null()
+bool set_socket_reuseaddr(const Network *ns, Socket sock);
/**
* Set socket to dual (IPv4 + IPv6 socket)
*
* @return true on success, false on failure.
*/
-bool set_socket_dualstack(Socket sock);
+non_null()
+bool set_socket_dualstack(const Network *ns, Socket sock);
/* Basic network functions: */
@@ -382,6 +495,7 @@ typedef struct Packet {
/**
* Function to send a network packet to a given IP/port.
*/
+non_null()
int send_packet(const Networking_Core *net, const IP_Port *ip_port, Packet packet);
/**
@@ -389,44 +503,51 @@ int send_packet(const Networking_Core *net, const IP_Port *ip_port, Packet packe
*
* @deprecated Use send_packet instead.
*/
+non_null()
int sendpacket(const Networking_Core *net, const IP_Port *ip_port, const uint8_t *data, uint16_t length);
/** Function to call when packet beginning with byte is received. */
+non_null(1) nullable(3, 4)
void networking_registerhandler(Networking_Core *net, uint8_t byte, packet_handler_cb *cb, void *object);
/** Call this several times a second. */
+non_null(1) nullable(2)
void networking_poll(const Networking_Core *net, void *userdata);
-/** Connect a socket to the address specified by the ip_port.
+/** @brief Connect a socket to the address specified by the ip_port.
*
- * Return 0 on success.
- * Return -1 on failure.
+ * Return true on success.
+ * Return false on failure.
*/
-int net_connect(const Logger *log, Socket sock, const IP_Port *ip_port);
+non_null()
+bool net_connect(const Logger *log, Socket sock, const IP_Port *ip_port);
-/** High-level getaddrinfo implementation.
- * Given node, which identifies an Internet host, net_getipport() fills an array
+/** @brief High-level getaddrinfo implementation.
+ *
+ * Given node, which identifies an Internet host, `net_getipport()` fills an array
* with one or more IP_Port structures, each of which contains an Internet
- * address that can be specified by calling net_connect(), the port is ignored.
+ * address that can be specified by calling `net_connect()`, the port is ignored.
*
* Skip all addresses with socktype != type (use type = -1 to get all addresses)
- * To correctly deallocate array memory use net_freeipport()
+ * To correctly deallocate array memory use `net_freeipport()`
*
* return number of elements in res array
* and -1 on error.
*/
+non_null()
int32_t net_getipport(const char *node, IP_Port **res, int tox_type);
-/** Deallocates memory allocated by net_getipport
- */
+/** Deallocates memory allocated by net_getipport */
+nullable(1)
void net_freeipport(IP_Port *ip_ports);
/**
* @return true on success, false on failure.
*/
-bool bind_to_port(Socket sock, Family family, uint16_t port);
+non_null()
+bool bind_to_port(const Network *ns, Socket sock, Family family, uint16_t port);
-/** Get the last networking error code.
+/** @brief Get the last networking error code.
*
* Similar to Unix's errno, but cross-platform, as not all platforms use errno
* to indicate networking errors.
@@ -434,45 +555,47 @@ bool bind_to_port(Socket sock, Family family, uint16_t port);
* Note that different platforms may return different codes for the same error,
* so you likely shouldn't be checking the value returned by this function
* unless you know what you are doing, you likely just want to use it in
- * combination with net_new_strerror() to print the error.
+ * combination with `net_new_strerror()` to print the error.
*
* return platform-dependent network error code, if any.
*/
int net_error(void);
-/** Get a text explanation for the error code from net_error().
+/** @brief Get a text explanation for the error code from `net_error()`.
*
* return NULL on failure.
* return pointer to a NULL-terminated string describing the error code on
- * success. The returned string must be freed using net_kill_strerror().
+ * success. The returned string must be freed using `net_kill_strerror()`.
*/
char *net_new_strerror(int error);
-/** Frees the string returned by net_new_strerror().
+/** @brief Frees the string returned by `net_new_strerror()`.
* It's valid to pass NULL as the argument, the function does nothing in this
* case.
*/
+non_null()
void net_kill_strerror(char *strerror);
-/** Initialize networking.
- * Added for reverse compatibility with old new_networking calls.
- */
-Networking_Core *new_networking(const Logger *log, const IP *ip, uint16_t port);
-/** Initialize networking.
+/** @brief Initialize networking.
* Bind to ip and port.
* ip must be in network order EX: 127.0.0.1 = (7F000001).
* port is in host byte order (this means don't worry about it).
*
- * return Networking_Core object if no problems
- * return NULL if there are problems.
+ * @return Networking_Core object if no problems
+ * @retval NULL if there are problems.
*
* If error is non NULL it is set to 0 if no issues, 1 if socket related error, 2 if other.
*/
-Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t port_from, uint16_t port_to,
- unsigned int *error);
-Networking_Core *new_networking_no_udp(const Logger *log);
+non_null(1, 2, 3) nullable(6)
+Networking_Core *new_networking_ex(
+ const Logger *log, const Network *ns, const IP *ip,
+ uint16_t port_from, uint16_t port_to, unsigned int *error);
+
+non_null()
+Networking_Core *new_networking_no_udp(const Logger *log, const Network *ns);
/** Function to cleanup networking stuff (doesn't do much right now). */
+nullable(1)
void kill_networking(Networking_Core *net);
#ifdef __cplusplus
diff --git a/protocols/Tox/libtox/src/toxcore/onion.c b/protocols/Tox/libtox/src/toxcore/onion.c
index 663c7b1b5c..d7e3f02665 100644
--- a/protocols/Tox/libtox/src/toxcore/onion.c
+++ b/protocols/Tox/libtox/src/toxcore/onion.c
@@ -8,9 +8,11 @@
*/
#include "onion.h"
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
+#include "ccompat.h"
#include "mono_time.h"
#include "util.h"
@@ -24,16 +26,19 @@
#define SEND_1 ONION_SEND_1
#define KEY_REFRESH_INTERVAL (2 * 60 * 60)
+
/** Change symmetric keys every 2 hours to make paths expire eventually. */
+non_null()
static void change_symmetric_key(Onion *onion)
{
if (mono_time_is_timeout(onion->mono_time, onion->timestamp, KEY_REFRESH_INTERVAL)) {
- new_symmetric_key(onion->secret_symmetric_key);
+ new_symmetric_key(onion->rng, onion->secret_symmetric_key);
onion->timestamp = mono_time_get(onion->mono_time);
}
}
/** packing and unpacking functions */
+non_null()
static void ip_pack(uint8_t *data, const IP *source)
{
data[0] = source->family.value;
@@ -47,6 +52,7 @@ static void ip_pack(uint8_t *data, const IP *source)
}
/** return 0 on success, -1 on failure. */
+non_null()
static int ip_unpack(IP *target, const uint8_t *data, unsigned int data_size, bool disable_family_check)
{
if (data_size < (1 + SIZE_IP6)) {
@@ -62,13 +68,14 @@ static int ip_unpack(IP *target, const uint8_t *data, unsigned int data_size, bo
memcpy(target->ip.v6.uint8, data + 1, SIZE_IP6);
}
- bool valid = disable_family_check ||
+ const bool valid = disable_family_check ||
net_family_is_ipv4(target->family) ||
net_family_is_ipv6(target->family);
return valid ? 0 : -1;
}
+non_null()
static void ipport_pack(uint8_t *data, const IP_Port *source)
{
ip_pack(data, &source->ip);
@@ -76,6 +83,7 @@ static void ipport_pack(uint8_t *data, const IP_Port *source)
}
/** return 0 on success, -1 on failure. */
+non_null()
static int ipport_unpack(IP_Port *target, const uint8_t *data, unsigned int data_size, bool disable_family_check)
{
if (data_size < (SIZE_IP + SIZE_PORT)) {
@@ -91,7 +99,7 @@ static int ipport_unpack(IP_Port *target, const uint8_t *data, unsigned int data
}
-/** Create a new onion path.
+/** @brief Create a new onion path.
*
* Create a new onion path out of nodes (nodes is a list of ONION_PATH_LENGTH nodes)
*
@@ -100,9 +108,9 @@ static int ipport_unpack(IP_Port *target, const uint8_t *data, unsigned int data
* return -1 on failure.
* return 0 on success.
*/
-int create_onion_path(const DHT *dht, Onion_Path *new_path, const Node_format *nodes)
+int create_onion_path(const Random *rng, const DHT *dht, Onion_Path *new_path, const Node_format *nodes)
{
- if (!new_path || !nodes) {
+ if (new_path == nullptr || nodes == nullptr) {
return -1;
}
@@ -112,11 +120,11 @@ int create_onion_path(const DHT *dht, Onion_Path *new_path, const Node_format *n
uint8_t random_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t random_secret_key[CRYPTO_SECRET_KEY_SIZE];
- crypto_new_keypair(random_public_key, random_secret_key);
+ crypto_new_keypair(rng, random_public_key, random_secret_key);
encrypt_precompute(nodes[1].public_key, random_secret_key, new_path->shared_key2);
memcpy(new_path->public_key2, random_public_key, CRYPTO_PUBLIC_KEY_SIZE);
- crypto_new_keypair(random_public_key, random_secret_key);
+ crypto_new_keypair(rng, random_public_key, random_secret_key);
encrypt_precompute(nodes[2].public_key, random_secret_key, new_path->shared_key3);
memcpy(new_path->public_key3, random_public_key, CRYPTO_PUBLIC_KEY_SIZE);
@@ -133,7 +141,7 @@ int create_onion_path(const DHT *dht, Onion_Path *new_path, const Node_format *n
return 0;
}
-/** Dump nodes in onion path to nodes of length num_nodes.
+/** @brief Dump nodes in onion path to nodes of length num_nodes.
*
* return -1 on failure.
* return 0 on success.
@@ -154,7 +162,7 @@ int onion_path_to_nodes(Node_format *nodes, unsigned int num_nodes, const Onion_
return 0;
}
-/** Create a onion packet.
+/** @brief Create a onion packet.
*
* Use Onion_Path path to create packet for data of length to dest.
* Maximum length of data is ONION_MAX_DATA_SIZE.
@@ -163,7 +171,8 @@ int onion_path_to_nodes(Node_format *nodes, unsigned int num_nodes, const Onion_
* return -1 on failure.
* return length of created packet on success.
*/
-int create_onion_packet(uint8_t *packet, uint16_t max_packet_length, const Onion_Path *path, const IP_Port *dest,
+int create_onion_packet(const Random *rng, uint8_t *packet, uint16_t max_packet_length,
+ const Onion_Path *path, const IP_Port *dest,
const uint8_t *data, uint16_t length)
{
if (1 + length + SEND_1 > max_packet_length || length == 0) {
@@ -176,7 +185,7 @@ int create_onion_packet(uint8_t *packet, uint16_t max_packet_length, const Onion
memcpy(step1 + SIZE_IPPORT, data, length);
uint8_t nonce[CRYPTO_NONCE_SIZE];
- random_nonce(nonce);
+ random_nonce(rng, nonce);
VLA(uint8_t, step2, SIZE_IPPORT + SEND_BASE + length);
ipport_pack(step2, &path->ip_port3);
@@ -213,7 +222,7 @@ int create_onion_packet(uint8_t *packet, uint16_t max_packet_length, const Onion
return 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + len;
}
-/** Create a onion packet to be sent over tcp.
+/** @brief Create a onion packet to be sent over tcp.
*
* Use Onion_Path path to create packet for data of length to dest.
* Maximum length of data is ONION_MAX_DATA_SIZE.
@@ -222,7 +231,8 @@ int create_onion_packet(uint8_t *packet, uint16_t max_packet_length, const Onion
* return -1 on failure.
* return length of created packet on success.
*/
-int create_onion_packet_tcp(uint8_t *packet, uint16_t max_packet_length, const Onion_Path *path, const IP_Port *dest,
+int create_onion_packet_tcp(const Random *rng, uint8_t *packet, uint16_t max_packet_length,
+ const Onion_Path *path, const IP_Port *dest,
const uint8_t *data, uint16_t length)
{
if (CRYPTO_NONCE_SIZE + SIZE_IPPORT + SEND_BASE * 2 + length > max_packet_length || length == 0) {
@@ -235,7 +245,7 @@ int create_onion_packet_tcp(uint8_t *packet, uint16_t max_packet_length, const O
memcpy(step1 + SIZE_IPPORT, data, length);
uint8_t nonce[CRYPTO_NONCE_SIZE];
- random_nonce(nonce);
+ random_nonce(rng, nonce);
VLA(uint8_t, step2, SIZE_IPPORT + SEND_BASE + length);
ipport_pack(step2, &path->ip_port3);
@@ -262,32 +272,7 @@ int create_onion_packet_tcp(uint8_t *packet, uint16_t max_packet_length, const O
return CRYPTO_NONCE_SIZE + SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE + len;
}
-/** Create and send a onion packet.
- *
- * Use Onion_Path path to send data of length to dest.
- * Maximum length of data is ONION_MAX_DATA_SIZE.
- *
- * return -1 on failure.
- * return 0 on success.
- */
-int send_onion_packet(const Networking_Core *net, const Onion_Path *path, const IP_Port *dest, const uint8_t *data,
- uint16_t length)
-{
- uint8_t packet[ONION_MAX_PACKET_SIZE];
- int len = create_onion_packet(packet, sizeof(packet), path, dest, data, length);
-
- if (len == -1) {
- return -1;
- }
-
- if (sendpacket(net, &path->ip_port1, packet, len) != len) {
- return -1;
- }
-
- return 0;
-}
-
-/** Create and send a onion response sent initially to dest with.
+/** @brief Create and send a onion response sent initially to dest with.
* Maximum length of data is ONION_RESPONSE_MAX_DATA_SIZE.
*
* return -1 on failure.
@@ -312,6 +297,7 @@ int send_onion_response(const Networking_Core *net, const IP_Port *dest, const u
return 0;
}
+non_null()
static int handle_send_initial(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
@@ -331,7 +317,7 @@ static int handle_send_initial(void *object, const IP_Port *source, const uint8_
uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE];
get_shared_key(onion->mono_time, &onion->shared_keys_1, shared_key, dht_get_self_secret_key(onion->dht),
packet + 1 + CRYPTO_NONCE_SIZE);
- int len = decrypt_data_symmetric(shared_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE,
+ const int len = decrypt_data_symmetric(shared_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE,
length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE), plain);
if (len != length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE)) {
@@ -353,7 +339,7 @@ int onion_send_1(const Onion *onion, const uint8_t *plain, uint16_t len, const I
IP_Port send_to;
- if (ipport_unpack(&send_to, plain, len, 0) == -1) {
+ if (ipport_unpack(&send_to, plain, len, false) == -1) {
return 1;
}
@@ -366,7 +352,7 @@ int onion_send_1(const Onion *onion, const uint8_t *plain, uint16_t len, const I
memcpy(data + 1 + CRYPTO_NONCE_SIZE, plain + SIZE_IPPORT, len - SIZE_IPPORT);
uint16_t data_len = 1 + CRYPTO_NONCE_SIZE + (len - SIZE_IPPORT);
uint8_t *ret_part = data + data_len;
- random_nonce(ret_part);
+ random_nonce(onion->rng, ret_part);
len = encrypt_data_symmetric(onion->secret_symmetric_key, ret_part, ip_port, SIZE_IPPORT,
ret_part + CRYPTO_NONCE_SIZE);
@@ -383,6 +369,7 @@ int onion_send_1(const Onion *onion, const uint8_t *plain, uint16_t len, const I
return 0;
}
+non_null()
static int handle_send_1(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata)
{
Onion *onion = (Onion *)object;
@@ -410,7 +397,7 @@ static int handle_send_1(void *object, const IP_Port *source, const uint8_t *pac
IP_Port send_to;
- if (ipport_unpack(&send_to, plain, len, 0) == -1) {
+ if (ipport_unpack(&send_to, plain, len, false) == -1) {
return 1;
}
@@ -420,7 +407,7 @@ static int handle_send_1(void *object, const IP_Port *source, const uint8_t *pac
memcpy(data + 1 + CRYPTO_NONCE_SIZE, plain + SIZE_IPPORT, len - SIZE_IPPORT);
uint16_t data_len = 1 + CRYPTO_NONCE_SIZE + (len - SIZE_IPPORT);
uint8_t *ret_part = data + data_len;
- random_nonce(ret_part);
+ random_nonce(onion->rng, ret_part);
uint8_t ret_data[RETURN_1 + SIZE_IPPORT];
ipport_pack(ret_data, source);
memcpy(ret_data + SIZE_IPPORT, packet + (length - RETURN_1), RETURN_1);
@@ -440,6 +427,7 @@ static int handle_send_1(void *object, const IP_Port *source, const uint8_t *pac
return 0;
}
+non_null()
static int handle_send_2(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata)
{
Onion *onion = (Onion *)object;
@@ -465,19 +453,17 @@ static int handle_send_2(void *object, const IP_Port *source, const uint8_t *pac
return 1;
}
- if (len <= SIZE_IPPORT) {
- return 1;
- }
+ assert(len > SIZE_IPPORT);
const uint8_t packet_id = plain[SIZE_IPPORT];
- if (packet_id != NET_PACKET_ANNOUNCE_REQUEST && packet_id != NET_PACKET_ONION_DATA_REQUEST) {
+ if (packet_id != NET_PACKET_ANNOUNCE_REQUEST_OLD && packet_id != NET_PACKET_ONION_DATA_REQUEST) {
return 1;
}
IP_Port send_to;
- if (ipport_unpack(&send_to, plain, len, 0) == -1) {
+ if (ipport_unpack(&send_to, plain, len, false) == -1) {
return 1;
}
@@ -485,7 +471,7 @@ static int handle_send_2(void *object, const IP_Port *source, const uint8_t *pac
memcpy(data, plain + SIZE_IPPORT, len - SIZE_IPPORT);
uint16_t data_len = len - SIZE_IPPORT;
uint8_t *ret_part = data + (len - SIZE_IPPORT);
- random_nonce(ret_part);
+ random_nonce(onion->rng, ret_part);
uint8_t ret_data[RETURN_2 + SIZE_IPPORT];
ipport_pack(ret_data, source);
memcpy(ret_data + SIZE_IPPORT, packet + (length - RETURN_2), RETURN_2);
@@ -506,6 +492,7 @@ static int handle_send_2(void *object, const IP_Port *source, const uint8_t *pac
}
+non_null()
static int handle_recv_3(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata)
{
Onion *onion = (Onion *)object;
@@ -520,14 +507,14 @@ static int handle_recv_3(void *object, const IP_Port *source, const uint8_t *pac
const uint8_t packet_id = packet[1 + RETURN_3];
- if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE && packet_id != NET_PACKET_ONION_DATA_RESPONSE) {
+ if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE_OLD && packet_id != NET_PACKET_ONION_DATA_RESPONSE) {
return 1;
}
change_symmetric_key(onion);
uint8_t plain[SIZE_IPPORT + RETURN_2];
- int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE,
+ const int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE,
SIZE_IPPORT + RETURN_2 + CRYPTO_MAC_SIZE, plain);
if ((uint32_t)len != sizeof(plain)) {
@@ -536,7 +523,7 @@ static int handle_recv_3(void *object, const IP_Port *source, const uint8_t *pac
IP_Port send_to;
- if (ipport_unpack(&send_to, plain, len, 0) == -1) {
+ if (ipport_unpack(&send_to, plain, len, false) == -1) {
return 1;
}
@@ -544,7 +531,7 @@ static int handle_recv_3(void *object, const IP_Port *source, const uint8_t *pac
data[0] = NET_PACKET_ONION_RECV_2;
memcpy(data + 1, plain + SIZE_IPPORT, RETURN_2);
memcpy(data + 1 + RETURN_2, packet + 1 + RETURN_3, length - (1 + RETURN_3));
- uint16_t data_len = 1 + RETURN_2 + (length - (1 + RETURN_3));
+ const uint16_t data_len = 1 + RETURN_2 + (length - (1 + RETURN_3));
if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) {
return 1;
@@ -553,6 +540,7 @@ static int handle_recv_3(void *object, const IP_Port *source, const uint8_t *pac
return 0;
}
+non_null()
static int handle_recv_2(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata)
{
Onion *onion = (Onion *)object;
@@ -567,14 +555,14 @@ static int handle_recv_2(void *object, const IP_Port *source, const uint8_t *pac
const uint8_t packet_id = packet[1 + RETURN_2];
- if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE && packet_id != NET_PACKET_ONION_DATA_RESPONSE) {
+ if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE_OLD && packet_id != NET_PACKET_ONION_DATA_RESPONSE) {
return 1;
}
change_symmetric_key(onion);
uint8_t plain[SIZE_IPPORT + RETURN_1];
- int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE,
+ const int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE,
SIZE_IPPORT + RETURN_1 + CRYPTO_MAC_SIZE, plain);
if ((uint32_t)len != sizeof(plain)) {
@@ -583,7 +571,7 @@ static int handle_recv_2(void *object, const IP_Port *source, const uint8_t *pac
IP_Port send_to;
- if (ipport_unpack(&send_to, plain, len, 0) == -1) {
+ if (ipport_unpack(&send_to, plain, len, false) == -1) {
return 1;
}
@@ -591,7 +579,7 @@ static int handle_recv_2(void *object, const IP_Port *source, const uint8_t *pac
data[0] = NET_PACKET_ONION_RECV_1;
memcpy(data + 1, plain + SIZE_IPPORT, RETURN_1);
memcpy(data + 1 + RETURN_1, packet + 1 + RETURN_2, length - (1 + RETURN_2));
- uint16_t data_len = 1 + RETURN_1 + (length - (1 + RETURN_2));
+ const uint16_t data_len = 1 + RETURN_1 + (length - (1 + RETURN_2));
if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) {
return 1;
@@ -600,6 +588,7 @@ static int handle_recv_2(void *object, const IP_Port *source, const uint8_t *pac
return 0;
}
+non_null()
static int handle_recv_1(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata)
{
Onion *onion = (Onion *)object;
@@ -614,14 +603,14 @@ static int handle_recv_1(void *object, const IP_Port *source, const uint8_t *pac
const uint8_t packet_id = packet[1 + RETURN_1];
- if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE && packet_id != NET_PACKET_ONION_DATA_RESPONSE) {
+ if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE_OLD && packet_id != NET_PACKET_ONION_DATA_RESPONSE) {
return 1;
}
change_symmetric_key(onion);
uint8_t plain[SIZE_IPPORT];
- int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE,
+ const int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE,
SIZE_IPPORT + CRYPTO_MAC_SIZE, plain);
if ((uint32_t)len != SIZE_IPPORT) {
@@ -630,13 +619,13 @@ static int handle_recv_1(void *object, const IP_Port *source, const uint8_t *pac
IP_Port send_to;
- if (ipport_unpack(&send_to, plain, len, 1) == -1) {
+ if (ipport_unpack(&send_to, plain, len, true) == -1) {
return 1;
}
- uint16_t data_len = length - (1 + RETURN_1);
+ const uint16_t data_len = length - (1 + RETURN_1);
- if (onion->recv_1_function &&
+ if (onion->recv_1_function != nullptr &&
!net_family_is_ipv4(send_to.ip.family) &&
!net_family_is_ipv6(send_to.ip.family)) {
return onion->recv_1_function(onion->callback_object, &send_to, packet + (1 + RETURN_1), data_len);
@@ -655,7 +644,7 @@ void set_callback_handle_recv_1(Onion *onion, onion_recv_1_cb *function, void *o
onion->callback_object = object;
}
-Onion *new_onion(const Logger *log, Mono_Time *mono_time, DHT *dht)
+Onion *new_onion(const Logger *log, const Mono_Time *mono_time, const Random *rng, DHT *dht)
{
if (dht == nullptr) {
return nullptr;
@@ -671,7 +660,8 @@ Onion *new_onion(const Logger *log, Mono_Time *mono_time, DHT *dht)
onion->dht = dht;
onion->net = dht_get_net(dht);
onion->mono_time = mono_time;
- new_symmetric_key(onion->secret_symmetric_key);
+ onion->rng = rng;
+ new_symmetric_key(rng, onion->secret_symmetric_key);
onion->timestamp = mono_time_get(onion->mono_time);
networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_INITIAL, &handle_send_initial, onion);
diff --git a/protocols/Tox/libtox/src/toxcore/onion.h b/protocols/Tox/libtox/src/toxcore/onion.h
index 970ef5b139..3da2137c44 100644
--- a/protocols/Tox/libtox/src/toxcore/onion.h
+++ b/protocols/Tox/libtox/src/toxcore/onion.h
@@ -17,7 +17,8 @@ typedef int onion_recv_1_cb(void *object, const IP_Port *dest, const uint8_t *da
typedef struct Onion {
const Logger *log;
- Mono_Time *mono_time;
+ const Mono_Time *mono_time;
+ const Random *rng;
DHT *dht;
Networking_Core *net;
uint8_t secret_symmetric_key[CRYPTO_SYMMETRIC_KEY_SIZE];
@@ -68,7 +69,7 @@ typedef struct Onion_Path {
uint32_t path_num;
} Onion_Path;
-/** Create a new onion path.
+/** @brief Create a new onion path.
*
* Create a new onion path out of nodes (nodes is a list of ONION_PATH_LENGTH nodes)
*
@@ -77,16 +78,18 @@ typedef struct Onion_Path {
* return -1 on failure.
* return 0 on success.
*/
-int create_onion_path(const DHT *dht, Onion_Path *new_path, const Node_format *nodes);
+non_null()
+int create_onion_path(const Random *rng, const DHT *dht, Onion_Path *new_path, const Node_format *nodes);
-/** Dump nodes in onion path to nodes of length num_nodes.
+/** @brief Dump nodes in onion path to nodes of length num_nodes.
*
* return -1 on failure.
* return 0 on success.
*/
+non_null()
int onion_path_to_nodes(Node_format *nodes, unsigned int num_nodes, const Onion_Path *path);
-/** Create a onion packet.
+/** @brief Create a onion packet.
*
* Use Onion_Path path to create packet for data of length to dest.
* Maximum length of data is ONION_MAX_DATA_SIZE.
@@ -95,11 +98,13 @@ int onion_path_to_nodes(Node_format *nodes, unsigned int num_nodes, const Onion_
* return -1 on failure.
* return length of created packet on success.
*/
-int create_onion_packet(uint8_t *packet, uint16_t max_packet_length, const Onion_Path *path, const IP_Port *dest,
+non_null()
+int create_onion_packet(const Random *rng, uint8_t *packet, uint16_t max_packet_length,
+ const Onion_Path *path, const IP_Port *dest,
const uint8_t *data, uint16_t length);
-/** Create a onion packet to be sent over tcp.
+/** @brief Create a onion packet to be sent over tcp.
*
* Use Onion_Path path to create packet for data of length to dest.
* Maximum length of data is ONION_MAX_DATA_SIZE.
@@ -108,30 +113,22 @@ int create_onion_packet(uint8_t *packet, uint16_t max_packet_length, const Onion
* return -1 on failure.
* return length of created packet on success.
*/
-int create_onion_packet_tcp(uint8_t *packet, uint16_t max_packet_length, const Onion_Path *path, const IP_Port *dest,
+non_null()
+int create_onion_packet_tcp(const Random *rng, uint8_t *packet, uint16_t max_packet_length,
+ const Onion_Path *path, const IP_Port *dest,
const uint8_t *data, uint16_t length);
-/** Create and send a onion packet.
- *
- * Use Onion_Path path to send data of length to dest.
- * Maximum length of data is ONION_MAX_DATA_SIZE.
- *
- * return -1 on failure.
- * return 0 on success.
- */
-int send_onion_packet(const Networking_Core *net, const Onion_Path *path, const IP_Port *dest, const uint8_t *data,
- uint16_t length);
-
-/** Create and send a onion response sent initially to dest with.
+/** @brief Create and send a onion response sent initially to dest with.
* Maximum length of data is ONION_RESPONSE_MAX_DATA_SIZE.
*
* return -1 on failure.
* return 0 on success.
*/
+non_null()
int send_onion_response(const Networking_Core *net, const IP_Port *dest, const uint8_t *data, uint16_t length,
const uint8_t *ret);
-/** Function to handle/send received decrypted versions of the packet sent with send_onion_packet.
+/** @brief Function to handle/send received decrypted versions of the packet created by create_onion_packet.
*
* return 0 on success.
* return 1 on failure.
@@ -141,14 +138,17 @@ int send_onion_response(const Networking_Core *net, const IP_Port *dest, const u
* Source family must be set to something else than TOX_AF_INET6 or TOX_AF_INET so that the callback gets called
* when the response is received.
*/
+non_null()
int onion_send_1(const Onion *onion, const uint8_t *plain, uint16_t len, const IP_Port *source, const uint8_t *nonce);
-/** Set the callback to be called when the dest ip_port doesn't have TOX_AF_INET6 or TOX_AF_INET as the family.
- */
+/** Set the callback to be called when the dest ip_port doesn't have TOX_AF_INET6 or TOX_AF_INET as the family. */
+non_null(1) nullable(2, 3)
void set_callback_handle_recv_1(Onion *onion, onion_recv_1_cb *function, void *object);
-Onion *new_onion(const Logger *log, Mono_Time *mono_time, DHT *dht);
+non_null()
+Onion *new_onion(const Logger *log, const Mono_Time *mono_time, const Random *rng, DHT *dht);
+nullable(1)
void kill_onion(Onion *onion);
diff --git a/protocols/Tox/libtox/src/toxcore/onion_announce.c b/protocols/Tox/libtox/src/toxcore/onion_announce.c
index 5c37fbe18c..f12e560837 100644
--- a/protocols/Tox/libtox/src/toxcore/onion_announce.c
+++ b/protocols/Tox/libtox/src/toxcore/onion_announce.c
@@ -8,20 +8,29 @@
*/
#include "onion_announce.h"
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
+#include "DHT.h"
#include "LAN_discovery.h"
+#include "ccompat.h"
#include "mono_time.h"
#include "util.h"
#define PING_ID_TIMEOUT ONION_ANNOUNCE_TIMEOUT
+#define ANNOUNCE_REQUEST_MIN_SIZE_RECV (ONION_ANNOUNCE_REQUEST_MIN_SIZE + ONION_RETURN_3)
+#define ANNOUNCE_REQUEST_MAX_SIZE_RECV (ONION_ANNOUNCE_REQUEST_MAX_SIZE + ONION_RETURN_3)
+
+/* TODO(Jfreegman): DEPRECATE */
#define ANNOUNCE_REQUEST_SIZE_RECV (ONION_ANNOUNCE_REQUEST_SIZE + ONION_RETURN_3)
#define DATA_REQUEST_MIN_SIZE ONION_DATA_REQUEST_MIN_SIZE
#define DATA_REQUEST_MIN_SIZE_RECV (DATA_REQUEST_MIN_SIZE + ONION_RETURN_3)
+#define ONION_MINIMAL_SIZE (ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE * 2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH)
+
static_assert(ONION_PING_ID_SIZE == CRYPTO_PUBLIC_KEY_SIZE,
"announce response packets assume that ONION_PING_ID_SIZE is equal to CRYPTO_PUBLIC_KEY_SIZE");
@@ -30,24 +39,31 @@ typedef struct Onion_Announce_Entry {
IP_Port ret_ip_port;
uint8_t ret[ONION_RETURN_3];
uint8_t data_public_key[CRYPTO_PUBLIC_KEY_SIZE];
- uint64_t time;
+ uint64_t announce_time;
} Onion_Announce_Entry;
struct Onion_Announce {
const Logger *log;
- Mono_Time *mono_time;
+ const Mono_Time *mono_time;
+ const Random *rng;
DHT *dht;
Networking_Core *net;
Onion_Announce_Entry entries[ONION_ANNOUNCE_MAX_ENTRIES];
- /* This is CRYPTO_SYMMETRIC_KEY_SIZE long just so we can use new_symmetric_key() to fill it */
- uint8_t secret_bytes[CRYPTO_SYMMETRIC_KEY_SIZE];
+ uint8_t hmac_key[CRYPTO_HMAC_KEY_SIZE];
Shared_Keys shared_keys_recv;
+
+ uint16_t extra_data_max_size;
+ pack_extra_data_cb *extra_data_callback;
+ void *extra_data_object;
};
-static bool onion_ping_id_eq(const uint8_t *a, const uint8_t *b)
+void onion_announce_extra_data_callback(Onion_Announce *onion_a, uint16_t extra_data_max_size,
+ pack_extra_data_cb *extra_data_callback, void *extra_data_object)
{
- return public_key_cmp(a, b) == 0;
+ onion_a->extra_data_max_size = extra_data_max_size;
+ onion_a->extra_data_callback = extra_data_callback;
+ onion_a->extra_data_object = extra_data_object;
}
uint8_t *onion_announce_entry_public_key(Onion_Announce *onion_a, uint32_t entry)
@@ -55,12 +71,14 @@ uint8_t *onion_announce_entry_public_key(Onion_Announce *onion_a, uint32_t entry
return onion_a->entries[entry].public_key;
}
-void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint64_t time)
+void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint64_t announce_time)
{
- onion_a->entries[entry].time = time;
+ onion_a->entries[entry].announce_time = announce_time;
}
-/** Create an onion announce request packet in packet of max_packet_length (recommended size ONION_ANNOUNCE_REQUEST_SIZE).
+/** @brief Create an onion announce request packet in packet of max_packet_length.
+ *
+ * Recommended value for max_packet_length is ONION_ANNOUNCE_REQUEST_MIN_SIZE.
*
* dest_client_id is the public key of the node the packet will be sent to.
* public_key and secret_key is the kepair which will be used to encrypt the request.
@@ -73,11 +91,11 @@ void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint
* return -1 on failure.
* return packet length on success.
*/
-int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const uint8_t *dest_client_id,
+int create_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)
{
- if (max_packet_length < ONION_ANNOUNCE_REQUEST_SIZE) {
+ if (max_packet_length < ONION_ANNOUNCE_REQUEST_MIN_SIZE) {
return -1;
}
@@ -89,22 +107,24 @@ int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const u
memcpy(plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE, &sendback_data,
sizeof(sendback_data));
- packet[0] = NET_PACKET_ANNOUNCE_REQUEST;
- random_nonce(packet + 1);
+ packet[0] = NET_PACKET_ANNOUNCE_REQUEST_OLD;
+ random_nonce(rng, packet + 1);
- int len = encrypt_data(dest_client_id, secret_key, packet + 1, plain, sizeof(plain),
- packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE);
+ const int len = encrypt_data(dest_client_id, secret_key, packet + 1, plain, sizeof(plain),
+ packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE);
- if ((uint32_t)len + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE != ONION_ANNOUNCE_REQUEST_SIZE) {
+ if ((uint32_t)len + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE != ONION_ANNOUNCE_REQUEST_MIN_SIZE) {
return -1;
}
memcpy(packet + 1 + CRYPTO_NONCE_SIZE, public_key, CRYPTO_PUBLIC_KEY_SIZE);
- return ONION_ANNOUNCE_REQUEST_SIZE;
+ return ONION_ANNOUNCE_REQUEST_MIN_SIZE;
}
-/** Create an onion data request packet in packet of max_packet_length (recommended size ONION_MAX_PACKET_SIZE).
+/** @brief Create an onion data request packet in packet of max_packet_length.
+ *
+ * Recommended value for max_packet_length is ONION_ANNOUNCE_REQUEST_SIZE.
*
* public_key is the real public key of the node which we want to send the data of length length to.
* encrypt_public_key is the public key used to encrypt the data packet.
@@ -114,7 +134,7 @@ int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const u
* return -1 on failure.
* return 0 on success.
*/
-int create_data_request(uint8_t *packet, uint16_t max_packet_length, const uint8_t *public_key,
+int create_data_request(const Random *rng, uint8_t *packet, uint16_t max_packet_length, const uint8_t *public_key,
const uint8_t *encrypt_public_key, const uint8_t *nonce, const uint8_t *data, uint16_t length)
{
if (DATA_REQUEST_MIN_SIZE + length > max_packet_length) {
@@ -131,12 +151,12 @@ int create_data_request(uint8_t *packet, uint16_t max_packet_length, const uint8
uint8_t random_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t random_secret_key[CRYPTO_SECRET_KEY_SIZE];
- crypto_new_keypair(random_public_key, random_secret_key);
+ crypto_new_keypair(rng, random_public_key, random_secret_key);
memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, random_public_key, CRYPTO_PUBLIC_KEY_SIZE);
- int len = encrypt_data(encrypt_public_key, random_secret_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, data, length,
- packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE);
+ const int len = encrypt_data(encrypt_public_key, random_secret_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, data, length,
+ packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE);
if (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + len != DATA_REQUEST_MIN_SIZE +
length) {
@@ -146,7 +166,7 @@ int create_data_request(uint8_t *packet, uint16_t max_packet_length, const uint8
return DATA_REQUEST_MIN_SIZE + length;
}
-/** Create and send an onion announce request packet.
+/** @brief Create and send an onion announce request packet.
*
* path is the path the request will take before it is sent to dest.
*
@@ -160,21 +180,22 @@ int create_data_request(uint8_t *packet, uint16_t max_packet_length, const uint8
* return -1 on failure.
* return 0 on success.
*/
-int send_announce_request(const Networking_Core *net, const Onion_Path *path, Node_format dest,
+int send_announce_request(const Networking_Core *net, const Random *rng,
+ const Onion_Path *path, const Node_format *dest,
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)
{
- uint8_t request[ONION_ANNOUNCE_REQUEST_SIZE];
- int len = create_announce_request(request, sizeof(request), dest.public_key, public_key, secret_key, ping_id, client_id,
- data_public_key, sendback_data);
+ uint8_t request[ONION_ANNOUNCE_REQUEST_MIN_SIZE];
+ int len = create_announce_request(rng, request, sizeof(request), dest->public_key, public_key, secret_key, ping_id,
+ client_id, data_public_key, sendback_data);
if (len != sizeof(request)) {
return -1;
}
uint8_t packet[ONION_MAX_PACKET_SIZE];
- len = create_onion_packet(packet, sizeof(packet), path, &dest.ip_port, request, sizeof(request));
+ len = create_onion_packet(rng, packet, sizeof(packet), path, &dest->ip_port, request, sizeof(request));
if (len == -1) {
return -1;
@@ -187,7 +208,7 @@ int send_announce_request(const Networking_Core *net, const Onion_Path *path, No
return 0;
}
-/** Create and send an onion data request packet.
+/** @brief Create and send an onion data request packet.
*
* path is the path the request will take before it is sent to dest.
* (if dest knows the person with the public_key they should
@@ -203,19 +224,19 @@ int send_announce_request(const Networking_Core *net, const Onion_Path *path, No
* return -1 on failure.
* return 0 on success.
*/
-int send_data_request(const Networking_Core *net, const Onion_Path *path, const IP_Port *dest,
- const uint8_t *public_key,
- const uint8_t *encrypt_public_key, const uint8_t *nonce, const uint8_t *data, uint16_t length)
+int send_data_request(const Networking_Core *net, const Random *rng, const Onion_Path *path, const IP_Port *dest,
+ const uint8_t *public_key, const uint8_t *encrypt_public_key, const uint8_t *nonce,
+ const uint8_t *data, uint16_t length)
{
uint8_t request[ONION_MAX_DATA_SIZE];
- int len = create_data_request(request, sizeof(request), public_key, encrypt_public_key, nonce, data, length);
+ int len = create_data_request(rng, request, sizeof(request), public_key, encrypt_public_key, nonce, data, length);
if (len == -1) {
return -1;
}
uint8_t packet[ONION_MAX_PACKET_SIZE];
- len = create_onion_packet(packet, sizeof(packet), path, dest, request, len);
+ len = create_onion_packet(rng, packet, sizeof(packet), path, dest, request, len);
if (len == -1) {
return -1;
@@ -228,29 +249,17 @@ int send_data_request(const Networking_Core *net, const Onion_Path *path, const
return 0;
}
-/** Generate a ping_id and put it in ping_id */
-static void generate_ping_id(const Onion_Announce *onion_a, uint64_t time, const uint8_t *public_key,
- const IP_Port *ret_ip_port, uint8_t *ping_id)
-{
- time /= PING_ID_TIMEOUT;
- uint8_t data[CRYPTO_SYMMETRIC_KEY_SIZE + sizeof(time) + CRYPTO_PUBLIC_KEY_SIZE + sizeof(IP_Port)];
- memcpy(data, onion_a->secret_bytes, CRYPTO_SYMMETRIC_KEY_SIZE);
- memcpy(data + CRYPTO_SYMMETRIC_KEY_SIZE, &time, sizeof(time));
- memcpy(data + CRYPTO_SYMMETRIC_KEY_SIZE + sizeof(time), public_key, CRYPTO_PUBLIC_KEY_SIZE);
- memcpy(data + CRYPTO_SYMMETRIC_KEY_SIZE + sizeof(time) + CRYPTO_PUBLIC_KEY_SIZE, ret_ip_port, sizeof(IP_Port));
- crypto_sha256(ping_id, data, sizeof(data));
-}
-
-/** check if public key is in entries list
+/** @brief check if public key is in entries list
*
* return -1 if no
* return position in list if yes
*/
+non_null()
static int in_entries(const Onion_Announce *onion_a, const uint8_t *public_key)
{
for (unsigned int i = 0; i < ONION_ANNOUNCE_MAX_ENTRIES; ++i) {
- if (!mono_time_is_timeout(onion_a->mono_time, onion_a->entries[i].time, ONION_ANNOUNCE_TIMEOUT)
- && public_key_cmp(onion_a->entries[i].public_key, public_key) == 0) {
+ if (!mono_time_is_timeout(onion_a->mono_time, onion_a->entries[i].announce_time, ONION_ANNOUNCE_TIMEOUT)
+ && pk_equal(onion_a->entries[i].public_key, public_key)) {
return i;
}
}
@@ -264,6 +273,7 @@ typedef struct Cmp_Data {
Onion_Announce_Entry entry;
} Cmp_Data;
+non_null()
static int cmp_entry(const void *a, const void *b)
{
const Cmp_Data *cmp1 = (const Cmp_Data *)a;
@@ -272,8 +282,8 @@ static int cmp_entry(const void *a, const void *b)
const Onion_Announce_Entry entry2 = cmp2->entry;
const uint8_t *cmp_public_key = cmp1->base_public_key;
- const int t1 = mono_time_is_timeout(cmp1->mono_time, entry1.time, ONION_ANNOUNCE_TIMEOUT);
- const int t2 = mono_time_is_timeout(cmp1->mono_time, entry2.time, ONION_ANNOUNCE_TIMEOUT);
+ const bool t1 = mono_time_is_timeout(cmp1->mono_time, entry1.announce_time, ONION_ANNOUNCE_TIMEOUT);
+ const bool t2 = mono_time_is_timeout(cmp1->mono_time, entry2.announce_time, ONION_ANNOUNCE_TIMEOUT);
if (t1 && t2) {
return 0;
@@ -287,25 +297,30 @@ static int cmp_entry(const void *a, const void *b)
return 1;
}
- const int close = id_closest(cmp_public_key, entry1.public_key, entry2.public_key);
+ const int closest = id_closest(cmp_public_key, entry1.public_key, entry2.public_key);
- if (close == 1) {
+ if (closest == 1) {
return 1;
}
- if (close == 2) {
+ if (closest == 2) {
return -1;
}
return 0;
}
+non_null()
static void sort_onion_announce_list(Onion_Announce_Entry *list, unsigned int length, const Mono_Time *mono_time,
const uint8_t *comp_public_key)
{
// Pass comp_public_key to qsort with each Client_data entry, so the
// comparison function can use it as the base of comparison.
- VLA(Cmp_Data, cmp_list, length);
+ Cmp_Data *cmp_list = (Cmp_Data *)calloc(length, sizeof(Cmp_Data));
+
+ if (cmp_list == nullptr) {
+ return;
+ }
for (uint32_t i = 0; i < length; ++i) {
cmp_list[i].mono_time = mono_time;
@@ -318,13 +333,16 @@ static void sort_onion_announce_list(Onion_Announce_Entry *list, unsigned int le
for (uint32_t i = 0; i < length; ++i) {
list[i] = cmp_list[i].entry;
}
+
+ free(cmp_list);
}
-/** add entry to entries list
+/** @brief add entry to entries list
*
* return -1 if failure
* return position if added
*/
+non_null()
static int add_to_entries(Onion_Announce *onion_a, const IP_Port *ret_ip_port, const uint8_t *public_key,
const uint8_t *data_public_key, const uint8_t *ret)
{
@@ -333,7 +351,7 @@ static int add_to_entries(Onion_Announce *onion_a, const IP_Port *ret_ip_port, c
if (pos == -1) {
for (unsigned i = 0; i < ONION_ANNOUNCE_MAX_ENTRIES; ++i) {
- if (mono_time_is_timeout(onion_a->mono_time, onion_a->entries[i].time, ONION_ANNOUNCE_TIMEOUT)) {
+ if (mono_time_is_timeout(onion_a->mono_time, onion_a->entries[i].announce_time, ONION_ANNOUNCE_TIMEOUT)) {
pos = i;
}
}
@@ -353,114 +371,236 @@ static int add_to_entries(Onion_Announce *onion_a, const IP_Port *ret_ip_port, c
onion_a->entries[pos].ret_ip_port = *ret_ip_port;
memcpy(onion_a->entries[pos].ret, ret, ONION_RETURN_3);
memcpy(onion_a->entries[pos].data_public_key, data_public_key, CRYPTO_PUBLIC_KEY_SIZE);
- onion_a->entries[pos].time = mono_time_get(onion_a->mono_time);
+ onion_a->entries[pos].announce_time = mono_time_get(onion_a->mono_time);
sort_onion_announce_list(onion_a->entries, ONION_ANNOUNCE_MAX_ENTRIES, onion_a->mono_time,
dht_get_self_public_key(onion_a->dht));
return in_entries(onion_a, public_key);
}
-static int handle_announce_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
- void *userdata)
+non_null()
+static void make_announce_payload_helper(const Onion_Announce *onion_a, const uint8_t *ping_id,
+ uint8_t *response, int index, const uint8_t *packet_public_key, const uint8_t *data_public_key)
{
- Onion_Announce *onion_a = (Onion_Announce *)object;
+ if (index < 0) {
+ response[0] = 0;
+ memcpy(response + 1, ping_id, ONION_PING_ID_SIZE);
+ return;
+ }
- if (length != ANNOUNCE_REQUEST_SIZE_RECV) {
- return 1;
+ if (pk_equal(onion_a->entries[index].public_key, packet_public_key)) {
+ if (!pk_equal(onion_a->entries[index].data_public_key, data_public_key)) {
+ response[0] = 0;
+ memcpy(response + 1, ping_id, ONION_PING_ID_SIZE);
+ } else {
+ response[0] = 2;
+ memcpy(response + 1, ping_id, ONION_PING_ID_SIZE);
+ }
+ } else {
+ response[0] = 1;
+ memcpy(response + 1, onion_a->entries[index].data_public_key, CRYPTO_PUBLIC_KEY_SIZE);
}
+}
+/** @brief Handle an onion announce request, possibly with extra data for group chats.
+ *
+ * @param onion_a The announce object.
+ * @param source Requester IP/Port.
+ * @param packet Encrypted incoming packet.
+ * @param length Length of incoming packet.
+ * @param response_packet_id Packet ID to use for the onion announce response.
+ * @param plain_size Expected size of the decrypted packet. This function returns an error if the
+ * actual decrypted size is not exactly equal to this number.
+ * @param want_node_count If true, the packed nodes in the response are preceded by the number of
+ * nodes sent in the packet. This is necessary if you want to send extra data after the nodes.
+ * @param max_extra_size Amount of memory to allocate in the outgoing packet to be filled by the
+ * extra data callback.
+ * @param pack_extra_data_callback Callback that may write extra data into the packet.
+ *
+ * @retval 1 on failure.
+ * @retval 0 on success.
+ */
+non_null(1, 2, 3) nullable(9)
+static int handle_announce_request_common(
+ Onion_Announce *onion_a, const IP_Port *source, const uint8_t *packet, uint16_t length,
+ uint8_t response_packet_id, uint16_t plain_size, bool want_node_count, uint16_t max_extra_size,
+ pack_extra_data_cb *pack_extra_data_callback)
+{
const uint8_t *packet_public_key = packet + 1 + CRYPTO_NONCE_SIZE;
uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE];
get_shared_key(onion_a->mono_time, &onion_a->shared_keys_recv, shared_key, dht_get_self_secret_key(onion_a->dht),
packet_public_key);
- uint8_t plain[ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE +
- ONION_ANNOUNCE_SENDBACK_DATA_LENGTH];
- int len = decrypt_data_symmetric(shared_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE,
- ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH +
- CRYPTO_MAC_SIZE, plain);
+ uint8_t *plain = (uint8_t *)malloc(plain_size);
- if ((uint32_t)len != sizeof(plain)) {
+ if (plain == nullptr) {
return 1;
}
- uint8_t ping_id1[ONION_PING_ID_SIZE];
- generate_ping_id(onion_a, mono_time_get(onion_a->mono_time), packet_public_key, source, ping_id1);
+ const int decrypted_len = decrypt_data_symmetric(shared_key, packet + 1,
+ packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, plain_size + CRYPTO_MAC_SIZE, plain);
- uint8_t ping_id2[ONION_PING_ID_SIZE];
- generate_ping_id(onion_a, mono_time_get(onion_a->mono_time) + PING_ID_TIMEOUT, packet_public_key, source, ping_id2);
+ if ((uint32_t)decrypted_len != plain_size) {
+ free(plain);
+ return 1;
+ }
- int index;
+ const uint16_t ping_id_data_len = CRYPTO_PUBLIC_KEY_SIZE + sizeof(*source);
+ uint8_t ping_id_data[CRYPTO_PUBLIC_KEY_SIZE + sizeof(*source)];
+ memcpy(ping_id_data, packet_public_key, CRYPTO_PUBLIC_KEY_SIZE);
+ memcpy(ping_id_data + CRYPTO_PUBLIC_KEY_SIZE, source, sizeof(*source));
const uint8_t *data_public_key = plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE;
- if (onion_ping_id_eq(ping_id1, plain)
- || onion_ping_id_eq(ping_id2, plain)) {
+ int index;
+
+ if (check_timed_auth(onion_a->mono_time, PING_ID_TIMEOUT, onion_a->hmac_key,
+ ping_id_data, ping_id_data_len, plain)) {
index = add_to_entries(onion_a, source, packet_public_key, data_public_key,
- packet + (ANNOUNCE_REQUEST_SIZE_RECV - ONION_RETURN_3));
+ packet + (length - ONION_RETURN_3));
} else {
index = in_entries(onion_a, plain + ONION_PING_ID_SIZE);
}
- /*Respond with a announce response packet*/
+ /* Respond with a announce response packet */
Node_format nodes_list[MAX_SENT_NODES];
- unsigned int num_nodes =
- get_close_nodes(onion_a->dht, plain + ONION_PING_ID_SIZE, nodes_list, net_family_unspec, ip_is_lan(&source->ip));
+ const unsigned int num_nodes =
+ get_close_nodes(onion_a->dht, plain + ONION_PING_ID_SIZE, nodes_list, net_family_unspec(), ip_is_lan(&source->ip), false);
+
+ assert(num_nodes <= UINT8_MAX);
+
uint8_t nonce[CRYPTO_NONCE_SIZE];
- random_nonce(nonce);
+ random_nonce(onion_a->rng, nonce);
- uint8_t pl[1 + ONION_PING_ID_SIZE + sizeof(nodes_list)];
+ const uint16_t nodes_offset = 1 + ONION_PING_ID_SIZE + (want_node_count ? 1 : 0);
+ const uint16_t response_size = nodes_offset
+ + MAX_SENT_NODES * PACKED_NODE_SIZE_IP6
+ + max_extra_size;
+ uint8_t *response = (uint8_t *)malloc(response_size);
- if (index == -1) {
- pl[0] = 0;
- memcpy(pl + 1, ping_id2, ONION_PING_ID_SIZE);
- } else {
- if (public_key_cmp(onion_a->entries[index].public_key, packet_public_key) == 0) {
- if (public_key_cmp(onion_a->entries[index].data_public_key, data_public_key) != 0) {
- pl[0] = 0;
- memcpy(pl + 1, ping_id2, ONION_PING_ID_SIZE);
- } else {
- pl[0] = 2;
- memcpy(pl + 1, ping_id2, ONION_PING_ID_SIZE);
- }
- } else {
- pl[0] = 1;
- memcpy(pl + 1, onion_a->entries[index].data_public_key, CRYPTO_PUBLIC_KEY_SIZE);
- }
+ if (response == nullptr) {
+ free(plain);
+ return 1;
}
+ uint8_t ping_id[TIMED_AUTH_SIZE];
+ generate_timed_auth(onion_a->mono_time, PING_ID_TIMEOUT, onion_a->hmac_key,
+ ping_id_data, ping_id_data_len, ping_id);
+
+ make_announce_payload_helper(onion_a, ping_id, response, index, packet_public_key, data_public_key);
+
int nodes_length = 0;
if (num_nodes != 0) {
- nodes_length = pack_nodes(pl + 1 + ONION_PING_ID_SIZE, sizeof(nodes_list), nodes_list, num_nodes);
+ nodes_length = pack_nodes(onion_a->log, response + nodes_offset, sizeof(nodes_list), nodes_list,
+ (uint16_t)num_nodes);
if (nodes_length <= 0) {
+ LOGGER_WARNING(onion_a->log, "Failed to pack nodes");
+ free(response);
+ free(plain);
return 1;
}
}
+ uint16_t offset = nodes_offset + nodes_length;
+
+ if (want_node_count) {
+ response[1 + ONION_PING_ID_SIZE] = (uint8_t)num_nodes;
+ }
+
+ const int extra_size = pack_extra_data_callback == nullptr ? 0
+ : pack_extra_data_callback(onion_a->extra_data_object,
+ onion_a->log, onion_a->mono_time, num_nodes,
+ plain + ONION_MINIMAL_SIZE, length - ANNOUNCE_REQUEST_MIN_SIZE_RECV,
+ response, response_size, offset);
+
+ if (extra_size == -1) {
+ free(response);
+ free(plain);
+ return 1;
+ }
+
+ offset += extra_size;
+
uint8_t data[ONION_ANNOUNCE_RESPONSE_MAX_SIZE];
- len = encrypt_data_symmetric(shared_key, nonce, pl, 1 + ONION_PING_ID_SIZE + nodes_length,
- data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE);
+ const int len = encrypt_data_symmetric(shared_key, nonce, response, offset,
+ data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE);
- if (len != 1 + ONION_PING_ID_SIZE + nodes_length + CRYPTO_MAC_SIZE) {
+ if (len != offset + CRYPTO_MAC_SIZE) {
+ LOGGER_ERROR(onion_a->log, "Failed to encrypt announce response");
+ free(response);
+ free(plain);
return 1;
}
- data[0] = NET_PACKET_ANNOUNCE_RESPONSE;
+ data[0] = response_packet_id;
memcpy(data + 1, plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE,
ONION_ANNOUNCE_SENDBACK_DATA_LENGTH);
memcpy(data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH, nonce, CRYPTO_NONCE_SIZE);
if (send_onion_response(onion_a->net, source, data,
1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE + len,
- packet + (ANNOUNCE_REQUEST_SIZE_RECV - ONION_RETURN_3)) == -1) {
+ packet + (length - ONION_RETURN_3)) == -1) {
+ free(response);
+ free(plain);
return 1;
}
+ free(response);
+ free(plain);
return 0;
}
+non_null()
+static int handle_gca_announce_request(Onion_Announce *onion_a, const IP_Port *source, const uint8_t *packet,
+ uint16_t length)
+{
+ if (length > ANNOUNCE_REQUEST_MAX_SIZE_RECV || length <= ANNOUNCE_REQUEST_MIN_SIZE_RECV) {
+ return 1;
+ }
+
+ if (onion_a->extra_data_callback == nullptr) {
+ return 1;
+ }
+
+ return handle_announce_request_common(onion_a, source, packet, length, NET_PACKET_ANNOUNCE_RESPONSE,
+ ONION_MINIMAL_SIZE + length - ANNOUNCE_REQUEST_MIN_SIZE_RECV,
+ true, onion_a->extra_data_max_size, onion_a->extra_data_callback);
+}
+
+non_null(1, 2, 3) nullable(5)
+static int handle_announce_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
+ void *userdata)
+{
+ Onion_Announce *onion_a = (Onion_Announce *)object;
+
+ if (length != ANNOUNCE_REQUEST_MIN_SIZE_RECV) {
+ return handle_gca_announce_request(onion_a, source, packet, length);
+ }
+
+ return handle_announce_request_common(onion_a, source, packet, length, NET_PACKET_ANNOUNCE_RESPONSE,
+ ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE * 2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH,
+ true, 0, nullptr);
+}
+
+/* TODO(Jfreegman): DEPRECATE */
+non_null(1, 2, 3) nullable(5)
+static int handle_announce_request_old(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
+ void *userdata)
+{
+ Onion_Announce *onion_a = (Onion_Announce *)object;
+
+ if (length != ANNOUNCE_REQUEST_SIZE_RECV) {
+ return 1;
+ }
+
+ return handle_announce_request_common(onion_a, source, packet, length, NET_PACKET_ANNOUNCE_RESPONSE_OLD,
+ ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE * 2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH,
+ false, 0, nullptr);
+}
+
+non_null()
static int handle_data_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
@@ -474,7 +614,7 @@ static int handle_data_request(void *object, const IP_Port *source, const uint8_
return 1;
}
- int index = in_entries(onion_a, packet + 1);
+ const int index = in_entries(onion_a, packet + 1);
if (index == -1) {
return 1;
@@ -492,7 +632,7 @@ static int handle_data_request(void *object, const IP_Port *source, const uint8_
return 0;
}
-Onion_Announce *new_onion_announce(const Logger *log, Mono_Time *mono_time, DHT *dht)
+Onion_Announce *new_onion_announce(const Logger *log, const Random *rng, const Mono_Time *mono_time, DHT *dht)
{
if (dht == nullptr) {
return nullptr;
@@ -505,14 +645,22 @@ Onion_Announce *new_onion_announce(const Logger *log, Mono_Time *mono_time, DHT
}
onion_a->log = log;
+ onion_a->rng = rng;
onion_a->mono_time = mono_time;
onion_a->dht = dht;
onion_a->net = dht_get_net(dht);
- new_symmetric_key(onion_a->secret_bytes);
+ onion_a->extra_data_max_size = 0;
+ onion_a->extra_data_callback = nullptr;
+ onion_a->extra_data_object = nullptr;
+ new_hmac_key(rng, onion_a->hmac_key);
networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST, &handle_announce_request, onion_a);
+ networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST_OLD, &handle_announce_request_old, onion_a);
networking_registerhandler(onion_a->net, NET_PACKET_ONION_DATA_REQUEST, &handle_data_request, onion_a);
+ // TODO(Jfreegman): Remove this when we merge the rest of new groupchats
+ onion_announce_extra_data_callback(onion_a, 0, nullptr, nullptr);
+
return onion_a;
}
@@ -523,6 +671,10 @@ void kill_onion_announce(Onion_Announce *onion_a)
}
networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST, nullptr, nullptr);
+ networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST_OLD, nullptr, nullptr);
networking_registerhandler(onion_a->net, NET_PACKET_ONION_DATA_REQUEST, nullptr, nullptr);
+
+ crypto_memzero(onion_a->hmac_key, CRYPTO_HMAC_KEY_SIZE);
+
free(onion_a);
}
diff --git a/protocols/Tox/libtox/src/toxcore/onion_announce.h b/protocols/Tox/libtox/src/toxcore/onion_announce.h
index c4159d2bf6..8cf1f49539 100644
--- a/protocols/Tox/libtox/src/toxcore/onion_announce.h
+++ b/protocols/Tox/libtox/src/toxcore/onion_announce.h
@@ -11,17 +11,23 @@
#include "logger.h"
#include "onion.h"
+#include "timed_auth.h"
#define ONION_ANNOUNCE_MAX_ENTRIES 160
#define ONION_ANNOUNCE_TIMEOUT 300
-#define ONION_PING_ID_SIZE CRYPTO_SHA256_SIZE
+#define ONION_PING_ID_SIZE TIMED_AUTH_SIZE
+#define ONION_MAX_EXTRA_DATA_SIZE 136
#define ONION_ANNOUNCE_SENDBACK_DATA_LENGTH (sizeof(uint64_t))
-#define ONION_ANNOUNCE_REQUEST_SIZE (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_MAC_SIZE)
+#define ONION_ANNOUNCE_REQUEST_MIN_SIZE (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_MAC_SIZE)
+#define ONION_ANNOUNCE_REQUEST_MAX_SIZE (ONION_ANNOUNCE_REQUEST_MIN_SIZE + ONION_MAX_EXTRA_DATA_SIZE)
+
+#define ONION_ANNOUNCE_RESPONSE_MIN_SIZE (2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE + ONION_PING_ID_SIZE + CRYPTO_MAC_SIZE)
+#define ONION_ANNOUNCE_RESPONSE_MAX_SIZE (ONION_ANNOUNCE_RESPONSE_MIN_SIZE + ONION_MAX_EXTRA_DATA_SIZE * MAX_SENT_NODES)
-#define ONION_ANNOUNCE_RESPONSE_MIN_SIZE (1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE + 1 + ONION_PING_ID_SIZE + CRYPTO_MAC_SIZE)
-#define ONION_ANNOUNCE_RESPONSE_MAX_SIZE (ONION_ANNOUNCE_RESPONSE_MIN_SIZE + sizeof(Node_format)*MAX_SENT_NODES)
+/* TODO: DEPRECATE */
+#define ONION_ANNOUNCE_REQUEST_SIZE (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_MAC_SIZE)
#define ONION_DATA_RESPONSE_MIN_SIZE (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE)
@@ -31,10 +37,14 @@
typedef struct Onion_Announce Onion_Announce;
/** These two are not public; they are for tests only! */
+non_null()
uint8_t *onion_announce_entry_public_key(Onion_Announce *onion_a, uint32_t entry);
-void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint64_t time);
+non_null()
+void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint64_t announce_time);
-/** Create an onion announce request packet in packet of max_packet_length (recommended size ONION_ANNOUNCE_REQUEST_SIZE).
+/** @brief Create an onion announce request packet in packet of max_packet_length.
+ *
+ * Recommended value for max_packet_length is ONION_ANNOUNCE_REQUEST_MIN_SIZE.
*
* dest_client_id is the public key of the node the packet will be sent to.
* public_key and secret_key is the kepair which will be used to encrypt the request.
@@ -47,11 +57,14 @@ void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint
* return -1 on failure.
* return packet length on success.
*/
-int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const uint8_t *dest_client_id,
+non_null()
+int create_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);
-/** Create an onion data request packet in packet of max_packet_length (recommended size ONION_MAX_PACKET_SIZE).
+/** @brief Create an onion data request packet in packet of max_packet_length.
+ *
+ * Recommended value for max_packet_length is ONION_ANNOUNCE_REQUEST_SIZE.
*
* public_key is the real public key of the node which we want to send the data of length length to.
* encrypt_public_key is the public key used to encrypt the data packet.
@@ -61,10 +74,11 @@ int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const u
* return -1 on failure.
* return 0 on success.
*/
-int create_data_request(uint8_t *packet, uint16_t max_packet_length, const uint8_t *public_key,
+non_null()
+int create_data_request(const Random *rng, uint8_t *packet, uint16_t max_packet_length, const uint8_t *public_key,
const uint8_t *encrypt_public_key, const uint8_t *nonce, const uint8_t *data, uint16_t length);
-/** Create and send an onion announce request packet.
+/** @brief Create and send an onion announce request packet.
*
* path is the path the request will take before it is sent to dest.
*
@@ -78,12 +92,14 @@ int create_data_request(uint8_t *packet, uint16_t max_packet_length, const uint8
* return -1 on failure.
* return 0 on success.
*/
-int send_announce_request(const Networking_Core *net, const Onion_Path *path, Node_format dest,
+non_null()
+int send_announce_request(const Networking_Core *net, const Random *rng,
+ const Onion_Path *path, const Node_format *dest,
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 *data_public_key, uint64_t sendback_data);
-/** Create and send an onion data request packet.
+/** @brief Create and send an onion data request packet.
*
* path is the path the request will take before it is sent to dest.
* (if dest knows the person with the public_key they should
@@ -99,14 +115,24 @@ int send_announce_request(const Networking_Core *net, const Onion_Path *path, No
* return -1 on failure.
* return 0 on success.
*/
-int send_data_request(const Networking_Core *net, const Onion_Path *path, const IP_Port *dest,
- const uint8_t *public_key,
- const uint8_t *encrypt_public_key, const uint8_t *nonce, const uint8_t *data, uint16_t length);
+non_null()
+int send_data_request(const Networking_Core *net, const Random *rng, const Onion_Path *path, const IP_Port *dest,
+ const uint8_t *public_key, const uint8_t *encrypt_public_key, const uint8_t *nonce,
+ const uint8_t *data, uint16_t length);
-Onion_Announce *new_onion_announce(const Logger *log, Mono_Time *mono_time, DHT *dht);
+typedef int pack_extra_data_cb(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);
-void kill_onion_announce(Onion_Announce *onion_a);
+non_null(1) nullable(3, 4)
+void onion_announce_extra_data_callback(Onion_Announce *onion_a, uint16_t extra_data_max_size,
+ pack_extra_data_cb *extra_data_callback, void *extra_data_object);
+non_null()
+Onion_Announce *new_onion_announce(const Logger *log, const Random *rng, const Mono_Time *mono_time, DHT *dht);
+
+nullable(1)
+void kill_onion_announce(Onion_Announce *onion_a);
#endif
diff --git a/protocols/Tox/libtox/src/toxcore/onion_client.c b/protocols/Tox/libtox/src/toxcore/onion_client.c
index 592cb4f57d..91168c661c 100644
--- a/protocols/Tox/libtox/src/toxcore/onion_client.c
+++ b/protocols/Tox/libtox/src/toxcore/onion_client.c
@@ -14,11 +14,11 @@
#include <string.h>
#include "LAN_discovery.h"
+#include "ccompat.h"
#include "mono_time.h"
#include "util.h"
-/** defines for the array size and
- * timeout for onion announce packets. */
+/** @brief defines for the array size and timeout for onion announce packets. */
#define ANNOUNCE_ARRAY_SIZE 256
#define ANNOUNCE_TIMEOUT 10
@@ -27,7 +27,7 @@ typedef struct Onion_Node {
IP_Port ip_port;
uint8_t ping_id[ONION_PING_ID_SIZE];
uint8_t data_public_key[CRYPTO_PUBLIC_KEY_SIZE];
- uint8_t is_stored;
+ uint8_t is_stored; // Tribool.
uint64_t added_time;
@@ -35,7 +35,7 @@ typedef struct Onion_Node {
uint64_t last_pinged;
- uint8_t unsuccessful_pings;
+ uint8_t pings_since_last_response;
uint32_t path_used;
} Onion_Node;
@@ -55,10 +55,10 @@ typedef struct Last_Pinged {
} Last_Pinged;
typedef struct Onion_Friend {
- uint8_t status; /* 0 if friend is not valid, 1 if friend is valid.*/
- uint8_t is_online; /* Set by the onion_set_friend_status function. */
+ bool is_valid;
+ bool is_online;
- uint8_t know_dht_public_key; /* 0 if we don't know the dht public key of the other, 1 if we do. */
+ bool know_dht_public_key;
uint8_t dht_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t real_public_key[CRYPTO_PUBLIC_KEY_SIZE];
@@ -71,7 +71,11 @@ typedef struct Onion_Friend {
uint64_t last_noreplay;
- uint64_t last_seen;
+ uint64_t last_populated; // the last time we had a fully populated client nodes list
+ uint64_t time_last_pinged; // the last time we pinged this friend with any node
+
+ uint32_t run_count;
+ uint32_t pings; // how many sucessful pings we've made for this friend
Last_Pinged last_pinged[MAX_STORED_PINGED_NODES];
uint8_t last_pinged_index;
@@ -83,18 +87,19 @@ typedef struct Onion_Friend {
onion_dht_pk_cb *dht_pk_callback;
void *dht_pk_callback_object;
uint32_t dht_pk_callback_number;
-
- uint32_t run_count;
} Onion_Friend;
+static const Onion_Friend empty_onion_friend = {false};
+
typedef struct Onion_Data_Handler {
oniondata_handler_cb *function;
void *object;
} Onion_Data_Handler;
struct Onion_Client {
- Mono_Time *mono_time;
+ const Mono_Time *mono_time;
const Logger *logger;
+ const Random *rng;
DHT *dht;
Net_Crypto *c;
@@ -111,6 +116,7 @@ struct Onion_Client {
uint8_t secret_symmetric_key[CRYPTO_SYMMETRIC_KEY_SIZE];
uint64_t last_run;
uint64_t first_run;
+ uint64_t last_time_connected;
uint8_t temp_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t temp_secret_key[CRYPTO_SECRET_KEY_SIZE];
@@ -128,6 +134,7 @@ struct Onion_Client {
Onion_Data_Handler onion_data_handlers[256];
uint64_t last_packet_recv;
+ uint64_t last_populated; // the last time we had a fully populated path nodes list
unsigned int onion_connected;
bool udp_connected;
@@ -143,20 +150,28 @@ Net_Crypto *onion_get_net_crypto(const Onion_Client *onion_c)
return onion_c->c;
}
-/** Add a node to the path_nodes bootstrap array.
+/** @brief Add a node to the path_nodes bootstrap array.
*
- * return -1 on failure
- * return 0 on success
+ * If a node with the given public key was already in the bootstrap array, this function has no
+ * effect and returns successfully. There is currently no way to update the IP/port for a bootstrap
+ * node, so if it changes, the Onion_Client must be recreated.
+ *
+ * @param onion_c The onion client object.
+ * @param ip_port IP/port for the bootstrap node.
+ * @param public_key DHT public key for the bootstrap node.
+ *
+ * @retval false on failure
+ * @retval true on success
*/
-int onion_add_bs_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const uint8_t *public_key)
+bool onion_add_bs_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const uint8_t *public_key)
{
if (!net_family_is_ipv4(ip_port->ip.family) && !net_family_is_ipv6(ip_port->ip.family)) {
- return -1;
+ return false;
}
for (unsigned int i = 0; i < MAX_PATH_NODES; ++i) {
- if (public_key_cmp(public_key, onion_c->path_nodes_bs[i].public_key) == 0) {
- return -1;
+ if (pk_equal(public_key, onion_c->path_nodes_bs[i].public_key)) {
+ return true;
}
}
@@ -164,21 +179,22 @@ int onion_add_bs_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const
memcpy(onion_c->path_nodes_bs[onion_c->path_nodes_index_bs % MAX_PATH_NODES].public_key, public_key,
CRYPTO_PUBLIC_KEY_SIZE);
- uint16_t last = onion_c->path_nodes_index_bs;
+ const uint16_t last = onion_c->path_nodes_index_bs;
++onion_c->path_nodes_index_bs;
if (onion_c->path_nodes_index_bs < last) {
onion_c->path_nodes_index_bs = MAX_PATH_NODES + 1;
}
- return 0;
+ return true;
}
-/** Add a node to the path_nodes array.
+/** @brief Add a node to the path_nodes array.
*
* return -1 on failure
* return 0 on success
*/
+non_null()
static int onion_add_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const uint8_t *public_key)
{
if (!net_family_is_ipv4(ip_port->ip.family) && !net_family_is_ipv6(ip_port->ip.family)) {
@@ -186,7 +202,7 @@ static int onion_add_path_node(Onion_Client *onion_c, const IP_Port *ip_port, co
}
for (unsigned int i = 0; i < MAX_PATH_NODES; ++i) {
- if (public_key_cmp(public_key, onion_c->path_nodes[i].public_key) == 0) {
+ if (pk_equal(public_key, onion_c->path_nodes[i].public_key)) {
return -1;
}
}
@@ -195,7 +211,7 @@ static int onion_add_path_node(Onion_Client *onion_c, const IP_Port *ip_port, co
memcpy(onion_c->path_nodes[onion_c->path_nodes_index % MAX_PATH_NODES].public_key, public_key,
CRYPTO_PUBLIC_KEY_SIZE);
- uint16_t last = onion_c->path_nodes_index;
+ const uint16_t last = onion_c->path_nodes_index;
++onion_c->path_nodes_index;
if (onion_c->path_nodes_index < last) {
@@ -205,13 +221,13 @@ static int onion_add_path_node(Onion_Client *onion_c, const IP_Port *ip_port, co
return 0;
}
-/** Put up to max_num nodes in nodes.
+/** @brief Put up to max_num nodes in nodes.
*
* return the number of nodes.
*/
uint16_t onion_backup_nodes(const Onion_Client *onion_c, Node_format *nodes, uint16_t max_num)
{
- if (!max_num) {
+ if (max_num == 0) {
return 0;
}
@@ -227,7 +243,7 @@ uint16_t onion_backup_nodes(const Onion_Client *onion_c, Node_format *nodes, uin
bool already_saved = false;
for (uint16_t k = 0; k < num_nodes; ++k) {
- if (public_key_cmp(nodes[k].public_key, onion_c->path_nodes_bs[j].public_key) == 0) {
+ if (pk_equal(nodes[k].public_key, onion_c->path_nodes_bs[j].public_key)) {
already_saved = true;
break;
}
@@ -242,13 +258,14 @@ uint16_t onion_backup_nodes(const Onion_Client *onion_c, Node_format *nodes, uin
return i;
}
-/** Put up to max_num random nodes in nodes.
+/** @brief Put up to max_num random nodes in nodes.
*
* return the number of nodes.
*/
+non_null()
static uint16_t random_nodes_path_onion(const Onion_Client *onion_c, Node_format *nodes, uint16_t max_num)
{
- if (!max_num) {
+ if (max_num == 0) {
return 0;
}
@@ -261,25 +278,22 @@ static uint16_t random_nodes_path_onion(const Onion_Client *onion_c, Node_format
}
for (unsigned int i = 0; i < max_num; ++i) {
- const uint32_t rand_idx = random_range_u32(num_nodes);
+ const uint32_t rand_idx = random_range_u32(onion_c->rng, num_nodes);
nodes[i] = onion_c->path_nodes[rand_idx];
}
} else {
- int random_tcp = get_random_tcp_con_number(onion_c->c);
+ const int random_tcp = get_random_tcp_con_number(onion_c->c);
if (random_tcp == -1) {
return 0;
}
if (num_nodes >= 2) {
- nodes[0] = (Node_format) {
- 0
- };
- nodes[0].ip_port.ip.family = net_family_tcp_family;
- nodes[0].ip_port.ip.ip.v4.uint32 = random_tcp;
+ nodes[0] = empty_node_format;
+ nodes[0].ip_port = tcp_connections_number_to_ip_port(random_tcp);
for (unsigned int i = 1; i < max_num; ++i) {
- const uint32_t rand_idx = random_range_u32(num_nodes);
+ const uint32_t rand_idx = random_range_u32(onion_c->rng, num_nodes);
nodes[i] = onion_c->path_nodes[rand_idx];
}
} else {
@@ -289,14 +303,11 @@ static uint16_t random_nodes_path_onion(const Onion_Client *onion_c, Node_format
return 0;
}
- nodes[0] = (Node_format) {
- 0
- };
- nodes[0].ip_port.ip.family = net_family_tcp_family;
- nodes[0].ip_port.ip.ip.v4.uint32 = random_tcp;
+ nodes[0] = empty_node_format;
+ nodes[0].ip_port = tcp_connections_number_to_ip_port(random_tcp);
for (unsigned int i = 1; i < max_num; ++i) {
- const uint32_t rand_idx = random_range_u32(num_nodes_bs);
+ const uint32_t rand_idx = random_range_u32(onion_c->rng, num_nodes_bs);
nodes[i] = onion_c->path_nodes_bs[rand_idx];
}
}
@@ -309,6 +320,7 @@ static uint16_t random_nodes_path_onion(const Onion_Client *onion_c, Node_format
* return -1 if nodes are suitable for creating a new path.
* return path number of already existing similar path if one already exists.
*/
+non_null()
static int is_path_used(const Mono_Time *mono_time, const Onion_Client_Paths *onion_paths, const Node_format *nodes)
{
for (unsigned int i = 0; i < NUMBER_ONION_PATHS; ++i) {
@@ -330,27 +342,29 @@ static int is_path_used(const Mono_Time *mono_time, const Onion_Client_Paths *on
}
/** is path timed out */
+non_null()
static bool path_timed_out(const Mono_Time *mono_time, const Onion_Client_Paths *onion_paths, uint32_t pathnum)
{
pathnum = pathnum % NUMBER_ONION_PATHS;
- bool is_new = onion_paths->last_path_success[pathnum] == onion_paths->path_creation_time[pathnum];
- uint64_t timeout = is_new ? ONION_PATH_FIRST_TIMEOUT : ONION_PATH_TIMEOUT;
+ const bool is_new = onion_paths->last_path_success[pathnum] == onion_paths->path_creation_time[pathnum];
+ const uint64_t timeout = is_new ? ONION_PATH_FIRST_TIMEOUT : ONION_PATH_TIMEOUT;
- return ((onion_paths->last_path_used_times[pathnum] >= ONION_PATH_MAX_NO_RESPONSE_USES
+ return (onion_paths->last_path_used_times[pathnum] >= ONION_PATH_MAX_NO_RESPONSE_USES
&& mono_time_is_timeout(mono_time, onion_paths->last_path_used[pathnum], timeout))
- || mono_time_is_timeout(mono_time, onion_paths->path_creation_time[pathnum], ONION_PATH_MAX_LIFETIME));
+ || mono_time_is_timeout(mono_time, onion_paths->path_creation_time[pathnum], ONION_PATH_MAX_LIFETIME);
}
/** should node be considered to have timed out */
+non_null()
static bool onion_node_timed_out(const Onion_Node *node, const Mono_Time *mono_time)
{
- return (node->timestamp == 0
- || (node->unsuccessful_pings >= ONION_NODE_MAX_PINGS
- && mono_time_is_timeout(mono_time, node->last_pinged, ONION_NODE_TIMEOUT)));
+ return node->timestamp == 0
+ || (node->pings_since_last_response >= ONION_NODE_MAX_PINGS
+ && mono_time_is_timeout(mono_time, node->last_pinged, ONION_NODE_TIMEOUT));
}
-/** Create a new path or use an old suitable one (if pathnum is valid)
+/** @brief Create a new path or use an old suitable one (if pathnum is valid)
* or a random one from onion_paths.
*
* return -1 on failure
@@ -359,10 +373,11 @@ static bool onion_node_timed_out(const Onion_Node *node, const Mono_Time *mono_t
* TODO(irungentoo): Make this function better, it currently probably is
* vulnerable to some attacks that could deanonimize us.
*/
+non_null()
static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_paths, uint32_t pathnum, Onion_Path *path)
{
if (pathnum == UINT32_MAX) {
- pathnum = random_range_u32(NUMBER_ONION_PATHS);
+ pathnum = random_range_u32(onion_c->rng, NUMBER_ONION_PATHS);
} else {
pathnum = pathnum % NUMBER_ONION_PATHS;
}
@@ -374,10 +389,10 @@ static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_pa
return -1;
}
- int n = is_path_used(onion_c->mono_time, onion_paths, nodes);
+ const int n = is_path_used(onion_c->mono_time, onion_paths, nodes);
if (n == -1) {
- if (create_onion_path(onion_c->dht, &onion_paths->paths[pathnum], nodes) == -1) {
+ if (create_onion_path(onion_c->rng, onion_c->dht, &onion_paths->paths[pathnum], nodes) == -1) {
return -1;
}
@@ -385,7 +400,7 @@ static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_pa
onion_paths->last_path_success[pathnum] = onion_paths->path_creation_time[pathnum];
onion_paths->last_path_used_times[pathnum] = ONION_PATH_MAX_NO_RESPONSE_USES / 2;
- uint32_t path_num = random_u32();
+ uint32_t path_num = random_u32(onion_c->rng);
path_num /= NUMBER_ONION_PATHS;
path_num *= NUMBER_ONION_PATHS;
path_num += pathnum;
@@ -406,18 +421,18 @@ static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_pa
}
/** Does path with path_num exist. */
+non_null()
static bool path_exists(const Mono_Time *mono_time, const Onion_Client_Paths *onion_paths, uint32_t path_num)
{
if (path_timed_out(mono_time, onion_paths, path_num)) {
- return 0;
+ return false;
}
return onion_paths->paths[path_num % NUMBER_ONION_PATHS].path_num == path_num;
}
-/** Set path timeouts, return the path number.
- *
- */
+/** Set path timeouts, return the path number. */
+non_null()
static uint32_t set_path_timeouts(Onion_Client *onion_c, uint32_t num, uint32_t path_num)
{
if (num > onion_c->num_friends) {
@@ -450,17 +465,18 @@ static uint32_t set_path_timeouts(Onion_Client *onion_c, uint32_t num, uint32_t
return -1;
}
-/** Function to send onion packet via TCP and UDP.
+/** @brief Function to send onion packet via TCP and UDP.
*
* return -1 on failure.
* return 0 on success.
*/
+non_null()
static int send_onion_packet_tcp_udp(const Onion_Client *onion_c, const Onion_Path *path, const IP_Port *dest,
const uint8_t *data, uint16_t length)
{
if (net_family_is_ipv4(path->ip_port1.ip.family) || net_family_is_ipv6(path->ip_port1.ip.family)) {
uint8_t packet[ONION_MAX_PACKET_SIZE];
- int len = create_onion_packet(packet, sizeof(packet), path, dest, data, length);
+ const int len = create_onion_packet(onion_c->rng, packet, sizeof(packet), path, dest, data, length);
if (len == -1) {
return -1;
@@ -473,21 +489,23 @@ static int send_onion_packet_tcp_udp(const Onion_Client *onion_c, const Onion_Pa
return 0;
}
- if (net_family_is_tcp_family(path->ip_port1.ip.family)) {
+ unsigned int tcp_connections_number;
+
+ if (ip_port_to_tcp_connections_number(&path->ip_port1, &tcp_connections_number)) {
uint8_t packet[ONION_MAX_PACKET_SIZE];
- int len = create_onion_packet_tcp(packet, sizeof(packet), path, dest, data, length);
+ const int len = create_onion_packet_tcp(onion_c->rng, packet, sizeof(packet), path, dest, data, length);
if (len == -1) {
return -1;
}
- return send_tcp_onion_request(onion_c->c, path->ip_port1.ip.ip.v4.uint32, packet, len);
+ return send_tcp_onion_request(onion_c->c, tcp_connections_number, packet, len);
}
return -1;
}
-/** Creates a sendback for use in an announce request.
+/** @brief Creates a sendback for use in an announce request.
*
* num is 0 if we used our secret public key for the announce
* num is 1 + friendnum if we use a temporary one.
@@ -502,6 +520,7 @@ static int send_onion_packet_tcp_udp(const Onion_Client *onion_c, const Onion_Pa
* return 0 on success
*
*/
+non_null()
static int new_sendback(Onion_Client *onion_c, uint32_t num, const uint8_t *public_key, const IP_Port *ip_port,
uint32_t path_num, uint64_t *sendback)
{
@@ -510,7 +529,7 @@ static int new_sendback(Onion_Client *onion_c, uint32_t num, const uint8_t *publ
memcpy(data + sizeof(uint32_t), public_key, CRYPTO_PUBLIC_KEY_SIZE);
memcpy(data + sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE, ip_port, sizeof(IP_Port));
memcpy(data + sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE + sizeof(IP_Port), &path_num, sizeof(uint32_t));
- *sendback = ping_array_add(onion_c->announce_ping_array, onion_c->mono_time, data, sizeof(data));
+ *sendback = ping_array_add(onion_c->announce_ping_array, onion_c->mono_time, onion_c->rng, data, sizeof(data));
if (*sendback == 0) {
return -1;
@@ -519,7 +538,7 @@ static int new_sendback(Onion_Client *onion_c, uint32_t num, const uint8_t *publ
return 0;
}
-/** Checks if the sendback is valid and returns the public key contained in it in ret_pubkey and the
+/** @brief Checks if the sendback is valid and returns the public key contained in it in ret_pubkey and the
* ip contained in it in ret_ip_port
*
* sendback is the sendback ONION_ANNOUNCE_SENDBACK_DATA_LENGTH big
@@ -529,6 +548,7 @@ static int new_sendback(Onion_Client *onion_c, uint32_t num, const uint8_t *publ
* return -1 on failure
* return num (see new_sendback(...)) on success
*/
+non_null()
static uint32_t check_sendback(Onion_Client *onion_c, const uint8_t *sendback, uint8_t *ret_pubkey,
IP_Port *ret_ip_port, uint32_t *path_num)
{
@@ -549,6 +569,7 @@ static uint32_t check_sendback(Onion_Client *onion_c, const uint8_t *sendback, u
return num;
}
+non_null(1, 3, 4) nullable(5)
static int client_send_announce_request(Onion_Client *onion_c, uint32_t num, const IP_Port *dest,
const uint8_t *dest_pubkey, const uint8_t *ping_id, uint32_t pathnum)
{
@@ -583,13 +604,15 @@ static int client_send_announce_request(Onion_Client *onion_c, uint32_t num, con
int len;
if (num == 0) {
- len = create_announce_request(request, sizeof(request), dest_pubkey, nc_get_self_public_key(onion_c->c),
- nc_get_self_secret_key(onion_c->c), ping_id, nc_get_self_public_key(onion_c->c),
- onion_c->temp_public_key, sendback);
+ len = create_announce_request(
+ onion_c->rng, request, sizeof(request), dest_pubkey, nc_get_self_public_key(onion_c->c),
+ nc_get_self_secret_key(onion_c->c), ping_id, nc_get_self_public_key(onion_c->c),
+ onion_c->temp_public_key, sendback);
} else {
- len = create_announce_request(request, sizeof(request), dest_pubkey, onion_c->friends_list[num - 1].temp_public_key,
- onion_c->friends_list[num - 1].temp_secret_key, ping_id,
- onion_c->friends_list[num - 1].real_public_key, zero_ping_id, sendback);
+ len = create_announce_request(
+ onion_c->rng, request, sizeof(request), dest_pubkey, onion_c->friends_list[num - 1].temp_public_key,
+ onion_c->friends_list[num - 1].temp_secret_key, ping_id,
+ onion_c->friends_list[num - 1].real_public_key, zero_ping_id, sendback);
}
if (len == -1) {
@@ -605,6 +628,7 @@ typedef struct Onion_Client_Cmp_Data {
Onion_Node entry;
} Onion_Client_Cmp_Data;
+non_null()
static int onion_client_cmp_entry(const void *a, const void *b)
{
const Onion_Client_Cmp_Data *cmp1 = (const Onion_Client_Cmp_Data *)a;
@@ -613,8 +637,8 @@ static int onion_client_cmp_entry(const void *a, const void *b)
const Onion_Node entry2 = cmp2->entry;
const uint8_t *cmp_public_key = cmp1->base_public_key;
- const int t1 = onion_node_timed_out(&entry1, cmp1->mono_time);
- const int t2 = onion_node_timed_out(&entry2, cmp2->mono_time);
+ const bool t1 = onion_node_timed_out(&entry1, cmp1->mono_time);
+ const bool t2 = onion_node_timed_out(&entry2, cmp2->mono_time);
if (t1 && t2) {
return 0;
@@ -628,25 +652,30 @@ static int onion_client_cmp_entry(const void *a, const void *b)
return 1;
}
- const int close = id_closest(cmp_public_key, entry1.public_key, entry2.public_key);
+ const int closest = id_closest(cmp_public_key, entry1.public_key, entry2.public_key);
- if (close == 1) {
+ if (closest == 1) {
return 1;
}
- if (close == 2) {
+ if (closest == 2) {
return -1;
}
return 0;
}
+non_null()
static void sort_onion_node_list(Onion_Node *list, unsigned int length, const Mono_Time *mono_time,
const uint8_t *comp_public_key)
{
// Pass comp_public_key to qsort with each Client_data entry, so the
// comparison function can use it as the base of comparison.
- VLA(Onion_Client_Cmp_Data, cmp_list, length);
+ Onion_Client_Cmp_Data *cmp_list = (Onion_Client_Cmp_Data *)calloc(length, sizeof(Onion_Client_Cmp_Data));
+
+ if (cmp_list == nullptr) {
+ return;
+ }
for (uint32_t i = 0; i < length; ++i) {
cmp_list[i].mono_time = mono_time;
@@ -659,8 +688,11 @@ static void sort_onion_node_list(Onion_Node *list, unsigned int length, const Mo
for (uint32_t i = 0; i < length; ++i) {
list[i] = cmp_list[i].entry;
}
+
+ free(cmp_list);
}
+non_null()
static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t *public_key, const IP_Port *ip_port,
uint8_t is_stored, const uint8_t *pingid_or_key, uint32_t path_used)
{
@@ -668,16 +700,16 @@ static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t
return -1;
}
- Onion_Node *list_nodes = nullptr;
+ Onion_Node *node_list = nullptr;
const uint8_t *reference_id = nullptr;
unsigned int list_length;
if (num == 0) {
- list_nodes = onion_c->clients_announce_list;
+ node_list = onion_c->clients_announce_list;
reference_id = nc_get_self_public_key(onion_c->c);
list_length = MAX_ONION_CLIENTS_ANNOUNCE;
- if (is_stored == 1 && public_key_cmp(pingid_or_key, onion_c->temp_public_key) != 0) {
+ if (is_stored == 1 && !pk_equal(pingid_or_key, onion_c->temp_public_key)) {
is_stored = 0;
}
} else {
@@ -685,29 +717,25 @@ static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t
return -1;
}
- if (is_stored == 1) {
- onion_c->friends_list[num - 1].last_seen = mono_time_get(onion_c->mono_time);
- }
-
- list_nodes = onion_c->friends_list[num - 1].clients_list;
+ node_list = onion_c->friends_list[num - 1].clients_list;
reference_id = onion_c->friends_list[num - 1].real_public_key;
list_length = MAX_ONION_CLIENTS;
}
- sort_onion_node_list(list_nodes, list_length, onion_c->mono_time, reference_id);
+ sort_onion_node_list(node_list, list_length, onion_c->mono_time, reference_id);
int index = -1;
- int stored = 0;
+ bool stored = false;
- if (onion_node_timed_out(&list_nodes[0], onion_c->mono_time)
- || id_closest(reference_id, list_nodes[0].public_key, public_key) == 2) {
+ if (onion_node_timed_out(&node_list[0], onion_c->mono_time)
+ || id_closest(reference_id, node_list[0].public_key, public_key) == 2) {
index = 0;
}
for (unsigned int i = 0; i < list_length; ++i) {
- if (public_key_cmp(list_nodes[i].public_key, public_key) == 0) {
+ if (pk_equal(node_list[i].public_key, public_key)) {
index = i;
- stored = 1;
+ stored = true;
break;
}
}
@@ -716,38 +744,39 @@ static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t
return 0;
}
- memcpy(list_nodes[index].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
- list_nodes[index].ip_port = *ip_port;
+ memcpy(node_list[index].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
+ node_list[index].ip_port = *ip_port;
// TODO(irungentoo): remove this and find a better source of nodes to use for paths.
onion_add_path_node(onion_c, ip_port, public_key);
if (is_stored == 1) {
- memcpy(list_nodes[index].data_public_key, pingid_or_key, CRYPTO_PUBLIC_KEY_SIZE);
+ memcpy(node_list[index].data_public_key, pingid_or_key, CRYPTO_PUBLIC_KEY_SIZE);
} else {
- memcpy(list_nodes[index].ping_id, pingid_or_key, ONION_PING_ID_SIZE);
+ memcpy(node_list[index].ping_id, pingid_or_key, ONION_PING_ID_SIZE);
}
- list_nodes[index].is_stored = is_stored;
- list_nodes[index].timestamp = mono_time_get(onion_c->mono_time);
- list_nodes[index].unsuccessful_pings = 0;
+ node_list[index].is_stored = is_stored;
+ node_list[index].timestamp = mono_time_get(onion_c->mono_time);
+ node_list[index].pings_since_last_response = 0;
if (!stored) {
- list_nodes[index].last_pinged = 0;
- list_nodes[index].added_time = mono_time_get(onion_c->mono_time);
+ node_list[index].last_pinged = 0;
+ node_list[index].added_time = mono_time_get(onion_c->mono_time);
}
- list_nodes[index].path_used = path_used;
+ node_list[index].path_used = path_used;
return 0;
}
-static int good_to_ping(const Mono_Time *mono_time, Last_Pinged *last_pinged, uint8_t *last_pinged_index,
- const uint8_t *public_key)
+non_null()
+static bool good_to_ping(const Mono_Time *mono_time, Last_Pinged *last_pinged, uint8_t *last_pinged_index,
+ const uint8_t *public_key)
{
for (unsigned int i = 0; i < MAX_STORED_PINGED_NODES; ++i) {
if (!mono_time_is_timeout(mono_time, last_pinged[i].timestamp, MIN_NODE_PING_TIME)) {
- if (public_key_cmp(last_pinged[i].public_key, public_key) == 0) {
- return 0;
+ if (pk_equal(last_pinged[i].public_key, public_key)) {
+ return false;
}
}
}
@@ -755,9 +784,10 @@ static int good_to_ping(const Mono_Time *mono_time, Last_Pinged *last_pinged, ui
memcpy(last_pinged[*last_pinged_index % MAX_STORED_PINGED_NODES].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
last_pinged[*last_pinged_index % MAX_STORED_PINGED_NODES].timestamp = mono_time_get(mono_time);
++*last_pinged_index;
- return 1;
+ return true;
}
+non_null()
static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, const Node_format *nodes, uint16_t num_nodes,
const IP_Port *source)
{
@@ -769,7 +799,7 @@ static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, const Node_for
return 0;
}
- const Onion_Node *list_nodes = nullptr;
+ const Onion_Node *node_list = nullptr;
const uint8_t *reference_id = nullptr;
unsigned int list_length;
@@ -777,13 +807,13 @@ static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, const Node_for
uint8_t *last_pinged_index = nullptr;
if (num == 0) {
- list_nodes = onion_c->clients_announce_list;
+ node_list = onion_c->clients_announce_list;
reference_id = nc_get_self_public_key(onion_c->c);
list_length = MAX_ONION_CLIENTS_ANNOUNCE;
last_pinged = onion_c->last_pinged;
last_pinged_index = &onion_c->last_pinged_index;
} else {
- list_nodes = onion_c->friends_list[num - 1].clients_list;
+ node_list = onion_c->friends_list[num - 1].clients_list;
reference_id = onion_c->friends_list[num - 1].real_public_key;
list_length = MAX_ONION_CLIENTS;
last_pinged = onion_c->friends_list[num - 1].last_pinged;
@@ -799,15 +829,15 @@ static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, const Node_for
}
}
- if (onion_node_timed_out(&list_nodes[0], onion_c->mono_time)
- || id_closest(reference_id, list_nodes[0].public_key, nodes[i].public_key) == 2
- || onion_node_timed_out(&list_nodes[1], onion_c->mono_time)
- || id_closest(reference_id, list_nodes[1].public_key, nodes[i].public_key) == 2) {
+ if (onion_node_timed_out(&node_list[0], onion_c->mono_time)
+ || id_closest(reference_id, node_list[0].public_key, nodes[i].public_key) == 2
+ || onion_node_timed_out(&node_list[1], onion_c->mono_time)
+ || id_closest(reference_id, node_list[1].public_key, nodes[i].public_key) == 2) {
uint32_t j;
/* check if node is already in list. */
for (j = 0; j < list_length; ++j) {
- if (public_key_cmp(list_nodes[j].public_key, nodes[i].public_key) == 0) {
+ if (pk_equal(node_list[j].public_key, nodes[i].public_key)) {
break;
}
}
@@ -821,6 +851,7 @@ static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, const Node_for
return 0;
}
+non_null()
static int handle_announce_response(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
@@ -830,12 +861,12 @@ static int handle_announce_response(void *object, const IP_Port *source, const u
return 1;
}
- uint16_t len_nodes = length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE;
+ const uint16_t len_nodes = length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE;
uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE];
IP_Port ip_port;
uint32_t path_num;
- uint32_t num = check_sendback(onion_c, packet + 1, public_key, &ip_port, &path_num);
+ const uint32_t num = check_sendback(onion_c, packet + 1, public_key, &ip_port, &path_num);
if (num > onion_c->num_friends) {
return 1;
@@ -850,7 +881,7 @@ static int handle_announce_response(void *object, const IP_Port *source, const u
packet + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE,
length - (1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE), plain);
} else {
- if (onion_c->friends_list[num - 1].status == 0) {
+ if (!onion_c->friends_list[num - 1].is_valid) {
return 1;
}
@@ -864,7 +895,7 @@ static int handle_announce_response(void *object, const IP_Port *source, const u
return 1;
}
- uint32_t path_used = set_path_timeouts(onion_c, num, path_num);
+ const uint32_t path_used = set_path_timeouts(onion_c, num, path_num);
if (client_add_to_list(onion_c, num, public_key, &ip_port, plain[0], plain + 1, path_used) == -1) {
return 1;
@@ -872,7 +903,7 @@ static int handle_announce_response(void *object, const IP_Port *source, const u
if (len_nodes != 0) {
Node_format nodes[MAX_SENT_NODES];
- int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, nullptr, plain + 1 + ONION_PING_ID_SIZE, len_nodes, 0);
+ const int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, nullptr, plain + 1 + ONION_PING_ID_SIZE, len_nodes, false);
if (num_nodes <= 0) {
return 1;
@@ -890,6 +921,7 @@ static int handle_announce_response(void *object, const IP_Port *source, const u
#define DATA_IN_RESPONSE_MIN_SIZE ONION_DATA_IN_RESPONSE_MIN_SIZE
+non_null()
static int handle_data_response(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
@@ -921,7 +953,7 @@ static int handle_data_response(void *object, const IP_Port *source, const uint8
return 1;
}
- if (!onion_c->onion_data_handlers[plain[0]].function) {
+ if (onion_c->onion_data_handlers[plain[0]].function == nullptr) {
return 1;
}
@@ -931,6 +963,7 @@ static int handle_data_response(void *object, const IP_Port *source, const uint8
#define DHTPK_DATA_MIN_LENGTH (1 + sizeof(uint64_t) + CRYPTO_PUBLIC_KEY_SIZE)
#define DHTPK_DATA_MAX_LENGTH (DHTPK_DATA_MIN_LENGTH + sizeof(Node_format)*MAX_SENT_NODES)
+non_null(1, 2, 3) nullable(5)
static int handle_dhtpk_announce(void *object, const uint8_t *source_pubkey, const uint8_t *data, uint16_t length,
void *userdata)
{
@@ -944,7 +977,7 @@ static int handle_dhtpk_announce(void *object, const uint8_t *source_pubkey, con
return 1;
}
- int friend_num = onion_friend_num(onion_c, source_pubkey);
+ const int friend_num = onion_friend_num(onion_c, source_pubkey);
if (friend_num == -1) {
return 1;
@@ -959,20 +992,19 @@ static int handle_dhtpk_announce(void *object, const uint8_t *source_pubkey, con
onion_c->friends_list[friend_num].last_noreplay = no_replay;
- if (onion_c->friends_list[friend_num].dht_pk_callback) {
+ if (onion_c->friends_list[friend_num].dht_pk_callback != nullptr) {
onion_c->friends_list[friend_num].dht_pk_callback(onion_c->friends_list[friend_num].dht_pk_callback_object,
onion_c->friends_list[friend_num].dht_pk_callback_number, data + 1 + sizeof(uint64_t), userdata);
}
onion_set_friend_DHT_pubkey(onion_c, friend_num, data + 1 + sizeof(uint64_t));
- onion_c->friends_list[friend_num].last_seen = mono_time_get(onion_c->mono_time);
- uint16_t len_nodes = length - DHTPK_DATA_MIN_LENGTH;
+ const uint16_t len_nodes = length - DHTPK_DATA_MIN_LENGTH;
if (len_nodes != 0) {
Node_format nodes[MAX_SENT_NODES];
- int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, nullptr, data + 1 + sizeof(uint64_t) + CRYPTO_PUBLIC_KEY_SIZE,
- len_nodes, 1);
+ const int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, nullptr, data + 1 + sizeof(uint64_t) + CRYPTO_PUBLIC_KEY_SIZE,
+ len_nodes, true);
if (num_nodes <= 0) {
return 1;
@@ -984,9 +1016,9 @@ static int handle_dhtpk_announce(void *object, const uint8_t *source_pubkey, con
if (net_family_is_ipv4(family) || net_family_is_ipv6(family)) {
dht_getnodes(onion_c->dht, &nodes[i].ip_port, nodes[i].public_key, onion_c->friends_list[friend_num].dht_public_key);
} else if (net_family_is_tcp_ipv4(family) || net_family_is_tcp_ipv6(family)) {
- if (onion_c->friends_list[friend_num].tcp_relay_node_callback) {
+ if (onion_c->friends_list[friend_num].tcp_relay_node_callback != nullptr) {
void *obj = onion_c->friends_list[friend_num].tcp_relay_node_callback_object;
- uint32_t number = onion_c->friends_list[friend_num].tcp_relay_node_callback_number;
+ const uint32_t number = onion_c->friends_list[friend_num].tcp_relay_node_callback_number;
onion_c->friends_list[friend_num].tcp_relay_node_callback(obj, number, &nodes[i].ip_port, nodes[i].public_key);
}
}
@@ -996,16 +1028,17 @@ static int handle_dhtpk_announce(void *object, const uint8_t *source_pubkey, con
return 0;
}
+non_null()
static int handle_tcp_onion(void *object, const uint8_t *data, uint16_t length, void *userdata)
{
if (length == 0) {
return 1;
}
- IP_Port ip_port = {0};
- ip_port.ip.family = net_family_tcp_family;
+ IP_Port ip_port = {{{0}}};
+ ip_port.ip.family = net_family_tcp_server();
- if (data[0] == NET_PACKET_ANNOUNCE_RESPONSE) {
+ if (data[0] == NET_PACKET_ANNOUNCE_RESPONSE_OLD) {
return handle_announce_response(object, &ip_port, data, length, userdata);
}
@@ -1016,7 +1049,7 @@ static int handle_tcp_onion(void *object, const uint8_t *data, uint16_t length,
return 1;
}
-/** Send data of length length to friendnum.
+/** @brief Send data of length length to friendnum.
* Maximum length of data is ONION_CLIENT_MAX_DATA_SIZE.
* This data will be received by the friend using the Onion_Data_Handlers callbacks.
*
@@ -1042,16 +1075,16 @@ int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data,
unsigned int good_nodes[MAX_ONION_CLIENTS];
unsigned int num_good = 0;
unsigned int num_nodes = 0;
- const Onion_Node *list_nodes = onion_c->friends_list[friend_num].clients_list;
+ const Onion_Node *node_list = onion_c->friends_list[friend_num].clients_list;
for (unsigned int i = 0; i < MAX_ONION_CLIENTS; ++i) {
- if (onion_node_timed_out(&list_nodes[i], onion_c->mono_time)) {
+ if (onion_node_timed_out(&node_list[i], onion_c->mono_time)) {
continue;
}
++num_nodes;
- if (list_nodes[i].is_stored) {
+ if (node_list[i].is_stored != 0) {
good_nodes[num_good] = i;
++num_good;
}
@@ -1062,7 +1095,7 @@ int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data,
}
uint8_t nonce[CRYPTO_NONCE_SIZE];
- random_nonce(nonce);
+ random_nonce(onion_c->rng, nonce);
VLA(uint8_t, packet, DATA_IN_RESPONSE_MIN_SIZE + length);
memcpy(packet, nc_get_self_public_key(onion_c->c), CRYPTO_PUBLIC_KEY_SIZE);
@@ -1084,14 +1117,15 @@ int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data,
}
uint8_t o_packet[ONION_MAX_PACKET_SIZE];
- len = create_data_request(o_packet, sizeof(o_packet), onion_c->friends_list[friend_num].real_public_key,
- list_nodes[good_nodes[i]].data_public_key, nonce, packet, SIZEOF_VLA(packet));
+ len = create_data_request(
+ onion_c->rng, o_packet, sizeof(o_packet), onion_c->friends_list[friend_num].real_public_key,
+ node_list[good_nodes[i]].data_public_key, nonce, packet, SIZEOF_VLA(packet));
if (len == -1) {
continue;
}
- if (send_onion_packet_tcp_udp(onion_c, &path, &list_nodes[good_nodes[i]].ip_port, o_packet, len) == 0) {
+ if (send_onion_packet_tcp_udp(onion_c, &path, &node_list[good_nodes[i]].ip_port, o_packet, len) == 0) {
++good;
}
}
@@ -1099,13 +1133,14 @@ int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data,
return good;
}
-/** Try to send the dht public key via the DHT instead of onion
+/** @brief Try to send the dht public key via the DHT instead of onion
*
* Even if this function succeeds, the friend might not receive any data.
*
* return the number of packets sent on success
* return -1 on failure.
*/
+non_null()
static int send_dht_dhtpk(const Onion_Client *onion_c, int friend_num, const uint8_t *data, uint16_t length)
{
if ((uint32_t)friend_num >= onion_c->num_friends) {
@@ -1117,7 +1152,7 @@ static int send_dht_dhtpk(const Onion_Client *onion_c, int friend_num, const uin
}
uint8_t nonce[CRYPTO_NONCE_SIZE];
- random_nonce(nonce);
+ random_nonce(onion_c->rng, nonce);
VLA(uint8_t, temp, DATA_IN_RESPONSE_MIN_SIZE + CRYPTO_NONCE_SIZE + length);
memcpy(temp, nc_get_self_public_key(onion_c->c), CRYPTO_PUBLIC_KEY_SIZE);
@@ -1131,8 +1166,9 @@ static int send_dht_dhtpk(const Onion_Client *onion_c, int friend_num, const uin
}
uint8_t packet_data[MAX_CRYPTO_REQUEST_SIZE];
- len = create_request(dht_get_self_public_key(onion_c->dht), dht_get_self_secret_key(onion_c->dht), packet_data,
- onion_c->friends_list[friend_num].dht_public_key, temp, SIZEOF_VLA(temp), CRYPTO_PACKET_DHTPK);
+ len = create_request(
+ onion_c->rng, dht_get_self_public_key(onion_c->dht), dht_get_self_secret_key(onion_c->dht), packet_data,
+ onion_c->friends_list[friend_num].dht_public_key, temp, SIZEOF_VLA(temp), CRYPTO_PACKET_DHTPK);
assert(len <= UINT16_MAX);
const Packet packet = {packet_data, (uint16_t)len};
@@ -1143,6 +1179,7 @@ static int send_dht_dhtpk(const Onion_Client *onion_c, int friend_num, const uin
return route_to_friend(onion_c->dht, onion_c->friends_list[friend_num].dht_public_key, &packet);
}
+non_null()
static int handle_dht_dhtpk(void *object, const IP_Port *source, const uint8_t *source_pubkey, const uint8_t *packet,
uint16_t length, void *userdata)
{
@@ -1157,22 +1194,22 @@ static int handle_dht_dhtpk(void *object, const IP_Port *source, const uint8_t *
}
uint8_t plain[DHTPK_DATA_MAX_LENGTH];
- int len = decrypt_data(packet, nc_get_self_secret_key(onion_c->c),
- packet + CRYPTO_PUBLIC_KEY_SIZE,
- packet + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE,
- length - (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE), plain);
+ const int len = decrypt_data(packet, nc_get_self_secret_key(onion_c->c),
+ packet + CRYPTO_PUBLIC_KEY_SIZE,
+ packet + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE,
+ length - (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE), plain);
if (len != length - (DATA_IN_RESPONSE_MIN_SIZE + CRYPTO_NONCE_SIZE)) {
return 1;
}
- if (public_key_cmp(source_pubkey, plain + 1 + sizeof(uint64_t)) != 0) {
+ if (!pk_equal(source_pubkey, plain + 1 + sizeof(uint64_t))) {
return 1;
}
return handle_dhtpk_announce(onion_c, packet, plain, len, userdata);
}
-/** Send the packets to tell our friends what our DHT public key is.
+/** @brief Send the packets to tell our friends what our DHT public key is.
*
* if onion_dht_both is 0, use only the onion to send the packet.
* if it is 1, use only the dht.
@@ -1181,6 +1218,7 @@ static int handle_dht_dhtpk(void *object, const IP_Port *source, const uint8_t *
* return the number of packets sent on success
* return -1 on failure.
*/
+non_null()
static int send_dhtpk_announce(Onion_Client *onion_c, uint16_t friend_num, uint8_t onion_dht_both)
{
if (friend_num >= onion_c->num_friends) {
@@ -1193,14 +1231,13 @@ static int send_dhtpk_announce(Onion_Client *onion_c, uint16_t friend_num, uint8
net_pack_u64(data + 1, no_replay);
memcpy(data + 1 + sizeof(uint64_t), dht_get_self_public_key(onion_c->dht), CRYPTO_PUBLIC_KEY_SIZE);
Node_format nodes[MAX_SENT_NODES];
- uint16_t num_relays = copy_connected_tcp_relays(onion_c->c, nodes, MAX_SENT_NODES / 2);
+ const uint16_t num_relays = copy_connected_tcp_relays(onion_c->c, nodes, MAX_SENT_NODES / 2);
uint16_t num_nodes = closelist_nodes(onion_c->dht, &nodes[num_relays], MAX_SENT_NODES - num_relays);
num_nodes += num_relays;
int nodes_len = 0;
if (num_nodes != 0) {
- nodes_len = pack_nodes(data + DHTPK_DATA_MIN_LENGTH, DHTPK_DATA_MAX_LENGTH - DHTPK_DATA_MIN_LENGTH, nodes,
- num_nodes);
+ nodes_len = pack_nodes(onion_c->logger, data + DHTPK_DATA_MIN_LENGTH, DHTPK_DATA_MAX_LENGTH - DHTPK_DATA_MIN_LENGTH, nodes, num_nodes);
if (nodes_len <= 0) {
return -1;
@@ -1229,7 +1266,7 @@ static int send_dhtpk_announce(Onion_Client *onion_c, uint16_t friend_num, uint8
return num1 + num2;
}
-/** Get the friend_num of a friend.
+/** @brief Get the friend_num of a friend.
*
* return -1 on failure.
* return friend number on success.
@@ -1237,11 +1274,11 @@ static int send_dhtpk_announce(Onion_Client *onion_c, uint16_t friend_num, uint8
int onion_friend_num(const Onion_Client *onion_c, const uint8_t *public_key)
{
for (unsigned int i = 0; i < onion_c->num_friends; ++i) {
- if (onion_c->friends_list[i].status == 0) {
+ if (!onion_c->friends_list[i].is_valid) {
continue;
}
- if (public_key_cmp(public_key, onion_c->friends_list[i].real_public_key) == 0) {
+ if (pk_equal(public_key, onion_c->friends_list[i].real_public_key)) {
return i;
}
}
@@ -1249,11 +1286,12 @@ int onion_friend_num(const Onion_Client *onion_c, const uint8_t *public_key)
return -1;
}
-/** Set the size of the friend list to num.
+/** @brief Set the size of the friend list to num.
*
- * return -1 if realloc fails.
- * return 0 if it succeeds.
+ * @retval -1 if realloc fails.
+ * @retval 0 if it succeeds.
*/
+non_null()
static int realloc_onion_friends(Onion_Client *onion_c, uint32_t num)
{
if (num == 0) {
@@ -1272,14 +1310,14 @@ static int realloc_onion_friends(Onion_Client *onion_c, uint32_t num)
return 0;
}
-/** Add a friend who we want to connect to.
+/** @brief Add a friend who we want to connect to.
*
* return -1 on failure.
* return the friend number on success or if the friend was already added.
*/
int onion_addfriend(Onion_Client *onion_c, const uint8_t *public_key)
{
- int num = onion_friend_num(onion_c, public_key);
+ const int num = onion_friend_num(onion_c, public_key);
if (num != -1) {
return num;
@@ -1288,7 +1326,7 @@ int onion_addfriend(Onion_Client *onion_c, const uint8_t *public_key)
unsigned int index = -1;
for (unsigned int i = 0; i < onion_c->num_friends; ++i) {
- if (onion_c->friends_list[i].status == 0) {
+ if (!onion_c->friends_list[i].is_valid) {
index = i;
break;
}
@@ -1300,17 +1338,17 @@ int onion_addfriend(Onion_Client *onion_c, const uint8_t *public_key)
}
index = onion_c->num_friends;
- memset(&onion_c->friends_list[onion_c->num_friends], 0, sizeof(Onion_Friend));
+ onion_c->friends_list[onion_c->num_friends] = empty_onion_friend;
++onion_c->num_friends;
}
- onion_c->friends_list[index].status = 1;
+ onion_c->friends_list[index].is_valid = true;
memcpy(onion_c->friends_list[index].real_public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
- crypto_new_keypair(onion_c->friends_list[index].temp_public_key, onion_c->friends_list[index].temp_secret_key);
+ crypto_new_keypair(onion_c->rng, onion_c->friends_list[index].temp_public_key, onion_c->friends_list[index].temp_secret_key);
return index;
}
-/** Delete a friend.
+/** @brief Delete a friend.
*
* return -1 on failure.
* return the deleted friend number on success.
@@ -1333,7 +1371,7 @@ int onion_delfriend(Onion_Client *onion_c, int friend_num)
unsigned int i;
for (i = onion_c->num_friends; i != 0; --i) {
- if (onion_c->friends_list[i - 1].status != 0) {
+ if (onion_c->friends_list[i - 1].is_valid) {
break;
}
}
@@ -1346,8 +1384,8 @@ int onion_delfriend(Onion_Client *onion_c, int friend_num)
return friend_num;
}
-/** Set the function for this friend that will be callbacked with object and number
- * when that friends gives us one of the TCP relays he is connected to.
+/** @brief Set the function for this friend that will be callbacked with object and number
+ * when that friend gives us one of the TCP relays they are connected to.
*
* object and number will be passed as argument to this function.
*
@@ -1367,8 +1405,8 @@ int recv_tcp_relay_handler(Onion_Client *onion_c, int friend_num,
return 0;
}
-/** Set the function for this friend that will be callbacked with object and number
- * when that friend gives us his DHT temporary public key.
+/** @brief Set the function for this friend that will be callbacked with object and number
+ * when that friend gives us their DHT temporary public key.
*
* object and number will be passed as argument to this function.
*
@@ -1388,7 +1426,7 @@ int onion_dht_pk_callback(Onion_Client *onion_c, int friend_num,
return 0;
}
-/** Set a friend's DHT public key.
+/** @brief Set a friend's DHT public key.
*
* return -1 on failure.
* return 0 on success.
@@ -1399,24 +1437,23 @@ int onion_set_friend_DHT_pubkey(Onion_Client *onion_c, int friend_num, const uin
return -1;
}
- if (onion_c->friends_list[friend_num].status == 0) {
+ if (!onion_c->friends_list[friend_num].is_valid) {
return -1;
}
if (onion_c->friends_list[friend_num].know_dht_public_key) {
- if (public_key_cmp(dht_key, onion_c->friends_list[friend_num].dht_public_key) == 0) {
+ if (pk_equal(dht_key, onion_c->friends_list[friend_num].dht_public_key)) {
return -1;
}
}
- onion_c->friends_list[friend_num].last_seen = mono_time_get(onion_c->mono_time);
- onion_c->friends_list[friend_num].know_dht_public_key = 1;
+ onion_c->friends_list[friend_num].know_dht_public_key = true;
memcpy(onion_c->friends_list[friend_num].dht_public_key, dht_key, CRYPTO_PUBLIC_KEY_SIZE);
return 0;
}
-/** Copy friends DHT public key into dht_key.
+/** @brief Copy friends DHT public key into dht_key.
*
* return 0 on failure (no key copied).
* return 1 on success (key copied).
@@ -1427,7 +1464,7 @@ unsigned int onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_
return 0;
}
- if (onion_c->friends_list[friend_num].status == 0) {
+ if (!onion_c->friends_list[friend_num].is_valid) {
return 0;
}
@@ -1439,12 +1476,11 @@ unsigned int onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_
return 1;
}
-/** Get the ip of friend friendnum and put it in ip_port
- *
- * return -1, -- if public_key does NOT refer to a friend
- * return 0, -- if public_key refers to a friend and we failed to find the friend (yet)
- * return 1, ip if public_key refers to a friend and we found him
+/** @brief Get the ip of friend friendnum and put it in ip_port
*
+ * @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 them
*/
int onion_getfriendip(const Onion_Client *onion_c, int friend_num, IP_Port *ip_port)
{
@@ -1458,25 +1494,20 @@ int onion_getfriendip(const Onion_Client *onion_c, int friend_num, IP_Port *ip_p
}
-/** Set if friend is online or not.
- * NOTE: This function is there and should be used so that we don't send useless packets to the friend if he is online.
+/** @brief Set if friend is online or not.
*
- * is_online 1 means friend is online.
- * is_online 0 means friend is offline
+ * NOTE: This function is there and should be used so that we don't send
+ * useless packets to the friend if they are online.
*
* return -1 on failure.
* return 0 on success.
*/
-int onion_set_friend_online(Onion_Client *onion_c, int friend_num, uint8_t is_online)
+int onion_set_friend_online(Onion_Client *onion_c, int friend_num, bool is_online)
{
if ((uint32_t)friend_num >= onion_c->num_friends) {
return -1;
}
- if (is_online == 0 && onion_c->friends_list[friend_num].is_online == 1) {
- onion_c->friends_list[friend_num].last_seen = mono_time_get(onion_c->mono_time);
- }
-
onion_c->friends_list[friend_num].is_online = is_online;
/* This should prevent some clock related issues */
@@ -1488,148 +1519,149 @@ int onion_set_friend_online(Onion_Client *onion_c, int friend_num, uint8_t is_on
return 0;
}
+non_null()
static void populate_path_nodes(Onion_Client *onion_c)
{
- Node_format nodes_list[MAX_FRIEND_CLIENTS];
+ Node_format node_list[MAX_FRIEND_CLIENTS];
- unsigned int num_nodes = randfriends_nodes(onion_c->dht, nodes_list, MAX_FRIEND_CLIENTS);
+ const unsigned int num_nodes = randfriends_nodes(onion_c->dht, node_list, MAX_FRIEND_CLIENTS);
for (unsigned int i = 0; i < num_nodes; ++i) {
- onion_add_path_node(onion_c, &nodes_list[i].ip_port, nodes_list[i].public_key);
+ onion_add_path_node(onion_c, &node_list[i].ip_port, node_list[i].public_key);
}
}
-static void populate_path_nodes_tcp(Onion_Client *onion_c)
-{
- Node_format nodes_list[MAX_SENT_NODES];
-
- unsigned int num_nodes = copy_connected_tcp_relays(onion_c->c, nodes_list, MAX_SENT_NODES);
+/* How often we ping new friends per node */
+#define ANNOUNCE_FRIEND_NEW_INTERVAL 3
- for (unsigned int i = 0; i < num_nodes; ++i) {
- onion_add_bs_path_node(onion_c, &nodes_list[i].ip_port, nodes_list[i].public_key);
- }
-}
+/* How long we consider a friend new based on the value of their run_count */
+#define ANNOUNCE_FRIEND_RUN_COUNT_BEGINNING 5
-#define ANNOUNCE_FRIEND (ONION_NODE_PING_INTERVAL * 6)
-#define ANNOUNCE_FRIEND_BEGINNING 3
+/* How often we try to re-populate the nodes lists if we don't meet a minimum threshhold of nodes */
+#define ANNOUNCE_POPULATE_TIMEOUT (60 * 10)
-#define RUN_COUNT_FRIEND_ANNOUNCE_BEGINNING 17
+/* The max time between lookup requests for a friend per node */
+#define ANNOUNCE_FRIEND_MAX_INTERVAL (60 * 60)
-#define ONION_FRIEND_BACKOFF_FACTOR 4
-#define ONION_FRIEND_MAX_PING_INTERVAL (5*60*MAX_ONION_CLIENTS)
+/* Max exponent when calculating the announce request interval */
+#define MAX_RUN_COUNT_EXPONENT 12
+non_null()
static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
{
if (friendnum >= onion_c->num_friends) {
return;
}
- if (onion_c->friends_list[friendnum].status == 0) {
+ Onion_Friend *o_friend = &onion_c->friends_list[friendnum];
+
+ if (!o_friend->is_valid) {
return;
}
- unsigned int interval = ANNOUNCE_FRIEND;
+ uint32_t interval;
+ const uint64_t tm = mono_time_get(onion_c->mono_time);
+ const bool friend_is_new = o_friend->run_count <= ANNOUNCE_FRIEND_RUN_COUNT_BEGINNING;
- if (onion_c->friends_list[friendnum].run_count < RUN_COUNT_FRIEND_ANNOUNCE_BEGINNING) {
- interval = ANNOUNCE_FRIEND_BEGINNING;
+ if (!friend_is_new) {
+ // how often we ping a node for a friend depends on how many times we've already tried.
+ // the interval increases exponentially, as the longer a friend has been offline, the less
+ // likely the case is that they're online and failed to find us
+ const uint32_t c = 1 << min_u32(MAX_RUN_COUNT_EXPONENT, o_friend->run_count - 2);
+ interval = min_u32(c, ANNOUNCE_FRIEND_MAX_INTERVAL);
} else {
- if (onion_c->friends_list[friendnum].last_seen == 0) {
- onion_c->friends_list[friendnum].last_seen = mono_time_get(onion_c->mono_time);
- }
+ interval = ANNOUNCE_FRIEND_NEW_INTERVAL;
+ }
+
+ if (o_friend->is_online) {
+ return;
+ }
- uint64_t backoff_interval = (mono_time_get(onion_c->mono_time) -
- onion_c->friends_list[friendnum].last_seen)
- / ONION_FRIEND_BACKOFF_FACTOR;
+ assert(interval >= ANNOUNCE_FRIEND_NEW_INTERVAL); // an int overflow would be devastating
- if (backoff_interval > ONION_FRIEND_MAX_PING_INTERVAL) {
- backoff_interval = ONION_FRIEND_MAX_PING_INTERVAL;
+ /* send packets to friend telling them our DHT public key. */
+ if (mono_time_is_timeout(onion_c->mono_time, onion_c->friends_list[friendnum].last_dht_pk_onion_sent,
+ ONION_DHTPK_SEND_INTERVAL)) {
+ if (send_dhtpk_announce(onion_c, friendnum, 0) >= 1) {
+ onion_c->friends_list[friendnum].last_dht_pk_onion_sent = tm;
}
+ }
- if (interval < backoff_interval) {
- interval = backoff_interval;
+ if (mono_time_is_timeout(onion_c->mono_time, onion_c->friends_list[friendnum].last_dht_pk_dht_sent,
+ DHT_DHTPK_SEND_INTERVAL)) {
+ if (send_dhtpk_announce(onion_c, friendnum, 1) >= 1) {
+ onion_c->friends_list[friendnum].last_dht_pk_dht_sent = tm;
}
}
- if (!onion_c->friends_list[friendnum].is_online) {
- unsigned int count = 0;
- Onion_Node *list_nodes = onion_c->friends_list[friendnum].clients_list;
+ uint16_t count = 0; // number of alive path nodes
- // ensure we get a response from some node roughly once per
- // (interval / MAX_ONION_CLIENTS)
- bool ping_random = true;
+ Onion_Node *node_list = o_friend->clients_list;
- for (unsigned i = 0; i < MAX_ONION_CLIENTS; ++i) {
- if (!(mono_time_is_timeout(onion_c->mono_time, list_nodes[i].timestamp, interval / MAX_ONION_CLIENTS)
- && mono_time_is_timeout(onion_c->mono_time, list_nodes[i].last_pinged, ONION_NODE_PING_INTERVAL))) {
- ping_random = false;
- break;
- }
+ for (unsigned i = 0; i < MAX_ONION_CLIENTS; ++i) {
+ if (onion_node_timed_out(&node_list[i], onion_c->mono_time)) {
+ continue;
}
- for (unsigned i = 0; i < MAX_ONION_CLIENTS; ++i) {
- if (onion_node_timed_out(&list_nodes[i], onion_c->mono_time)) {
- continue;
- }
+ ++count;
+
+ // we don't want new nodes to be pinged immediately
+ if (node_list[i].last_pinged == 0) {
+ node_list[i].last_pinged = tm;
+ continue;
+ }
- ++count;
+ // node hasn't responded in a while so we skip it
+ if (node_list[i].pings_since_last_response >= ONION_NODE_MAX_PINGS) {
+ continue;
+ }
+ // space requests out between nodes
+ if (!mono_time_is_timeout(onion_c->mono_time, o_friend->time_last_pinged, interval / (MAX_ONION_CLIENTS / 2))) {
+ continue;
+ }
- if (list_nodes[i].last_pinged == 0) {
- list_nodes[i].last_pinged = mono_time_get(onion_c->mono_time);
- continue;
- }
+ if (!mono_time_is_timeout(onion_c->mono_time, node_list[i].last_pinged, interval)) {
+ continue;
+ }
- if (list_nodes[i].unsuccessful_pings >= ONION_NODE_MAX_PINGS) {
- continue;
- }
+ if (client_send_announce_request(onion_c, friendnum + 1, &node_list[i].ip_port,
+ node_list[i].public_key, nullptr, -1) == 0) {
+ node_list[i].last_pinged = tm;
+ o_friend->time_last_pinged = tm;
+ ++node_list[i].pings_since_last_response;
+ ++o_friend->pings;
- if (mono_time_is_timeout(onion_c->mono_time, list_nodes[i].last_pinged, interval)
- || (ping_random && random_range_u32(MAX_ONION_CLIENTS - i) == 0)) {
- if (client_send_announce_request(onion_c, friendnum + 1, &list_nodes[i].ip_port,
- list_nodes[i].public_key, nullptr, -1) == 0) {
- list_nodes[i].last_pinged = mono_time_get(onion_c->mono_time);
- ++list_nodes[i].unsuccessful_pings;
- ping_random = false;
- }
+ if (o_friend->pings % (MAX_ONION_CLIENTS / 2) == 0) {
+ ++o_friend->run_count;
}
}
+ }
- if (count != MAX_ONION_CLIENTS) {
- const uint16_t num_nodes = min_u16(onion_c->path_nodes_index, MAX_PATH_NODES);
- uint16_t n = num_nodes;
+ if (count == MAX_ONION_CLIENTS) {
+ if (!friend_is_new) {
+ o_friend->last_populated = tm;
+ }
- if (num_nodes > (MAX_ONION_CLIENTS / 2)) {
- n = (MAX_ONION_CLIENTS / 2);
- }
+ return;
+ }
- if (count <= random_range_u32(MAX_ONION_CLIENTS)) {
- if (num_nodes != 0) {
- for (unsigned int j = 0; j < n; ++j) {
- const uint32_t num = random_range_u32(num_nodes);
- client_send_announce_request(onion_c, friendnum + 1, &onion_c->path_nodes[num].ip_port,
- onion_c->path_nodes[num].public_key, nullptr, -1);
- }
+ // check if path nodes list for this friend needs to be repopulated
+ if (count <= MAX_ONION_CLIENTS / 2
+ || mono_time_is_timeout(onion_c->mono_time, o_friend->last_populated, ANNOUNCE_POPULATE_TIMEOUT)) {
+ const uint16_t num_nodes = min_u16(onion_c->path_nodes_index, MAX_PATH_NODES);
+ const uint16_t n = min_u16(num_nodes, MAX_PATH_NODES / 4);
- ++onion_c->friends_list[friendnum].run_count;
- }
- }
- } else {
- ++onion_c->friends_list[friendnum].run_count;
+ if (n == 0) {
+ return;
}
- /* send packets to friend telling them our DHT public key. */
- if (mono_time_is_timeout(onion_c->mono_time, onion_c->friends_list[friendnum].last_dht_pk_onion_sent,
- ONION_DHTPK_SEND_INTERVAL)) {
- if (send_dhtpk_announce(onion_c, friendnum, 0) >= 1) {
- onion_c->friends_list[friendnum].last_dht_pk_onion_sent = mono_time_get(onion_c->mono_time);
- }
- }
+ o_friend->last_populated = tm;
- if (mono_time_is_timeout(onion_c->mono_time, onion_c->friends_list[friendnum].last_dht_pk_dht_sent,
- DHT_DHTPK_SEND_INTERVAL)) {
- if (send_dhtpk_announce(onion_c, friendnum, 1) >= 1) {
- onion_c->friends_list[friendnum].last_dht_pk_dht_sent = mono_time_get(onion_c->mono_time);
- }
+ for (uint16_t i = 0; i < n; ++i) {
+ const uint32_t num = random_range_u32(onion_c->rng, num_nodes);
+ client_send_announce_request(onion_c, friendnum + 1, &onion_c->path_nodes[num].ip_port,
+ onion_c->path_nodes[num].public_key, nullptr, -1);
}
}
}
@@ -1648,43 +1680,45 @@ void oniondata_registerhandler(Onion_Client *onion_c, uint8_t byte, oniondata_ha
#define TIME_TO_STABLE (ONION_NODE_PING_INTERVAL * 6)
#define ANNOUNCE_INTERVAL_STABLE (ONION_NODE_PING_INTERVAL * 8)
+non_null()
static void do_announce(Onion_Client *onion_c)
{
unsigned int count = 0;
- Onion_Node *list_nodes = onion_c->clients_announce_list;
+ Onion_Node *node_list = onion_c->clients_announce_list;
for (unsigned int i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) {
- if (onion_node_timed_out(&list_nodes[i], onion_c->mono_time)) {
+ if (onion_node_timed_out(&node_list[i], onion_c->mono_time)) {
continue;
}
++count;
/* Don't announce ourselves the first time this is run to new peers */
- if (list_nodes[i].last_pinged == 0) {
- list_nodes[i].last_pinged = 1;
+ if (node_list[i].last_pinged == 0) {
+ node_list[i].last_pinged = 1;
continue;
}
- if (list_nodes[i].unsuccessful_pings >= ONION_NODE_MAX_PINGS) {
+ if (node_list[i].pings_since_last_response >= ONION_NODE_MAX_PINGS) {
continue;
}
unsigned int interval = ANNOUNCE_INTERVAL_NOT_ANNOUNCED;
- if (list_nodes[i].is_stored && path_exists(onion_c->mono_time, &onion_c->onion_paths_self, list_nodes[i].path_used)) {
+ if (node_list[i].is_stored != 0
+ && path_exists(onion_c->mono_time, &onion_c->onion_paths_self, node_list[i].path_used)) {
interval = ANNOUNCE_INTERVAL_ANNOUNCED;
- uint32_t pathnum = list_nodes[i].path_used % NUMBER_ONION_PATHS;
+ const uint32_t pathnum = node_list[i].path_used % NUMBER_ONION_PATHS;
/* A node/path is considered "stable", and can be pinged less
* aggressively, if it has survived for at least TIME_TO_STABLE
* and the latest packets sent to it are not timing out.
*/
- if (mono_time_is_timeout(onion_c->mono_time, list_nodes[i].added_time, TIME_TO_STABLE)
- && !(list_nodes[i].unsuccessful_pings > 0
- && mono_time_is_timeout(onion_c->mono_time, list_nodes[i].last_pinged, ONION_NODE_TIMEOUT))
+ if (mono_time_is_timeout(onion_c->mono_time, node_list[i].added_time, TIME_TO_STABLE)
+ && !(node_list[i].pings_since_last_response > 0
+ && mono_time_is_timeout(onion_c->mono_time, node_list[i].last_pinged, ONION_NODE_TIMEOUT))
&& mono_time_is_timeout(onion_c->mono_time, onion_c->onion_paths_self.path_creation_time[pathnum], TIME_TO_STABLE)
&& !(onion_c->onion_paths_self.last_path_used_times[pathnum] > 0
&& mono_time_is_timeout(onion_c->mono_time, onion_c->onion_paths_self.last_path_used[pathnum], ONION_PATH_TIMEOUT))) {
@@ -1692,31 +1726,37 @@ static void do_announce(Onion_Client *onion_c)
}
}
- if (mono_time_is_timeout(onion_c->mono_time, list_nodes[i].last_pinged, interval)
- || (mono_time_is_timeout(onion_c->mono_time, onion_c->last_announce, ONION_NODE_PING_INTERVAL)
- && random_range_u32(MAX_ONION_CLIENTS_ANNOUNCE - i) == 0)) {
- uint32_t path_to_use = list_nodes[i].path_used;
+ if (mono_time_is_timeout(onion_c->mono_time, node_list[i].last_pinged, interval)
+ || mono_time_is_timeout(onion_c->mono_time, onion_c->last_announce, ONION_NODE_PING_INTERVAL)) {
+ uint32_t path_to_use = node_list[i].path_used;
- if (list_nodes[i].unsuccessful_pings == ONION_NODE_MAX_PINGS - 1
- && mono_time_is_timeout(onion_c->mono_time, list_nodes[i].added_time, TIME_TO_STABLE)) {
+ if (node_list[i].pings_since_last_response == ONION_NODE_MAX_PINGS - 1
+ && mono_time_is_timeout(onion_c->mono_time, node_list[i].added_time, TIME_TO_STABLE)) {
/* Last chance for a long-lived node - try a random path */
path_to_use = -1;
}
- if (client_send_announce_request(onion_c, 0, &list_nodes[i].ip_port, list_nodes[i].public_key,
- list_nodes[i].ping_id, path_to_use) == 0) {
- list_nodes[i].last_pinged = mono_time_get(onion_c->mono_time);
- ++list_nodes[i].unsuccessful_pings;
+ if (client_send_announce_request(onion_c, 0, &node_list[i].ip_port, node_list[i].public_key,
+ node_list[i].ping_id, path_to_use) == 0) {
+ node_list[i].last_pinged = mono_time_get(onion_c->mono_time);
+ ++node_list[i].pings_since_last_response;
onion_c->last_announce = mono_time_get(onion_c->mono_time);
}
}
}
- if (count != MAX_ONION_CLIENTS_ANNOUNCE) {
+ if (count == MAX_ONION_CLIENTS_ANNOUNCE) {
+ onion_c->last_populated = mono_time_get(onion_c->mono_time);
+ return;
+ }
+
+ // check if list needs to be re-populated
+ if (count <= MAX_ONION_CLIENTS_ANNOUNCE / 2
+ || mono_time_is_timeout(onion_c->mono_time, onion_c->last_populated, ANNOUNCE_POPULATE_TIMEOUT)) {
uint16_t num_nodes;
const Node_format *path_nodes;
- if (random_u08() % 2 == 0 || onion_c->path_nodes_index == 0) {
+ if (onion_c->path_nodes_index == 0) {
num_nodes = min_u16(onion_c->path_nodes_index_bs, MAX_PATH_NODES);
path_nodes = onion_c->path_nodes_bs;
} else {
@@ -1724,38 +1764,42 @@ static void do_announce(Onion_Client *onion_c)
path_nodes = onion_c->path_nodes;
}
- if (count <= random_range_u32(MAX_ONION_CLIENTS_ANNOUNCE)) {
- if (num_nodes != 0) {
- for (unsigned int i = 0; i < (MAX_ONION_CLIENTS_ANNOUNCE / 2); ++i) {
- const uint32_t num = random_range_u32(num_nodes);
- client_send_announce_request(onion_c, 0, &path_nodes[num].ip_port, path_nodes[num].public_key, nullptr, -1);
- }
- }
+ if (num_nodes == 0) {
+ return;
+ }
+
+ for (unsigned int i = 0; i < (MAX_ONION_CLIENTS_ANNOUNCE / 2); ++i) {
+ const uint32_t num = random_range_u32(onion_c->rng, num_nodes);
+ client_send_announce_request(onion_c, 0, &path_nodes[num].ip_port, path_nodes[num].public_key, nullptr, -1);
}
}
}
-/** return 0 if we are not connected to the network.
- * return 1 if we are.
+/**
+ * @retval false if we are not connected to the network.
+ * @retval true if we are.
*/
-static int onion_isconnected(const Onion_Client *onion_c)
+non_null()
+static bool onion_isconnected(Onion_Client *onion_c)
{
unsigned int num = 0;
unsigned int announced = 0;
if (mono_time_is_timeout(onion_c->mono_time, onion_c->last_packet_recv, ONION_OFFLINE_TIMEOUT)) {
- return 0;
+ onion_c->last_populated = 0;
+ return false;
}
if (onion_c->path_nodes_index == 0) {
- return 0;
+ onion_c->last_populated = 0;
+ return false;
}
for (unsigned int i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) {
if (!onion_node_timed_out(&onion_c->clients_announce_list[i], onion_c->mono_time)) {
++num;
- if (onion_c->clients_announce_list[i].is_stored) {
+ if (onion_c->clients_announce_list[i].is_stored != 0) {
++announced;
}
}
@@ -1769,32 +1813,43 @@ static int onion_isconnected(const Onion_Client *onion_c)
/* Consider ourselves online if we are announced to half or more nodes
* we are connected to */
- if (num && announced) {
+ if (num != 0 && announced != 0) {
if ((num / 2) <= announced && (pnodes / 2) <= num) {
- return 1;
+ return true;
}
}
- return 0;
+ onion_c->last_populated = 0;
+
+ return false;
+}
+
+non_null()
+static void reset_friend_run_counts(Onion_Client *onion_c)
+{
+ for (uint16_t i = 0; i < onion_c->num_friends; ++i) {
+ Onion_Friend *o_friend = &onion_c->friends_list[i];
+
+ if (o_friend->is_valid) {
+ o_friend->run_count = 0;
+ }
+ }
}
#define ONION_CONNECTION_SECONDS 3
+#define ONION_CONNECTED_TIMEOUT 10
-/** return 0 if we are not connected to the network.
- * return 1 if we are connected with TCP only.
- * return 2 if we are also connected with UDP.
- */
-unsigned int onion_connection_status(const Onion_Client *onion_c)
+Onion_Connection_Status onion_connection_status(const Onion_Client *onion_c)
{
if (onion_c->onion_connected >= ONION_CONNECTION_SECONDS) {
if (onion_c->udp_connected) {
- return 2;
+ return ONION_CONNECTION_STATUS_UDP;
}
- return 1;
+ return ONION_CONNECTION_STATUS_TCP;
}
- return 0;
+ return ONION_CONNECTION_STATUS_NONE;
}
void do_onion_client(Onion_Client *onion_c)
@@ -1809,12 +1864,16 @@ void do_onion_client(Onion_Client *onion_c)
}
if (onion_isconnected(onion_c)) {
+ if (mono_time_is_timeout(onion_c->mono_time, onion_c->last_time_connected, ONION_CONNECTED_TIMEOUT)) {
+ reset_friend_run_counts(onion_c);
+ }
+
+ onion_c->last_time_connected = mono_time_get(onion_c->mono_time);
+
if (onion_c->onion_connected < ONION_CONNECTION_SECONDS * 2) {
++onion_c->onion_connected;
}
} else {
- populate_path_nodes_tcp(onion_c);
-
if (onion_c->onion_connected != 0) {
--onion_c->onion_connected;
}
@@ -1826,7 +1885,7 @@ void do_onion_client(Onion_Client *onion_c)
set_tcp_onion_status(nc_get_tcp_c(onion_c->c), !onion_c->udp_connected);
}
- if (onion_connection_status(onion_c)) {
+ if (onion_connection_status(onion_c) != ONION_CONNECTION_STATUS_NONE) {
for (unsigned i = 0; i < onion_c->num_friends; ++i) {
do_friend(onion_c, i);
}
@@ -1839,7 +1898,7 @@ void do_onion_client(Onion_Client *onion_c)
onion_c->last_run = mono_time_get(onion_c->mono_time);
}
-Onion_Client *new_onion_client(const Logger *logger, Mono_Time *mono_time, Net_Crypto *c)
+Onion_Client *new_onion_client(const Logger *logger, const Random *rng, const Mono_Time *mono_time, Net_Crypto *c)
{
if (c == nullptr) {
return nullptr;
@@ -1860,12 +1919,13 @@ Onion_Client *new_onion_client(const Logger *logger, Mono_Time *mono_time, Net_C
onion_c->mono_time = mono_time;
onion_c->logger = logger;
+ onion_c->rng = rng;
onion_c->dht = nc_get_dht(c);
onion_c->net = dht_get_net(onion_c->dht);
onion_c->c = c;
- new_symmetric_key(onion_c->secret_symmetric_key);
- crypto_new_keypair(onion_c->temp_public_key, onion_c->temp_secret_key);
- networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_announce_response, onion_c);
+ new_symmetric_key(rng, onion_c->secret_symmetric_key);
+ crypto_new_keypair(rng, onion_c->temp_public_key, onion_c->temp_secret_key);
+ networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE_OLD, &handle_announce_response, onion_c);
networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, &handle_data_response, onion_c);
oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, &handle_dhtpk_announce, onion_c);
cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_DHTPK, &handle_dht_dhtpk, onion_c);
@@ -1882,7 +1942,7 @@ void kill_onion_client(Onion_Client *onion_c)
ping_array_kill(onion_c->announce_ping_array);
realloc_onion_friends(onion_c, 0);
- networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, nullptr, nullptr);
+ networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE_OLD, nullptr, nullptr);
networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, nullptr, nullptr);
oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, nullptr, nullptr);
cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_DHTPK, nullptr, nullptr);
diff --git a/protocols/Tox/libtox/src/toxcore/onion_client.h b/protocols/Tox/libtox/src/toxcore/onion_client.h
index c724e97344..2a30005c69 100644
--- a/protocols/Tox/libtox/src/toxcore/onion_client.h
+++ b/protocols/Tox/libtox/src/toxcore/onion_client.h
@@ -10,6 +10,8 @@
#ifndef C_TOXCORE_TOXCORE_ONION_CLIENT_H
#define C_TOXCORE_TOXCORE_ONION_CLIENT_H
+#include <stdbool.h>
+
#include "net_crypto.h"
#include "onion_announce.h"
#include "ping_array.h"
@@ -25,8 +27,10 @@
#define NUMBER_ONION_PATHS 6
-/** The timeout the first time the path is added and
- * then for all the next consecutive times */
+/**
+ * The timeout the first time the path is added and
+ * then for all the next consecutive times
+ */
#define ONION_PATH_FIRST_TIMEOUT 4
#define ONION_PATH_TIMEOUT 10
#define ONION_PATH_MAX_LIFETIME 1200
@@ -39,7 +43,8 @@
#define MAX_PATH_NODES 32
-/** If no announce response packets are received within this interval tox will
+/**
+ * If no announce response packets are received within this interval tox will
* be considered offline. We give time for a node to be pinged often enough
* that it times out, which leads to the network being thoroughly tested as it
* is replaced.
@@ -52,107 +57,126 @@
typedef struct Onion_Client Onion_Client;
+non_null()
DHT *onion_get_dht(const Onion_Client *onion_c);
+non_null()
Net_Crypto *onion_get_net_crypto(const Onion_Client *onion_c);
-/** Add a node to the path_nodes bootstrap array.
+/** @brief Add a node to the path_nodes bootstrap array.
+ *
+ * If a node with the given public key was already in the bootstrap array, this function has no
+ * effect and returns successfully. There is currently no way to update the IP/port for a bootstrap
+ * node, so if it changes, the Onion_Client must be recreated.
*
- * return -1 on failure
- * return 0 on success
+ * @param onion_c The onion client object.
+ * @param ip_port IP/port for the bootstrap node.
+ * @param public_key DHT public key for the bootstrap node.
+ *
+ * @retval false on failure
+ * @retval true on success
*/
-int onion_add_bs_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const uint8_t *public_key);
+non_null()
+bool onion_add_bs_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const uint8_t *public_key);
-/** Put up to max_num nodes in nodes.
+/** @brief Put up to max_num nodes in nodes.
*
* return the number of nodes.
*/
+non_null()
uint16_t onion_backup_nodes(const Onion_Client *onion_c, Node_format *nodes, uint16_t max_num);
-/** Get the friend_num of a friend.
+/** @brief Get the friend_num of a friend.
*
* return -1 on failure.
* return friend number on success.
*/
+non_null()
int onion_friend_num(const Onion_Client *onion_c, const uint8_t *public_key);
-/** Add a friend who we want to connect to.
+/** @brief Add a friend who we want to connect to.
*
* return -1 on failure.
* return the friend number on success or if the friend was already added.
*/
+non_null()
int onion_addfriend(Onion_Client *onion_c, const uint8_t *public_key);
-/** Delete a friend.
+/** @brief Delete a friend.
*
* return -1 on failure.
* return the deleted friend number on success.
*/
+non_null()
int onion_delfriend(Onion_Client *onion_c, int friend_num);
-/** Set if friend is online or not.
- * NOTE: This function is there and should be used so that we don't send useless packets to the friend if he is online.
+/** @brief Set if friend is online or not.
*
- * is_online 1 means friend is online.
- * is_online 0 means friend is offline
+ * NOTE: This function is there and should be used so that we don't send
+ * useless packets to the friend if they are online.
*
* return -1 on failure.
* return 0 on success.
*/
-int onion_set_friend_online(Onion_Client *onion_c, int friend_num, uint8_t is_online);
+non_null()
+int onion_set_friend_online(Onion_Client *onion_c, int friend_num, bool is_online);
-/** Get the ip of friend friendnum and put it in ip_port
- *
- * return -1, -- if public_key does NOT refer to a friend
- * return 0, -- if public_key refers to a friend and we failed to find the friend (yet)
- * return 1, ip if public_key refers to a friend and we found him
+/** @brief Get the ip of friend friendnum and put it in ip_port
*
+ * @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 them
*/
+non_null()
int onion_getfriendip(const Onion_Client *onion_c, int friend_num, IP_Port *ip_port);
typedef int recv_tcp_relay_cb(void *object, uint32_t number, const IP_Port *ip_port, const uint8_t *public_key);
-/** Set the function for this friend that will be callbacked with object and number
- * when that friends gives us one of the TCP relays he is connected to.
+/** @brief Set the function for this friend that will be callbacked with object and number
+ * when that friend gives us one of the TCP relays they are connected to.
*
* object and number will be passed as argument to this function.
*
* return -1 on failure.
* return 0 on success.
*/
+non_null()
int recv_tcp_relay_handler(Onion_Client *onion_c, int friend_num,
recv_tcp_relay_cb *callback, void *object, uint32_t number);
typedef void onion_dht_pk_cb(void *data, int32_t number, const uint8_t *dht_public_key, void *userdata);
-/** Set the function for this friend that will be callbacked with object and number
- * when that friend gives us his DHT temporary public key.
+/** @brief Set the function for this friend that will be callbacked with object and number
+ * when that friend gives us their DHT temporary public key.
*
* object and number will be passed as argument to this function.
*
* return -1 on failure.
* return 0 on success.
*/
+non_null()
int onion_dht_pk_callback(Onion_Client *onion_c, int friend_num, onion_dht_pk_cb *function, void *object,
uint32_t number);
-/** Set a friend's DHT public key.
+/** @brief Set a friend's DHT public key.
*
* return -1 on failure.
* return 0 on success.
*/
+non_null()
int onion_set_friend_DHT_pubkey(Onion_Client *onion_c, int friend_num, const uint8_t *dht_key);
-/** Copy friends DHT public key into dht_key.
+/** @brief Copy friends DHT public key into dht_key.
*
* return 0 on failure (no key copied).
* return 1 on success (key copied).
*/
+non_null()
unsigned int onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_num, uint8_t *dht_key);
#define ONION_DATA_IN_RESPONSE_MIN_SIZE (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE)
#define ONION_CLIENT_MAX_DATA_SIZE (MAX_DATA_REQUEST_SIZE - ONION_DATA_IN_RESPONSE_MIN_SIZE)
-/** Send data of length length to friendnum.
+/** @brief Send data of length length to friendnum.
* Maximum length of data is ONION_CLIENT_MAX_DATA_SIZE.
* This data will be received by the friend using the Onion_Data_Handlers callbacks.
*
@@ -161,25 +185,36 @@ unsigned int onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_
* return the number of packets sent on success
* return -1 on failure.
*/
+non_null()
int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data, uint16_t length);
typedef int oniondata_handler_cb(void *object, const uint8_t *source_pubkey, const uint8_t *data,
uint16_t len, void *userdata);
/** Function to call when onion data packet with contents beginning with byte is received. */
+non_null(1) nullable(3, 4)
void oniondata_registerhandler(Onion_Client *onion_c, uint8_t byte, oniondata_handler_cb *cb, void *object);
+non_null()
void do_onion_client(Onion_Client *onion_c);
-Onion_Client *new_onion_client(const Logger *logger, Mono_Time *mono_time, Net_Crypto *c);
+non_null()
+Onion_Client *new_onion_client(const Logger *logger, const Random *rng, const Mono_Time *mono_time, Net_Crypto *c);
+nullable(1)
void kill_onion_client(Onion_Client *onion_c);
-/** return 0 if we are not connected to the network.
- * return 1 if we are connected with TCP only.
- * return 2 if we are also connected with UDP.
- */
-unsigned int onion_connection_status(const Onion_Client *onion_c);
+typedef enum Onion_Connection_Status {
+ /** We are not connected to the network. */
+ ONION_CONNECTION_STATUS_NONE = 0,
+ /** We are connected with TCP only. */
+ ONION_CONNECTION_STATUS_TCP = 1,
+ /** We are also connected with UDP. */
+ ONION_CONNECTION_STATUS_UDP = 2,
+} Onion_Connection_Status;
+
+non_null()
+Onion_Connection_Status onion_connection_status(const Onion_Client *onion_c);
#endif
diff --git a/protocols/Tox/libtox/src/toxcore/ping.c b/protocols/Tox/libtox/src/toxcore/ping.c
index 249d4a0647..91a780b123 100644
--- a/protocols/Tox/libtox/src/toxcore/ping.c
+++ b/protocols/Tox/libtox/src/toxcore/ping.c
@@ -13,6 +13,7 @@
#include <string.h>
#include "DHT.h"
+#include "ccompat.h"
#include "mono_time.h"
#include "network.h"
#include "ping_array.h"
@@ -29,6 +30,7 @@
struct Ping {
const Mono_Time *mono_time;
+ const Random *rng;
DHT *dht;
Ping_Array *ping_array;
@@ -47,7 +49,7 @@ void ping_send_request(Ping *ping, const IP_Port *ipp, const uint8_t *public_key
int rc;
uint64_t ping_id;
- if (id_equal(public_key, dht_get_self_public_key(ping->dht))) {
+ if (pk_equal(public_key, dht_get_self_public_key(ping->dht))) {
return;
}
@@ -57,9 +59,9 @@ void ping_send_request(Ping *ping, const IP_Port *ipp, const uint8_t *public_key
dht_get_shared_key_sent(ping->dht, shared_key, public_key);
// Generate random ping_id.
uint8_t data[PING_DATA_SIZE];
- id_copy(data, public_key);
+ pk_copy(data, public_key);
memcpy(data + CRYPTO_PUBLIC_KEY_SIZE, ipp, sizeof(IP_Port));
- ping_id = ping_array_add(ping->ping_array, ping->mono_time, data, sizeof(data));
+ ping_id = ping_array_add(ping->ping_array, ping->mono_time, ping->rng, data, sizeof(data));
if (ping_id == 0) {
crypto_memzero(shared_key, sizeof(shared_key));
@@ -71,8 +73,8 @@ void ping_send_request(Ping *ping, const IP_Port *ipp, const uint8_t *public_key
memcpy(ping_plain + 1, &ping_id, sizeof(ping_id));
pk[0] = NET_PACKET_PING_REQUEST;
- id_copy(pk + 1, dht_get_self_public_key(ping->dht)); // Our pubkey
- random_nonce(pk + 1 + CRYPTO_PUBLIC_KEY_SIZE); // Generate new nonce
+ pk_copy(pk + 1, dht_get_self_public_key(ping->dht)); // Our pubkey
+ random_nonce(ping->rng, pk + 1 + CRYPTO_PUBLIC_KEY_SIZE); // Generate new nonce
rc = encrypt_data_symmetric(shared_key,
@@ -90,12 +92,13 @@ void ping_send_request(Ping *ping, const IP_Port *ipp, const uint8_t *public_key
sendpacket(dht_get_net(ping->dht), ipp, pk, sizeof(pk));
}
+non_null()
static int ping_send_response(const Ping *ping, const IP_Port *ipp, const uint8_t *public_key,
uint64_t ping_id, const uint8_t *shared_encryption_key)
{
uint8_t pk[DHT_PING_SIZE];
- if (id_equal(public_key, dht_get_self_public_key(ping->dht))) {
+ if (pk_equal(public_key, dht_get_self_public_key(ping->dht))) {
return 1;
}
@@ -104,8 +107,8 @@ static int ping_send_response(const Ping *ping, const IP_Port *ipp, const uint8_
memcpy(ping_plain + 1, &ping_id, sizeof(ping_id));
pk[0] = NET_PACKET_PING_RESPONSE;
- id_copy(pk + 1, dht_get_self_public_key(ping->dht)); // Our pubkey
- random_nonce(pk + 1 + CRYPTO_PUBLIC_KEY_SIZE); // Generate new nonce
+ pk_copy(pk + 1, dht_get_self_public_key(ping->dht)); // Our pubkey
+ random_nonce(ping->rng, pk + 1 + CRYPTO_PUBLIC_KEY_SIZE); // Generate new nonce
// Encrypt ping_id using recipient privkey
const int rc = encrypt_data_symmetric(shared_encryption_key,
@@ -120,6 +123,7 @@ static int ping_send_response(const Ping *ping, const IP_Port *ipp, const uint8_
return sendpacket(dht_get_net(ping->dht), ipp, pk, sizeof(pk));
}
+non_null()
static int handle_ping_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
@@ -131,7 +135,7 @@ static int handle_ping_request(void *object, const IP_Port *source, const uint8_
Ping *ping = dht_get_ping(dht);
- if (id_equal(packet + 1, dht_get_self_public_key(ping->dht))) {
+ if (pk_equal(packet + 1, dht_get_self_public_key(ping->dht))) {
return 1;
}
@@ -167,6 +171,7 @@ static int handle_ping_request(void *object, const IP_Port *source, const uint8_
return 0;
}
+non_null()
static int handle_ping_response(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
void *userdata)
{
@@ -179,7 +184,7 @@ static int handle_ping_response(void *object, const IP_Port *source, const uint8
Ping *ping = dht_get_ping(dht);
- if (id_equal(packet + 1, dht_get_self_public_key(ping->dht))) {
+ if (pk_equal(packet + 1, dht_get_self_public_key(ping->dht))) {
return 1;
}
@@ -214,7 +219,7 @@ static int handle_ping_response(void *object, const IP_Port *source, const uint8
return 1;
}
- if (!id_equal(packet + 1, data)) {
+ if (!pk_equal(packet + 1, data)) {
return 1;
}
@@ -229,16 +234,17 @@ static int handle_ping_response(void *object, const IP_Port *source, const uint8
return 0;
}
-/** Check if public_key with ip_port is in the list.
+/** @brief Check if public_key with ip_port is in the list.
*
- * return 1 if it is.
- * return 0 if it isn't.
+ * return true if it is.
+ * return false if it isn't.
*/
-static int in_list(const Client_data *list, uint16_t length, const Mono_Time *mono_time, const uint8_t *public_key,
- const IP_Port *ip_port)
+non_null()
+static bool in_list(const Client_data *list, uint16_t length, const Mono_Time *mono_time, const uint8_t *public_key,
+ const IP_Port *ip_port)
{
for (unsigned int i = 0; i < length; ++i) {
- if (id_equal(list[i].public_key, public_key)) {
+ if (pk_equal(list[i].public_key, public_key)) {
const IPPTsPng *ipptp;
if (net_family_is_ipv4(ip_port->ip.family)) {
@@ -249,23 +255,23 @@ static int in_list(const Client_data *list, uint16_t length, const Mono_Time *mo
if (!mono_time_is_timeout(mono_time, ipptp->timestamp, BAD_NODE_TIMEOUT)
&& ipport_equal(&ipptp->ip_port, ip_port)) {
- return 1;
+ return true;
}
}
}
- return 0;
+ return false;
}
-/** Add nodes to the to_ping list.
+/** @brief Add nodes to the to_ping list.
* All nodes in this list are pinged every TIME_TO_PING seconds
* and are then removed from the list.
* If the list is full the nodes farthest from our public_key are replaced.
* The purpose of this list is to enable quick integration of new nodes into the
* network while preventing amplification attacks.
*
- * return 0 if node was added.
- * return -1 if node was not added.
+ * @retval 0 if node was added.
+ * @retval -1 if node was not added.
*/
int32_t ping_add(Ping *ping, const uint8_t *public_key, const IP_Port *ip_port)
{
@@ -295,7 +301,7 @@ int32_t ping_add(Ping *ping, const uint8_t *public_key, const IP_Port *ip_port)
return 0;
}
- if (public_key_cmp(ping->to_ping[i].public_key, public_key) == 0) {
+ if (pk_equal(ping->to_ping[i].public_key, public_key)) {
return -1;
}
}
@@ -308,7 +314,7 @@ int32_t ping_add(Ping *ping, const uint8_t *public_key, const IP_Port *ip_port)
}
-/** Ping all the valid nodes in the to_ping list every TIME_TO_PING seconds.
+/** @brief Ping all the valid nodes in the to_ping list every TIME_TO_PING seconds.
* This function must be run at least once every TIME_TO_PING seconds.
*/
void ping_iterate(Ping *ping)
@@ -342,7 +348,7 @@ void ping_iterate(Ping *ping)
}
-Ping *ping_new(const Mono_Time *mono_time, DHT *dht)
+Ping *ping_new(const Mono_Time *mono_time, const Random *rng, DHT *dht)
{
Ping *ping = (Ping *)calloc(1, sizeof(Ping));
@@ -358,6 +364,7 @@ Ping *ping_new(const Mono_Time *mono_time, DHT *dht)
}
ping->mono_time = mono_time;
+ ping->rng = rng;
ping->dht = dht;
networking_registerhandler(dht_get_net(ping->dht), NET_PACKET_PING_REQUEST, &handle_ping_request, dht);
networking_registerhandler(dht_get_net(ping->dht), NET_PACKET_PING_RESPONSE, &handle_ping_response, dht);
diff --git a/protocols/Tox/libtox/src/toxcore/ping.h b/protocols/Tox/libtox/src/toxcore/ping.h
index 4e8192323a..da8d7f003e 100644
--- a/protocols/Tox/libtox/src/toxcore/ping.h
+++ b/protocols/Tox/libtox/src/toxcore/ping.h
@@ -17,24 +17,32 @@
typedef struct Ping Ping;
-Ping *ping_new(const struct Mono_Time *mono_time, DHT *dht);
+non_null()
+Ping *ping_new(const Mono_Time *mono_time, const Random *rng, DHT *dht);
+nullable(1)
void ping_kill(Ping *ping);
-/** Add nodes to the to_ping list.
+/** @brief Add nodes to the to_ping list.
* All nodes in this list are pinged every TIME_TO_PING seconds
* and are then removed from the list.
* If the list is full the nodes farthest from our public_key are replaced.
* The purpose of this list is to enable quick integration of new nodes into the
* network while preventing amplification attacks.
*
- * return 0 if node was added.
- * return -1 if node was not added.
+ * @retval 0 if node was added.
+ * @retval -1 if node was not added.
*/
+non_null()
int32_t ping_add(Ping *ping, const uint8_t *public_key, const IP_Port *ip_port);
+/** @brief Ping all the valid nodes in the to_ping list every TIME_TO_PING seconds.
+ * This function must be run at least once every TIME_TO_PING seconds.
+ */
+non_null()
void ping_iterate(Ping *ping);
+non_null()
void ping_send_request(Ping *ping, const IP_Port *ipp, const uint8_t *public_key);
#endif // C_TOXCORE_TOXCORE_PING_H
diff --git a/protocols/Tox/libtox/src/toxcore/ping_array.c b/protocols/Tox/libtox/src/toxcore/ping_array.c
index fe067d2b29..e22a2e6c1c 100644
--- a/protocols/Tox/libtox/src/toxcore/ping_array.c
+++ b/protocols/Tox/libtox/src/toxcore/ping_array.c
@@ -11,6 +11,7 @@
#include <stdlib.h>
#include <string.h>
+#include "ccompat.h"
#include "crypto_core.h"
#include "mono_time.h"
#include "util.h"
@@ -18,7 +19,7 @@
typedef struct Ping_Array_Entry {
uint8_t *data;
uint32_t length;
- uint64_t time;
+ uint64_t ping_time;
uint64_t ping_id;
} Ping_Array_Entry;
@@ -62,6 +63,7 @@ Ping_Array *ping_array_new(uint32_t size, uint32_t timeout)
return empty_array;
}
+non_null()
static void clear_entry(Ping_Array *array, uint32_t index)
{
const Ping_Array_Entry empty = {nullptr};
@@ -85,14 +87,14 @@ void ping_array_kill(Ping_Array *array)
free(array);
}
-/** Clear timed out entries.
- */
+/** Clear timed out entries. */
+non_null()
static void ping_array_clear_timedout(Ping_Array *array, const Mono_Time *mono_time)
{
while (array->last_deleted != array->last_added) {
const uint32_t index = array->last_deleted % array->total_size;
- if (!mono_time_is_timeout(mono_time, array->entries[index].time, array->timeout)) {
+ if (!mono_time_is_timeout(mono_time, array->entries[index].ping_time, array->timeout)) {
break;
}
@@ -101,8 +103,8 @@ static void ping_array_clear_timedout(Ping_Array *array, const Mono_Time *mono_t
}
}
-uint64_t ping_array_add(Ping_Array *array, const Mono_Time *mono_time, const uint8_t *data,
- uint32_t length)
+uint64_t ping_array_add(Ping_Array *array, const Mono_Time *mono_time, const Random *rng,
+ const uint8_t *data, uint32_t length)
{
ping_array_clear_timedout(array, mono_time);
const uint32_t index = array->last_added % array->total_size;
@@ -120,9 +122,9 @@ uint64_t ping_array_add(Ping_Array *array, const Mono_Time *mono_time, const uin
memcpy(array->entries[index].data, data, length);
array->entries[index].length = length;
- array->entries[index].time = mono_time_get(mono_time);
+ array->entries[index].ping_time = mono_time_get(mono_time);
++array->last_added;
- uint64_t ping_id = random_u64();
+ uint64_t ping_id = random_u64(rng);
ping_id /= array->total_size;
ping_id *= array->total_size;
ping_id += index;
@@ -148,7 +150,7 @@ int32_t ping_array_check(Ping_Array *array, const Mono_Time *mono_time, uint8_t
return -1;
}
- if (mono_time_is_timeout(mono_time, array->entries[index].time, array->timeout)) {
+ if (mono_time_is_timeout(mono_time, array->entries[index].ping_time, array->timeout)) {
return -1;
}
diff --git a/protocols/Tox/libtox/src/toxcore/ping_array.h b/protocols/Tox/libtox/src/toxcore/ping_array.h
index 98dceaba0c..fda84cb110 100644
--- a/protocols/Tox/libtox/src/toxcore/ping_array.h
+++ b/protocols/Tox/libtox/src/toxcore/ping_array.h
@@ -12,6 +12,7 @@
#include <stddef.h>
#include <stdint.h>
+#include "crypto_core.h"
#include "mono_time.h"
#ifdef __cplusplus
@@ -26,22 +27,24 @@ typedef struct Ping_Array Ping_Array;
* @param size represents the total size of the array and should be a power of 2.
* @param timeout represents the maximum timeout in seconds for the entry.
*
- * @return 0 on success, -1 on failure.
+ * @return pointer to allocated Ping_Array on success, nullptr on failure.
*/
struct Ping_Array *ping_array_new(uint32_t size, uint32_t timeout);
/**
* @brief Free all the allocated memory in a @ref Ping_Array.
*/
-void ping_array_kill(struct Ping_Array *array);
+nullable(1)
+void ping_array_kill(Ping_Array *array);
/**
* @brief Add a data with length to the @ref Ping_Array list and return a ping_id.
*
* @return ping_id on success, 0 on failure.
*/
-uint64_t ping_array_add(struct Ping_Array *array, const struct Mono_Time *mono_time, const uint8_t *data,
- uint32_t length);
+non_null()
+uint64_t ping_array_add(Ping_Array *array, const Mono_Time *mono_time, const Random *rng,
+ const uint8_t *data, uint32_t length);
/**
* @brief Check if @p ping_id is valid and not timed out.
@@ -50,7 +53,8 @@ uint64_t ping_array_add(struct Ping_Array *array, const struct Mono_Time *mono_t
*
* @return length of data copied on success, -1 on failure.
*/
-int32_t ping_array_check(struct Ping_Array *array, const struct Mono_Time *mono_time, uint8_t *data, size_t length,
+non_null()
+int32_t ping_array_check(Ping_Array *array, const Mono_Time *mono_time, uint8_t *data, size_t length,
uint64_t ping_id);
#ifdef __cplusplus
diff --git a/protocols/Tox/libtox/src/toxcore/state.c b/protocols/Tox/libtox/src/toxcore/state.c
index a31768131a..701cf4426b 100644
--- a/protocols/Tox/libtox/src/toxcore/state.c
+++ b/protocols/Tox/libtox/src/toxcore/state.c
@@ -6,6 +6,8 @@
#include <string.h>
+#include "ccompat.h"
+
/** state load/save */
int state_load(const Logger *log, state_load_cb *state_load_callback, void *outer,
const uint8_t *data, uint32_t length, uint16_t cookie_inner)
diff --git a/protocols/Tox/libtox/src/toxcore/state.h b/protocols/Tox/libtox/src/toxcore/state.h
index c539828b11..716286d6d1 100644
--- a/protocols/Tox/libtox/src/toxcore/state.h
+++ b/protocols/Tox/libtox/src/toxcore/state.h
@@ -53,9 +53,11 @@ typedef enum State_Load_Status {
typedef State_Load_Status state_load_cb(void *outer, const uint8_t *data, uint32_t len, uint16_t type);
/** state load/save */
+non_null()
int state_load(const Logger *log, state_load_cb *state_load_callback, void *outer,
const uint8_t *data, uint32_t length, uint16_t cookie_inner);
+non_null()
uint8_t *state_write_section_header(uint8_t *data, uint16_t cookie_type, uint32_t len, uint32_t section_type);
// Utilities for state data serialisation.
@@ -63,13 +65,19 @@ uint8_t *state_write_section_header(uint8_t *data, uint16_t cookie_type, uint32_
uint16_t lendian_to_host16(uint16_t lendian);
uint16_t host_to_lendian16(uint16_t host);
+non_null()
void host_to_lendian_bytes64(uint8_t *dest, uint64_t num);
+non_null()
void lendian_bytes_to_host64(uint64_t *dest, const uint8_t *lendian);
+non_null()
void host_to_lendian_bytes32(uint8_t *dest, uint32_t num);
+non_null()
void lendian_bytes_to_host32(uint32_t *dest, const uint8_t *lendian);
+non_null()
void host_to_lendian_bytes16(uint8_t *dest, uint16_t num);
+non_null()
void lendian_bytes_to_host16(uint16_t *dest, const uint8_t *lendian);
#ifdef __cplusplus
diff --git a/protocols/Tox/libtox/src/toxcore/timed_auth.c b/protocols/Tox/libtox/src/toxcore/timed_auth.c
new file mode 100644
index 0000000000..ebd5100286
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/timed_auth.c
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2019-2021 The TokTok team.
+ */
+#include "timed_auth.h"
+
+#include <string.h>
+
+#include "ccompat.h"
+
+non_null(1,6) nullable(4)
+static void create_timed_auth_to_hash(const Mono_Time *mono_time, uint16_t timeout, bool previous, const uint8_t *data,
+ uint16_t length, uint8_t *to_hash)
+{
+ const uint64_t t = (mono_time_get(mono_time) / timeout) - (previous ? 1 : 0);
+ memcpy(to_hash, &t, sizeof(t));
+
+ if (data != nullptr) {
+ memcpy(to_hash + sizeof(t), data, length);
+ }
+}
+
+void generate_timed_auth(const Mono_Time *mono_time, uint16_t timeout, const uint8_t *key,
+ const uint8_t *data, uint16_t length, uint8_t *timed_auth)
+{
+ VLA(uint8_t, to_hash, sizeof(uint64_t) + length);
+ create_timed_auth_to_hash(mono_time, timeout, false, data, length, to_hash);
+ crypto_hmac(timed_auth, key, to_hash, SIZEOF_VLA(to_hash));
+}
+
+bool check_timed_auth(const Mono_Time *mono_time, uint16_t timeout, const uint8_t *key, const uint8_t *data,
+ uint16_t length, const uint8_t *timed_auth)
+{
+ VLA(uint8_t, to_hash, sizeof(uint64_t) + length);
+
+ for (uint8_t i = 0; i < 2; ++i) {
+ create_timed_auth_to_hash(mono_time, timeout, i != 0, data, length, to_hash);
+
+ if (crypto_hmac_verify(timed_auth, key, to_hash, SIZEOF_VLA(to_hash))) {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/protocols/Tox/libtox/src/toxcore/timed_auth.h b/protocols/Tox/libtox/src/toxcore/timed_auth.h
new file mode 100644
index 0000000000..691b04d0df
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/timed_auth.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2019-2021 The TokTok team.
+ */
+#ifndef C_TOXCORE_TOXCORE_TIMED_AUTH_H
+#define C_TOXCORE_TOXCORE_TIMED_AUTH_H
+
+#include "crypto_core.h"
+#include "mono_time.h"
+
+#define TIMED_AUTH_SIZE CRYPTO_HMAC_SIZE
+
+/**
+ * @brief Write timed authentication code of data to timed_auth.
+ *
+ * @param timed_auth Must be of size TIMED_AUTH_SIZE.
+ */
+
+non_null(1, 3, 6) nullable(4)
+void generate_timed_auth(const Mono_Time *mono_time, uint16_t timeout, const uint8_t *key,
+ const uint8_t *data, uint16_t length, uint8_t *timed_auth);
+
+/**
+ * @brief Check timed_auth. This succeeds if `timed_auth` was generated by
+ * `generate_timed_auth` at most `timeout` seconds ago, and fails if at least
+ * `2*timeout` seconds ago. More precisely, it succeeds iff
+ * `current_time / timeout` is equal to or one more than
+ * `creation_time / timeout`.
+ *
+ * @param timed_auth Must be of size TIMED_AUTH_SIZE.
+ * @return true on success, false otherwise.
+ */
+non_null(1, 3, 6) nullable(4)
+bool check_timed_auth(const Mono_Time *mono_time, uint16_t timeout, const uint8_t *key,
+ const uint8_t *data, uint16_t length, const uint8_t *timed_auth);
+#endif
diff --git a/protocols/Tox/libtox/src/toxcore/tox.c b/protocols/Tox/libtox/src/toxcore/tox.c
index fce8347c5e..079f404079 100644
--- a/protocols/Tox/libtox/src/toxcore/tox.c
+++ b/protocols/Tox/libtox/src/toxcore/tox.c
@@ -11,22 +11,25 @@
#endif
#include "tox.h"
-#include "tox_private.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "Messenger.h"
+#include "ccompat.h"
#include "group.h"
#include "logger.h"
#include "mono_time.h"
+#include "network.h"
+#include "tox_private.h"
+#include "tox_struct.h"
#include "../toxencryptsave/defines.h"
#define SET_ERROR_PARAMETER(param, x) \
do { \
- if (param) { \
+ if (param != nullptr) { \
*param = x; \
} \
} while (0)
@@ -35,6 +38,10 @@ static_assert(TOX_HASH_LENGTH == CRYPTO_SHA256_SIZE,
"TOX_HASH_LENGTH is assumed to be equal to CRYPTO_SHA256_SIZE");
static_assert(FILE_ID_LENGTH == CRYPTO_SYMMETRIC_KEY_SIZE,
"FILE_ID_LENGTH is assumed to be equal to CRYPTO_SYMMETRIC_KEY_SIZE");
+static_assert(TOX_DHT_NODE_IP_STRING_SIZE == IP_NTOA_LEN,
+ "TOX_DHT_NODE_IP_STRING_SIZE is assumed to be equal to IP_NTOA_LEN");
+static_assert(TOX_DHT_NODE_PUBLIC_KEY_SIZE == CRYPTO_PUBLIC_KEY_SIZE,
+ "TOX_DHT_NODE_PUBLIC_KEY_SIZE is assumed to be equal to CRYPTO_PUBLIC_KEY_SIZE");
static_assert(TOX_FILE_ID_LENGTH == CRYPTO_SYMMETRIC_KEY_SIZE,
"TOX_FILE_ID_LENGTH is assumed to be equal to CRYPTO_SYMMETRIC_KEY_SIZE");
static_assert(TOX_FILE_ID_LENGTH == TOX_HASH_LENGTH,
@@ -48,58 +55,27 @@ static_assert(TOX_MAX_NAME_LENGTH == MAX_NAME_LENGTH,
static_assert(TOX_MAX_STATUS_MESSAGE_LENGTH == MAX_STATUSMESSAGE_LENGTH,
"TOX_MAX_STATUS_MESSAGE_LENGTH is assumed to be equal to MAX_STATUSMESSAGE_LENGTH");
-struct Tox {
- // XXX: Messenger *must* be the first member, because toxav casts its
- // `Tox *` to `Messenger **`.
- Messenger *m;
- Mono_Time *mono_time;
- pthread_mutex_t *mutex;
-
- tox_self_connection_status_cb *self_connection_status_callback;
- tox_friend_name_cb *friend_name_callback;
- tox_friend_status_message_cb *friend_status_message_callback;
- tox_friend_status_cb *friend_status_callback;
- tox_friend_connection_status_cb *friend_connection_status_callback;
- tox_friend_typing_cb *friend_typing_callback;
- tox_friend_read_receipt_cb *friend_read_receipt_callback;
- tox_friend_request_cb *friend_request_callback;
- tox_friend_message_cb *friend_message_callback;
- tox_file_recv_control_cb *file_recv_control_callback;
- tox_file_chunk_request_cb *file_chunk_request_callback;
- tox_file_recv_cb *file_recv_callback;
- tox_file_recv_chunk_cb *file_recv_chunk_callback;
- tox_conference_invite_cb *conference_invite_callback;
- tox_conference_connected_cb *conference_connected_callback;
- tox_conference_message_cb *conference_message_callback;
- tox_conference_title_cb *conference_title_callback;
- tox_conference_peer_name_cb *conference_peer_name_callback;
- tox_conference_peer_list_changed_cb *conference_peer_list_changed_callback;
- tox_friend_lossy_packet_cb *friend_lossy_packet_callback_per_pktid[UINT8_MAX + 1];
- tox_friend_lossless_packet_cb *friend_lossless_packet_callback_per_pktid[UINT8_MAX + 1];
-
- void *toxav_object; // workaround to store a ToxAV object (setter and getter functions are available)
+struct Tox_Userdata {
+ Tox *tox;
+ void *user_data;
};
-static void lock(const Tox *tox)
+static logger_cb tox_log_handler;
+non_null(1, 3, 5, 6) nullable(7)
+static void tox_log_handler(void *context, Logger_Level level, const char *file, int line, const char *func,
+ const char *message, void *userdata)
{
- if (tox->mutex != nullptr) {
- pthread_mutex_lock(tox->mutex);
- }
-}
+ Tox *tox = (Tox *)context;
+ assert(tox != nullptr);
-static void unlock(const Tox *tox)
-{
- if (tox->mutex != nullptr) {
- pthread_mutex_unlock(tox->mutex);
+ if (tox->log_callback != nullptr) {
+ tox->log_callback(tox, (Tox_Log_Level)level, file, line, func, message, userdata);
}
}
-struct Tox_Userdata {
- Tox *tox;
- void *user_data;
-};
-
-static void tox_self_connection_status_handler(Messenger *m, unsigned int connection_status, void *user_data)
+static m_self_connection_status_cb tox_self_connection_status_handler;
+non_null(1) nullable(3)
+static void tox_self_connection_status_handler(Messenger *m, Onion_Connection_Status connection_status, void *user_data)
{
struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
@@ -108,6 +84,8 @@ static void tox_self_connection_status_handler(Messenger *m, unsigned int connec
}
}
+static m_friend_name_cb tox_friend_name_handler;
+non_null(1, 3) nullable(5)
static void tox_friend_name_handler(Messenger *m, uint32_t friend_number, const uint8_t *name, size_t length,
void *user_data)
{
@@ -118,6 +96,8 @@ static void tox_friend_name_handler(Messenger *m, uint32_t friend_number, const
}
}
+static m_friend_status_message_cb tox_friend_status_message_handler;
+non_null(1, 3) nullable(5)
static void tox_friend_status_message_handler(Messenger *m, uint32_t friend_number, const uint8_t *message,
size_t length, void *user_data)
{
@@ -128,6 +108,8 @@ static void tox_friend_status_message_handler(Messenger *m, uint32_t friend_numb
}
}
+static m_friend_status_cb tox_friend_status_handler;
+non_null(1) nullable(4)
static void tox_friend_status_handler(Messenger *m, uint32_t friend_number, unsigned int status, void *user_data)
{
struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
@@ -137,6 +119,8 @@ static void tox_friend_status_handler(Messenger *m, uint32_t friend_number, unsi
}
}
+static m_friend_connection_status_cb tox_friend_connection_status_handler;
+non_null(1) nullable(4)
static void tox_friend_connection_status_handler(Messenger *m, uint32_t friend_number, unsigned int connection_status,
void *user_data)
{
@@ -148,6 +132,8 @@ static void tox_friend_connection_status_handler(Messenger *m, uint32_t friend_n
}
}
+static m_friend_typing_cb tox_friend_typing_handler;
+non_null(1) nullable(4)
static void tox_friend_typing_handler(Messenger *m, uint32_t friend_number, bool is_typing, void *user_data)
{
struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
@@ -157,6 +143,8 @@ static void tox_friend_typing_handler(Messenger *m, uint32_t friend_number, bool
}
}
+static m_friend_read_receipt_cb tox_friend_read_receipt_handler;
+non_null(1) nullable(4)
static void tox_friend_read_receipt_handler(Messenger *m, uint32_t friend_number, uint32_t message_id, void *user_data)
{
struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
@@ -166,6 +154,8 @@ static void tox_friend_read_receipt_handler(Messenger *m, uint32_t friend_number
}
}
+static m_friend_request_cb tox_friend_request_handler;
+non_null(1, 2, 3) nullable(5)
static void tox_friend_request_handler(Messenger *m, const uint8_t *public_key, const uint8_t *message, size_t length,
void *user_data)
{
@@ -176,17 +166,21 @@ static void tox_friend_request_handler(Messenger *m, const uint8_t *public_key,
}
}
-static void tox_friend_message_handler(Messenger *m, uint32_t friend_number, unsigned int type, const uint8_t *message,
- size_t length, void *user_data)
+static m_friend_message_cb tox_friend_message_handler;
+non_null(1, 4) nullable(6)
+static void tox_friend_message_handler(Messenger *m, uint32_t friend_number, unsigned int message_type,
+ const uint8_t *message, size_t length, void *user_data)
{
struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
if (tox_data->tox->friend_message_callback != nullptr) {
- tox_data->tox->friend_message_callback(tox_data->tox, friend_number, (Tox_Message_Type)type, message, length,
+ tox_data->tox->friend_message_callback(tox_data->tox, friend_number, (Tox_Message_Type)message_type, message, length,
tox_data->user_data);
}
}
+static m_file_recv_control_cb tox_file_recv_control_handler;
+non_null(1) nullable(5)
static void tox_file_recv_control_handler(Messenger *m, uint32_t friend_number, uint32_t file_number,
unsigned int control, void *user_data)
{
@@ -198,6 +192,8 @@ static void tox_file_recv_control_handler(Messenger *m, uint32_t friend_number,
}
}
+static m_file_chunk_request_cb tox_file_chunk_request_handler;
+non_null(1) nullable(6)
static void tox_file_chunk_request_handler(Messenger *m, uint32_t friend_number, uint32_t file_number,
uint64_t position, size_t length, void *user_data)
{
@@ -209,6 +205,8 @@ static void tox_file_chunk_request_handler(Messenger *m, uint32_t friend_number,
}
}
+static m_file_recv_cb tox_file_recv_handler;
+non_null(1, 6) nullable(8)
static void tox_file_recv_handler(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)
{
@@ -220,6 +218,8 @@ static void tox_file_recv_handler(Messenger *m, uint32_t friend_number, uint32_t
}
}
+static m_file_recv_chunk_cb tox_file_recv_chunk_handler;
+non_null(1, 5) nullable(7)
static void tox_file_recv_chunk_handler(Messenger *m, uint32_t friend_number, uint32_t file_number, uint64_t position,
const uint8_t *data, size_t length, void *user_data)
{
@@ -231,6 +231,8 @@ static void tox_file_recv_chunk_handler(Messenger *m, uint32_t friend_number, ui
}
}
+static g_conference_invite_cb tox_conference_invite_handler;
+non_null(1, 4) nullable(6)
static void tox_conference_invite_handler(Messenger *m, uint32_t friend_number, int type, const uint8_t *cookie,
size_t length, void *user_data)
{
@@ -242,6 +244,8 @@ static void tox_conference_invite_handler(Messenger *m, uint32_t friend_number,
}
}
+static g_conference_connected_cb tox_conference_connected_handler;
+non_null(1) nullable(3)
static void tox_conference_connected_handler(Messenger *m, uint32_t conference_number, void *user_data)
{
struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
@@ -251,6 +255,8 @@ static void tox_conference_connected_handler(Messenger *m, uint32_t conference_n
}
}
+static g_conference_message_cb tox_conference_message_handler;
+non_null(1, 5) nullable(7)
static void tox_conference_message_handler(Messenger *m, uint32_t conference_number, uint32_t peer_number, int type,
const uint8_t *message, size_t length, void *user_data)
{
@@ -262,6 +268,8 @@ static void tox_conference_message_handler(Messenger *m, uint32_t conference_num
}
}
+static title_cb tox_conference_title_handler;
+non_null(1, 4) nullable(6)
static void tox_conference_title_handler(Messenger *m, uint32_t conference_number, uint32_t peer_number,
const uint8_t *title, size_t length, void *user_data)
{
@@ -273,6 +281,8 @@ static void tox_conference_title_handler(Messenger *m, uint32_t conference_numbe
}
}
+static peer_name_cb tox_conference_peer_name_handler;
+non_null(1, 4) nullable(6)
static void tox_conference_peer_name_handler(Messenger *m, uint32_t conference_number, uint32_t peer_number,
const uint8_t *name, size_t length, void *user_data)
{
@@ -284,6 +294,8 @@ static void tox_conference_peer_name_handler(Messenger *m, uint32_t conference_n
}
}
+static peer_list_changed_cb tox_conference_peer_list_changed_handler;
+non_null(1) nullable(3)
static void tox_conference_peer_list_changed_handler(Messenger *m, uint32_t conference_number, void *user_data)
{
struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
@@ -293,6 +305,24 @@ static void tox_conference_peer_list_changed_handler(Messenger *m, uint32_t conf
}
}
+static dht_get_nodes_response_cb tox_dht_get_nodes_response_handler;
+non_null(1, 2) nullable(3)
+static void tox_dht_get_nodes_response_handler(const DHT *dht, const Node_format *node, void *user_data)
+{
+ struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
+
+ if (tox_data->tox->dht_get_nodes_response_callback == nullptr) {
+ return;
+ }
+
+ Ip_Ntoa ip_str;
+ tox_data->tox->dht_get_nodes_response_callback(
+ tox_data->tox, node->public_key, net_ip_ntoa(&node->ip_port.ip, &ip_str), net_ntohs(node->ip_port.port),
+ tox_data->user_data);
+}
+
+static m_friend_lossy_packet_cb tox_friend_lossy_packet_handler;
+non_null(1, 4) nullable(6)
static void tox_friend_lossy_packet_handler(Messenger *m, uint32_t friend_number, uint8_t packet_id,
const uint8_t *data, size_t length, void *user_data)
{
@@ -307,6 +337,8 @@ static void tox_friend_lossy_packet_handler(Messenger *m, uint32_t friend_number
}
}
+static m_friend_lossless_packet_cb tox_friend_lossless_packet_handler;
+non_null(1, 4) nullable(6)
static void tox_friend_lossless_packet_handler(Messenger *m, uint32_t friend_number, uint8_t packet_id,
const uint8_t *data, size_t length, void *user_data)
{
@@ -327,6 +359,7 @@ bool tox_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch)
return TOX_VERSION_IS_API_COMPATIBLE(major, minor, patch);
}
+non_null()
static State_Load_Status state_load_callback(void *outer, const uint8_t *data, uint32_t length, uint16_t type)
{
const Tox *tox = (const Tox *)outer;
@@ -352,6 +385,7 @@ static State_Load_Status state_load_callback(void *outer, const uint8_t *data, u
}
/** Load tox from data of size length. */
+non_null()
static int tox_load(Tox *tox, const uint8_t *data, uint32_t length)
{
uint32_t data32[2];
@@ -451,8 +485,14 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
m_options.tcp_server_port = tox_options_get_tcp_port(opts);
m_options.hole_punching_enabled = tox_options_get_hole_punching_enabled(opts);
m_options.local_discovery_enabled = tox_options_get_local_discovery_enabled(opts);
+ m_options.dht_announcements_enabled = tox_options_get_dht_announcements_enabled(opts);
+
+ if (m_options.udp_disabled) {
+ m_options.local_discovery_enabled = false;
+ }
- m_options.log_callback = (logger_cb *)tox_options_get_log_callback(opts);
+ tox->log_callback = tox_options_get_log_callback(opts);
+ m_options.log_callback = tox_log_handler;
m_options.log_context = tox;
m_options.log_user_data = tox_options_get_log_user_data(opts);
@@ -480,6 +520,23 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
}
}
+ const Tox_System *sys = tox_options_get_operating_system(opts);
+ const Tox_System default_system = tox_default_system();
+ if (sys == nullptr) {
+ sys = &default_system;
+ }
+
+ if (sys->rng == nullptr || sys->ns == nullptr) {
+ // TODO(iphydf): Not quite right, but similar.
+ SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
+ tox_options_free(default_options);
+ free(tox);
+ return nullptr;
+ }
+
+ tox->rng = *sys->rng;
+ tox->ns = *sys->ns;
+
if (m_options.proxy_info.proxy_type != TCP_PROXY_NONE) {
if (tox_options_get_proxy_port(opts) == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_PORT);
@@ -491,10 +548,12 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
ip_init(&m_options.proxy_info.ip_port.ip, m_options.ipv6enabled);
if (m_options.ipv6enabled) {
- m_options.proxy_info.ip_port.ip.family = net_family_unspec;
+ m_options.proxy_info.ip_port.ip.family = net_family_unspec();
}
- if (addr_resolve_or_parse_ip(tox_options_get_proxy_host(opts), &m_options.proxy_info.ip_port.ip, nullptr) == 0) {
+ const char *const proxy_host = tox_options_get_proxy_host(opts);
+
+ if (proxy_host == nullptr || !addr_resolve_or_parse_ip(&tox->ns, proxy_host, &m_options.proxy_info.ip_port.ip, nullptr)) {
SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_HOST);
// TODO(irungentoo): TOX_ERR_NEW_PROXY_NOT_FOUND if domain.
tox_options_free(default_options);
@@ -505,7 +564,7 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
m_options.proxy_info.ip_port.port = net_htons(tox_options_get_proxy_port(opts));
}
- tox->mono_time = mono_time_new();
+ tox->mono_time = mono_time_new(sys->mono_time_callback, sys->mono_time_user_data);
if (tox->mono_time == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
@@ -534,16 +593,12 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
tox->mutex = nullptr;
}
- lock(tox);
+ tox_lock(tox);
- unsigned int m_error;
- tox->m = new_messenger(tox->mono_time, &m_options, &m_error);
-
- // TODO(iphydf): Clarify this code, check for NULL before new_groupchats, so
- // new_groupchats can assume m is non-NULL.
- if (!new_groupchats(tox->mono_time, tox->m)) {
- kill_messenger(tox->m);
+ Messenger_Error m_error;
+ tox->m = new_messenger(tox->mono_time, &tox->rng, &tox->ns, &m_options, &m_error);
+ if (tox->m == nullptr) {
if (m_error == MESSENGER_ERROR_PORT) {
SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PORT_ALLOC);
} else if (m_error == MESSENGER_ERROR_TCP_SERVER) {
@@ -554,7 +609,23 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
mono_time_free(tox->mono_time);
tox_options_free(default_options);
- unlock(tox);
+ tox_unlock(tox);
+
+ if (tox->mutex != nullptr) {
+ pthread_mutex_destroy(tox->mutex);
+ }
+
+ free(tox->mutex);
+ free(tox);
+ return nullptr;
+ }
+
+ if (new_groupchats(tox->mono_time, tox->m) == nullptr) {
+ kill_messenger(tox->m);
+
+ mono_time_free(tox->mono_time);
+ tox_options_free(default_options);
+ tox_unlock(tox);
if (tox->mutex != nullptr) {
pthread_mutex_destroy(tox->mutex);
@@ -562,6 +633,8 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
free(tox->mutex);
free(tox);
+
+ SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
return nullptr;
}
@@ -588,6 +661,7 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
callback_file_reqchunk(tox->m, tox_file_chunk_request_handler);
callback_file_sendrequest(tox->m, tox_file_recv_handler);
callback_file_data(tox->m, tox_file_recv_chunk_handler);
+ dht_callback_get_nodes_response(tox->m->dht, tox_dht_get_nodes_response_handler);
g_callback_group_invite(tox->m->conferences_object, tox_conference_invite_handler);
g_callback_group_connected(tox->m->conferences_object, tox_conference_connected_handler);
g_callback_group_message(tox->m->conferences_object, tox_conference_message_handler);
@@ -599,7 +673,7 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
tox_options_free(default_options);
- unlock(tox);
+ tox_unlock(tox);
return tox;
}
@@ -609,12 +683,12 @@ void tox_kill(Tox *tox)
return;
}
- lock(tox);
+ tox_lock(tox);
LOGGER_ASSERT(tox->m->log, tox->m->msi_packet == nullptr, "Attempted to kill tox while toxav is still alive");
kill_groupchats(tox->m->conferences_object);
kill_messenger(tox->m);
mono_time_free(tox->mono_time);
- unlock(tox);
+ tox_unlock(tox);
if (tox->mutex != nullptr) {
pthread_mutex_destroy(tox->mutex);
@@ -629,6 +703,7 @@ static uint32_t end_size(void)
return 2 * sizeof(uint32_t);
}
+non_null()
static void end_save(uint8_t *data)
{
state_write_section_header(data, STATE_COOKIE_TYPE, 0, STATE_TYPE_END);
@@ -637,12 +712,12 @@ static void end_save(uint8_t *data)
size_t tox_get_savedata_size(const Tox *tox)
{
assert(tox != nullptr);
- lock(tox);
- size_t ret = 2 * sizeof(uint32_t)
+ tox_lock(tox);
+ const size_t ret = 2 * sizeof(uint32_t)
+ messenger_size(tox->m)
+ conferences_size(tox->m->conferences_object)
+ end_size();
- unlock(tox);
+ tox_unlock(tox);
return ret;
}
@@ -656,7 +731,7 @@ void tox_get_savedata(const Tox *tox, uint8_t *savedata)
memset(savedata, 0, tox_get_savedata_size(tox));
- lock(tox);
+ tox_lock(tox);
const uint32_t size32 = sizeof(uint32_t);
@@ -670,86 +745,101 @@ void tox_get_savedata(const Tox *tox, uint8_t *savedata)
savedata = conferences_save(tox->m->conferences_object, savedata);
end_save(savedata);
- unlock(tox);
+ tox_unlock(tox);
}
-bool tox_bootstrap(Tox *tox, const char *host, uint16_t port, const uint8_t *public_key, Tox_Err_Bootstrap *error)
+non_null(5) nullable(1, 2, 4, 6)
+static int32_t resolve_bootstrap_node(Tox *tox, const char *host, uint16_t port, const uint8_t *public_key, IP_Port **root, Tox_Err_Bootstrap *error)
{
assert(tox != nullptr);
+ assert(root != nullptr);
- if (!host || !public_key) {
+ if (host == nullptr || public_key == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_NULL);
- return 0;
+ return -1;
}
if (port == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_PORT);
- return 0;
+ return -1;
}
- IP_Port *root;
-
- const int32_t count = net_getipport(host, &root, TOX_SOCK_DGRAM);
+ const int32_t count = net_getipport(host, root, TOX_SOCK_DGRAM);
if (count == -1) {
LOGGER_DEBUG(tox->m->log, "could not resolve bootstrap node '%s'", host);
- net_freeipport(root);
+ net_freeipport(*root);
SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST);
- return 0;
+ return -1;
+ }
+
+ assert(*root != nullptr);
+ return count;
+}
+
+bool tox_bootstrap(Tox *tox, const char *host, uint16_t port, const uint8_t *public_key, Tox_Err_Bootstrap *error)
+{
+ IP_Port *root;
+ const int32_t count = resolve_bootstrap_node(tox, host, port, public_key, &root, error);
+
+ if (count == -1) {
+ return false;
}
- lock(tox);
+ tox_lock(tox);
+ assert(count >= 0);
+ bool onion_success = false;
+ // UDP bootstrap is default success if it's disabled (because we don't even try).
+ bool udp_success = tox->m->options.udp_disabled;
for (int32_t i = 0; i < count; ++i) {
root[i].port = net_htons(port);
- onion_add_bs_path_node(tox->m->onion_c, &root[i], public_key);
+ if (onion_add_bs_path_node(tox->m->onion_c, &root[i], public_key)) {
+ // If UDP is enabled, the caller cares about whether any of the
+ // bootstrap calls below will succeed. In TCP-only mode, adding
+ // onion path nodes successfully is sufficient.
+ onion_success = true;
+ }
if (!tox->m->options.udp_disabled) {
- dht_bootstrap(tox->m->dht, &root[i], public_key);
+ if (dht_bootstrap(tox->m->dht, &root[i], public_key)) {
+ // If any of the bootstrap calls worked, we call it success.
+ udp_success = true;
+ }
}
}
- unlock(tox);
+ tox_unlock(tox);
net_freeipport(root);
- if (count) {
- SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_OK);
- return 1;
+ if (count == 0 || !onion_success || !udp_success) {
+ LOGGER_DEBUG(tox->m->log, "bootstrap node '%s' resolved to %d IP_Ports%s (onion: %s, UDP: %s)",
+ host, count,
+ count > 0 ? ", but failed to bootstrap with any of them" : "",
+ onion_success ? "success" : "FAILURE",
+ tox->m->options.udp_disabled ? "disabled" : (udp_success ? "success" : "FAILURE"));
+ SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST);
+ return false;
}
- LOGGER_DEBUG(tox->m->log, "bootstrap node '%s' resolved to 0 IP_Ports", host);
- SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST);
- return 0;
+ SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_OK);
+ return true;
}
bool tox_add_tcp_relay(Tox *tox, const char *host, uint16_t port, const uint8_t *public_key,
Tox_Err_Bootstrap *error)
{
- assert(tox != nullptr);
-
- if (!host || !public_key) {
- SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_NULL);
- return 0;
- }
-
- if (port == 0) {
- SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_PORT);
- return 0;
- }
-
IP_Port *root;
-
- const int32_t count = net_getipport(host, &root, TOX_SOCK_STREAM);
+ const int32_t count = resolve_bootstrap_node(tox, host, port, public_key, &root, error);
if (count == -1) {
- net_freeipport(root);
- SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST);
- return 0;
+ return false;
}
- lock(tox);
+ tox_lock(tox);
+ assert(count >= 0);
for (int32_t i = 0; i < count; ++i) {
root[i].port = net_htons(port);
@@ -757,35 +847,38 @@ bool tox_add_tcp_relay(Tox *tox, const char *host, uint16_t port, const uint8_t
add_tcp_relay(tox->m->net_crypto, &root[i], public_key);
}
- unlock(tox);
+ tox_unlock(tox);
net_freeipport(root);
- if (count) {
- SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_OK);
- return 1;
+ if (count == 0) {
+ SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST);
+ return false;
}
- SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST);
- return 0;
+ SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_OK);
+ return true;
}
Tox_Connection tox_self_get_connection_status(const Tox *tox)
{
assert(tox != nullptr);
- lock(tox);
- const unsigned int ret = onion_connection_status(tox->m->onion_c);
- unlock(tox);
+ tox_lock(tox);
+ const Onion_Connection_Status ret = onion_connection_status(tox->m->onion_c);
+ tox_unlock(tox);
- if (ret == 2) {
- return TOX_CONNECTION_UDP;
- }
+ switch (ret) {
+ case ONION_CONNECTION_STATUS_NONE:
+ return TOX_CONNECTION_NONE;
+
+ case ONION_CONNECTION_STATUS_TCP:
+ return TOX_CONNECTION_TCP;
- if (ret == 1) {
- return TOX_CONNECTION_TCP;
+ case ONION_CONNECTION_STATUS_UDP:
+ return TOX_CONNECTION_UDP;
}
- return TOX_CONNECTION_NONE;
+ LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
}
@@ -798,16 +891,21 @@ void tox_callback_self_connection_status(Tox *tox, tox_self_connection_status_cb
uint32_t tox_iteration_interval(const Tox *tox)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
uint32_t ret = messenger_run_interval(tox->m);
- unlock(tox);
+
+ if (m_is_receiving_file(tox->m)) {
+ ret = 1;
+ }
+
+ tox_unlock(tox);
return ret;
}
void tox_iterate(Tox *tox, void *user_data)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
mono_time_update(tox->mono_time);
@@ -815,34 +913,34 @@ void tox_iterate(Tox *tox, void *user_data)
do_messenger(tox->m, &tox_data);
do_groupchats(tox->m->conferences_object, &tox_data);
- unlock(tox);
+ tox_unlock(tox);
}
void tox_self_get_address(const Tox *tox, uint8_t *address)
{
assert(tox != nullptr);
- if (address) {
- lock(tox);
+ if (address != nullptr) {
+ tox_lock(tox);
getaddress(tox->m, address);
- unlock(tox);
+ tox_unlock(tox);
}
}
void tox_self_set_nospam(Tox *tox, uint32_t nospam)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
set_nospam(tox->m->fr, net_htonl(nospam));
- unlock(tox);
+ tox_unlock(tox);
}
uint32_t tox_self_get_nospam(const Tox *tox)
{
assert(tox != nullptr);
- lock(tox);
- uint32_t ret = net_ntohl(get_nospam(tox->m->fr));
- unlock(tox);
+ tox_lock(tox);
+ const uint32_t ret = net_ntohl(get_nospam(tox->m->fr));
+ tox_unlock(tox);
return ret;
}
@@ -850,10 +948,10 @@ void tox_self_get_public_key(const Tox *tox, uint8_t *public_key)
{
assert(tox != nullptr);
- if (public_key) {
- lock(tox);
+ if (public_key != nullptr) {
+ tox_lock(tox);
memcpy(public_key, nc_get_self_public_key(tox->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE);
- unlock(tox);
+ tox_unlock(tox);
}
}
@@ -861,10 +959,10 @@ void tox_self_get_secret_key(const Tox *tox, uint8_t *secret_key)
{
assert(tox != nullptr);
- if (secret_key) {
- lock(tox);
+ if (secret_key != nullptr) {
+ tox_lock(tox);
memcpy(secret_key, nc_get_self_secret_key(tox->m->net_crypto), CRYPTO_SECRET_KEY_SIZE);
- unlock(tox);
+ tox_unlock(tox);
}
}
@@ -872,32 +970,32 @@ bool tox_self_set_name(Tox *tox, const uint8_t *name, size_t length, Tox_Err_Set
{
assert(tox != nullptr);
- if (!name && length != 0) {
+ if (name == nullptr && length != 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_NULL);
- return 0;
+ return false;
}
- lock(tox);
+ tox_lock(tox);
if (setname(tox->m, name, length) == 0) {
// TODO(irungentoo): function to set different per group names?
send_name_all_groups(tox->m->conferences_object);
SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_OK);
- unlock(tox);
- return 1;
+ tox_unlock(tox);
+ return true;
}
SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_TOO_LONG);
- unlock(tox);
- return 0;
+ tox_unlock(tox);
+ return false;
}
size_t tox_self_get_name_size(const Tox *tox)
{
assert(tox != nullptr);
- lock(tox);
- size_t ret = m_get_self_name_size(tox->m);
- unlock(tox);
+ tox_lock(tox);
+ const size_t ret = m_get_self_name_size(tox->m);
+ tox_unlock(tox);
return ret;
}
@@ -905,10 +1003,10 @@ void tox_self_get_name(const Tox *tox, uint8_t *name)
{
assert(tox != nullptr);
- if (name) {
- lock(tox);
+ if (name != nullptr) {
+ tox_lock(tox);
getself_name(tox->m, name);
- unlock(tox);
+ tox_unlock(tox);
}
}
@@ -916,30 +1014,30 @@ bool tox_self_set_status_message(Tox *tox, const uint8_t *status_message, size_t
{
assert(tox != nullptr);
- if (!status_message && length != 0) {
+ if (status_message == nullptr && length != 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_NULL);
- return 0;
+ return false;
}
- lock(tox);
+ tox_lock(tox);
if (m_set_statusmessage(tox->m, status_message, length) == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_OK);
- unlock(tox);
- return 1;
+ tox_unlock(tox);
+ return true;
}
SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_TOO_LONG);
- unlock(tox);
- return 0;
+ tox_unlock(tox);
+ return false;
}
size_t tox_self_get_status_message_size(const Tox *tox)
{
assert(tox != nullptr);
- lock(tox);
- size_t ret = m_get_self_statusmessage_size(tox->m);
- unlock(tox);
+ tox_lock(tox);
+ const size_t ret = m_get_self_statusmessage_size(tox->m);
+ tox_unlock(tox);
return ret;
}
@@ -947,30 +1045,31 @@ void tox_self_get_status_message(const Tox *tox, uint8_t *status_message)
{
assert(tox != nullptr);
- if (status_message) {
- lock(tox);
+ if (status_message != nullptr) {
+ tox_lock(tox);
m_copy_self_statusmessage(tox->m, status_message);
- unlock(tox);
+ tox_unlock(tox);
}
}
void tox_self_set_status(Tox *tox, Tox_User_Status status)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
m_set_userstatus(tox->m, status);
- unlock(tox);
+ tox_unlock(tox);
}
Tox_User_Status tox_self_get_status(const Tox *tox)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const uint8_t status = m_get_self_userstatus(tox->m);
- unlock(tox);
+ tox_unlock(tox);
return (Tox_User_Status)status;
}
+non_null(1) nullable(3)
static void set_friend_error(const Logger *log, int32_t ret, Tox_Err_Friend_Add *error)
{
switch (ret) {
@@ -1022,22 +1121,22 @@ uint32_t tox_friend_add(Tox *tox, const uint8_t *address, const uint8_t *message
{
assert(tox != nullptr);
- if (!address || !message) {
+ if (address == nullptr || message == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_NULL);
return UINT32_MAX;
}
- lock(tox);
+ tox_lock(tox);
const int32_t ret = m_addfriend(tox->m, address, message, length);
if (ret >= 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_OK);
- unlock(tox);
- return ret;
+ tox_unlock(tox);
+ return (uint32_t)ret;
}
set_friend_error(tox->m->log, ret, error);
- unlock(tox);
+ tox_unlock(tox);
return UINT32_MAX;
}
@@ -1045,54 +1144,54 @@ uint32_t tox_friend_add_norequest(Tox *tox, const uint8_t *public_key, Tox_Err_F
{
assert(tox != nullptr);
- if (!public_key) {
+ if (public_key == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_NULL);
return UINT32_MAX;
}
- lock(tox);
+ tox_lock(tox);
const int32_t ret = m_addfriend_norequest(tox->m, public_key);
if (ret >= 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_OK);
- unlock(tox);
- return ret;
+ tox_unlock(tox);
+ return (uint32_t)ret;
}
set_friend_error(tox->m->log, ret, error);
- unlock(tox);
+ tox_unlock(tox);
return UINT32_MAX;
}
bool tox_friend_delete(Tox *tox, uint32_t friend_number, Tox_Err_Friend_Delete *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = m_delfriend(tox->m, friend_number);
- unlock(tox);
+ tox_unlock(tox);
// TODO(irungentoo): handle if realloc fails?
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_DELETE_FRIEND_NOT_FOUND);
- return 0;
+ return false;
}
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_DELETE_OK);
- return 1;
+ return true;
}
uint32_t tox_friend_by_public_key(const Tox *tox, const uint8_t *public_key, Tox_Err_Friend_By_Public_Key *error)
{
assert(tox != nullptr);
- if (!public_key) {
+ if (public_key == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_BY_PUBLIC_KEY_NULL);
return UINT32_MAX;
}
- lock(tox);
+ tox_lock(tox);
const int32_t ret = getfriend_id(tox->m, public_key);
- unlock(tox);
+ tox_unlock(tox);
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_BY_PUBLIC_KEY_NOT_FOUND);
@@ -1100,7 +1199,8 @@ uint32_t tox_friend_by_public_key(const Tox *tox, const uint8_t *public_key, Tox
}
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_BY_PUBLIC_KEY_OK);
- return ret;
+ assert(ret >= 0);
+ return (uint32_t)ret;
}
bool tox_friend_get_public_key(const Tox *tox, uint32_t friend_number, uint8_t *public_key,
@@ -1108,38 +1208,38 @@ bool tox_friend_get_public_key(const Tox *tox, uint32_t friend_number, uint8_t *
{
assert(tox != nullptr);
- if (!public_key) {
- return 0;
+ if (public_key == nullptr) {
+ return false;
}
- lock(tox);
+ tox_lock(tox);
if (get_real_pk(tox->m, friend_number, public_key) == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_PUBLIC_KEY_FRIEND_NOT_FOUND);
- unlock(tox);
- return 0;
+ tox_unlock(tox);
+ return false;
}
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_PUBLIC_KEY_OK);
- unlock(tox);
- return 1;
+ tox_unlock(tox);
+ return true;
}
bool tox_friend_exists(const Tox *tox, uint32_t friend_number)
{
assert(tox != nullptr);
- lock(tox);
- bool ret = m_friend_exists(tox->m, friend_number);
- unlock(tox);
+ tox_lock(tox);
+ const bool ret = m_friend_exists(tox->m, friend_number);
+ tox_unlock(tox);
return ret;
}
uint64_t tox_friend_get_last_online(const Tox *tox, uint32_t friend_number, Tox_Err_Friend_Get_Last_Online *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const uint64_t timestamp = m_get_last_online(tox->m, friend_number);
- unlock(tox);
+ tox_unlock(tox);
if (timestamp == UINT64_MAX) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_LAST_ONLINE_FRIEND_NOT_FOUND);
@@ -1153,9 +1253,9 @@ uint64_t tox_friend_get_last_online(const Tox *tox, uint32_t friend_number, Tox_
size_t tox_self_get_friend_list_size(const Tox *tox)
{
assert(tox != nullptr);
- lock(tox);
- size_t ret = count_friendlist(tox->m);
- unlock(tox);
+ tox_lock(tox);
+ const size_t ret = count_friendlist(tox->m);
+ tox_unlock(tox);
return ret;
}
@@ -1163,20 +1263,20 @@ void tox_self_get_friend_list(const Tox *tox, uint32_t *friend_list)
{
assert(tox != nullptr);
- if (friend_list) {
- lock(tox);
+ if (friend_list != nullptr) {
+ tox_lock(tox);
// TODO(irungentoo): size parameter?
copy_friendlist(tox->m, friend_list, count_friendlist(tox->m));
- unlock(tox);
+ tox_unlock(tox);
}
}
size_t tox_friend_get_name_size(const Tox *tox, uint32_t friend_number, Tox_Err_Friend_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = m_get_name_size(tox->m, friend_number);
- unlock(tox);
+ tox_unlock(tox);
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
@@ -1191,22 +1291,22 @@ bool tox_friend_get_name(const Tox *tox, uint32_t friend_number, uint8_t *name,
{
assert(tox != nullptr);
- if (!name) {
+ if (name == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_NULL);
- return 0;
+ return false;
}
- lock(tox);
+ tox_lock(tox);
const int ret = getname(tox->m, friend_number, name);
- unlock(tox);
+ tox_unlock(tox);
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
- return 0;
+ return false;
}
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK);
- return 1;
+ return true;
}
void tox_callback_friend_name(Tox *tox, tox_friend_name_cb *callback)
@@ -1218,9 +1318,9 @@ void tox_callback_friend_name(Tox *tox, tox_friend_name_cb *callback)
size_t tox_friend_get_status_message_size(const Tox *tox, uint32_t friend_number, Tox_Err_Friend_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = m_get_statusmessage_size(tox->m, friend_number);
- unlock(tox);
+ tox_unlock(tox);
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
@@ -1236,17 +1336,17 @@ bool tox_friend_get_status_message(const Tox *tox, uint32_t friend_number, uint8
{
assert(tox != nullptr);
- if (!status_message) {
+ if (status_message == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_NULL);
return false;
}
- lock(tox);
+ tox_lock(tox);
const int size = m_get_statusmessage_size(tox->m, friend_number);
if (size == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
- unlock(tox);
+ tox_unlock(tox);
return false;
}
@@ -1254,7 +1354,7 @@ bool tox_friend_get_status_message(const Tox *tox, uint32_t friend_number, uint8
LOGGER_ASSERT(tox->m->log, ret == size, "concurrency problem: friend status message changed");
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK);
- unlock(tox);
+ tox_unlock(tox);
return ret == size;
}
@@ -1267,13 +1367,13 @@ void tox_callback_friend_status_message(Tox *tox, tox_friend_status_message_cb *
Tox_User_Status tox_friend_get_status(const Tox *tox, uint32_t friend_number, Tox_Err_Friend_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = m_get_userstatus(tox->m, friend_number);
- unlock(tox);
+ tox_unlock(tox);
if (ret == USERSTATUS_INVALID) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
- return (Tox_User_Status)(TOX_USER_STATUS_BUSY + 1);
+ return TOX_USER_STATUS_NONE;
}
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK);
@@ -1289,9 +1389,9 @@ void tox_callback_friend_status(Tox *tox, tox_friend_status_cb *callback)
Tox_Connection tox_friend_get_connection_status(const Tox *tox, uint32_t friend_number, Tox_Err_Friend_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = m_get_friend_connectionstatus(tox->m, friend_number);
- unlock(tox);
+ tox_unlock(tox);
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
@@ -1311,17 +1411,17 @@ void tox_callback_friend_connection_status(Tox *tox, tox_friend_connection_statu
bool tox_friend_get_typing(const Tox *tox, uint32_t friend_number, Tox_Err_Friend_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = m_get_istyping(tox->m, friend_number);
- unlock(tox);
+ tox_unlock(tox);
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
- return 0;
+ return false;
}
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK);
- return !!ret;
+ return ret != 0;
}
void tox_callback_friend_typing(Tox *tox, tox_friend_typing_cb *callback)
@@ -1333,19 +1433,20 @@ void tox_callback_friend_typing(Tox *tox, tox_friend_typing_cb *callback)
bool tox_self_set_typing(Tox *tox, uint32_t friend_number, bool typing, Tox_Err_Set_Typing *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
if (m_set_usertyping(tox->m, friend_number, typing) == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_SET_TYPING_FRIEND_NOT_FOUND);
- unlock(tox);
- return 0;
+ tox_unlock(tox);
+ return false;
}
SET_ERROR_PARAMETER(error, TOX_ERR_SET_TYPING_OK);
- unlock(tox);
- return 1;
+ tox_unlock(tox);
+ return true;
}
+non_null(1) nullable(3)
static void set_message_error(const Logger *log, int ret, Tox_Err_Friend_Send_Message *error)
{
switch (ret) {
@@ -1392,21 +1493,21 @@ uint32_t tox_friend_send_message(Tox *tox, uint32_t friend_number, Tox_Message_T
{
assert(tox != nullptr);
- if (!message) {
+ if (message == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_SEND_MESSAGE_NULL);
return 0;
}
- if (!length) {
+ if (length == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_SEND_MESSAGE_EMPTY);
return 0;
}
uint32_t message_id = 0;
- lock(tox);
+ tox_lock(tox);
set_message_error(tox->m->log, m_send_message_generic(tox->m, friend_number, type, message, length, &message_id),
error);
- unlock(tox);
+ tox_unlock(tox);
return message_id;
}
@@ -1430,126 +1531,126 @@ void tox_callback_friend_message(Tox *tox, tox_friend_message_cb *callback)
bool tox_hash(uint8_t *hash, const uint8_t *data, size_t length)
{
- if (!hash || (length && !data)) {
- return 0;
+ if (hash == nullptr || (data == nullptr && length != 0)) {
+ return false;
}
crypto_sha256(hash, data, length);
- return 1;
+ return true;
}
bool tox_file_control(Tox *tox, uint32_t friend_number, uint32_t file_number, Tox_File_Control control,
Tox_Err_File_Control *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = file_control(tox->m, friend_number, file_number, control);
- unlock(tox);
+ tox_unlock(tox);
if (ret == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_OK);
- return 1;
+ return true;
}
switch (ret) {
case -1: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND);
- return 0;
+ return false;
}
case -2: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED);
- return 0;
+ return false;
}
case -3: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_NOT_FOUND);
- return 0;
+ return false;
}
case -4: {
/* can't happen (this code is returned if `control` is invalid type) */
LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
- return 0;
+ return false;
}
case -5: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_ALREADY_PAUSED);
- return 0;
+ return false;
}
case -6: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_DENIED);
- return 0;
+ return false;
}
case -7: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_NOT_PAUSED);
- return 0;
+ return false;
}
case -8: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_SENDQ);
- return 0;
+ return false;
}
}
/* can't happen */
LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
- return 0;
+ return false;
}
bool tox_file_seek(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position,
Tox_Err_File_Seek *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = file_seek(tox->m, friend_number, file_number, position);
- unlock(tox);
+ tox_unlock(tox);
if (ret == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_OK);
- return 1;
+ return true;
}
switch (ret) {
case -1: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_FRIEND_NOT_FOUND);
- return 0;
+ return false;
}
case -2: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_FRIEND_NOT_CONNECTED);
- return 0;
+ return false;
}
case -3: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_NOT_FOUND);
- return 0;
+ return false;
}
case -4: // fall-through
case -5: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_DENIED);
- return 0;
+ return false;
}
case -6: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_INVALID_POSITION);
- return 0;
+ return false;
}
case -8: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_SENDQ);
- return 0;
+ return false;
}
}
/* can't happen */
LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
- return 0;
+ return false;
}
void tox_callback_file_recv_control(Tox *tox, tox_file_recv_control_cb *callback)
@@ -1563,18 +1664,18 @@ bool tox_file_get_file_id(const Tox *tox, uint32_t friend_number, uint32_t file_
{
assert(tox != nullptr);
- if (!file_id) {
+ if (file_id == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_NULL);
- return 0;
+ return false;
}
- lock(tox);
+ tox_lock(tox);
const int ret = file_get_id(tox->m, friend_number, file_number, file_id);
- unlock(tox);
+ tox_unlock(tox);
if (ret == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_OK);
- return 1;
+ return true;
}
if (ret == -1) {
@@ -1583,7 +1684,7 @@ bool tox_file_get_file_id(const Tox *tox, uint32_t friend_number, uint32_t file_
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_NOT_FOUND);
}
- return 0;
+ return false;
}
uint32_t tox_file_send(Tox *tox, uint32_t friend_number, uint32_t kind, uint64_t file_size, const uint8_t *file_id,
@@ -1591,22 +1692,22 @@ uint32_t tox_file_send(Tox *tox, uint32_t friend_number, uint32_t kind, uint64_t
{
assert(tox != nullptr);
- if (filename_length && !filename) {
+ if (filename == nullptr && filename_length != 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_NULL);
return UINT32_MAX;
}
uint8_t f_id[FILE_ID_LENGTH];
- if (!file_id) {
+ if (file_id == nullptr) {
/* Tox keys are 32 bytes like FILE_ID_LENGTH. */
- new_symmetric_key(f_id);
+ new_symmetric_key(&tox->rng, f_id);
file_id = f_id;
}
- lock(tox);
+ tox_lock(tox);
const long int file_num = new_filesender(tox->m, friend_number, kind, file_size, file_id, filename, filename_length);
- unlock(tox);
+ tox_unlock(tox);
if (file_num >= 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_OK);
@@ -1645,56 +1746,56 @@ bool tox_file_send_chunk(Tox *tox, uint32_t friend_number, uint32_t file_number,
size_t length, Tox_Err_File_Send_Chunk *error)
{
assert(tox != nullptr);
- lock(tox);
- const int ret = file_data(tox->m, friend_number, file_number, position, data, length);
- unlock(tox);
+ tox_lock(tox);
+ const int ret = send_file_data(tox->m, friend_number, file_number, position, data, length);
+ tox_unlock(tox);
if (ret == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_OK);
- return 1;
+ return true;
}
switch (ret) {
case -1: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_FRIEND_NOT_FOUND);
- return 0;
+ return false;
}
case -2: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_FRIEND_NOT_CONNECTED);
- return 0;
+ return false;
}
case -3: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_NOT_FOUND);
- return 0;
+ return false;
}
case -4: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_NOT_TRANSFERRING);
- return 0;
+ return false;
}
case -5: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_INVALID_LENGTH);
- return 0;
+ return false;
}
case -6: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_SENDQ);
- return 0;
+ return false;
}
case -7: {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_WRONG_POSITION);
- return 0;
+ return false;
}
}
/* can't happen */
LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
- return 0;
+ return false;
}
void tox_callback_file_chunk_request(Tox *tox, tox_file_chunk_request_cb *callback)
@@ -1754,9 +1855,9 @@ void tox_callback_conference_peer_list_changed(Tox *tox, tox_conference_peer_lis
uint32_t tox_conference_new(Tox *tox, Tox_Err_Conference_New *error)
{
assert(tox != nullptr);
- lock(tox);
- const int ret = add_groupchat(tox->m->conferences_object, GROUPCHAT_TYPE_TEXT);
- unlock(tox);
+ tox_lock(tox);
+ const int ret = add_groupchat(tox->m->conferences_object, &tox->rng, GROUPCHAT_TYPE_TEXT);
+ tox_unlock(tox);
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_NEW_INIT);
@@ -1770,11 +1871,11 @@ uint32_t tox_conference_new(Tox *tox, Tox_Err_Conference_New *error)
bool tox_conference_delete(Tox *tox, uint32_t conference_number, Tox_Err_Conference_Delete *error)
{
assert(tox != nullptr);
- lock(tox);
- const int ret = del_groupchat(tox->m->conferences_object, conference_number, true);
- unlock(tox);
+ tox_lock(tox);
+ const bool ret = del_groupchat(tox->m->conferences_object, conference_number, true);
+ tox_unlock(tox);
- if (ret == -1) {
+ if (!ret) {
SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_DELETE_CONFERENCE_NOT_FOUND);
return false;
}
@@ -1786,9 +1887,9 @@ bool tox_conference_delete(Tox *tox, uint32_t conference_number, Tox_Err_Confere
uint32_t tox_conference_peer_count(const Tox *tox, uint32_t conference_number, Tox_Err_Conference_Peer_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_number_peers(tox->m->conferences_object, conference_number, false);
- unlock(tox);
+ tox_unlock(tox);
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
@@ -1803,9 +1904,9 @@ size_t tox_conference_peer_get_name_size(const Tox *tox, uint32_t conference_num
Tox_Err_Conference_Peer_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_peername_size(tox->m->conferences_object, conference_number, peer_number, false);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -1827,9 +1928,9 @@ bool tox_conference_peer_get_name(const Tox *tox, uint32_t conference_number, ui
Tox_Err_Conference_Peer_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_peername(tox->m->conferences_object, conference_number, peer_number, name, false);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -1851,9 +1952,9 @@ bool tox_conference_peer_get_public_key(const Tox *tox, uint32_t conference_numb
uint8_t *public_key, Tox_Err_Conference_Peer_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_peer_pubkey(tox->m->conferences_object, conference_number, peer_number, public_key, false);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -1875,9 +1976,9 @@ bool tox_conference_peer_number_is_ours(const Tox *tox, uint32_t conference_numb
Tox_Err_Conference_Peer_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_peernumber_is_ours(tox->m->conferences_object, conference_number, peer_number);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -1897,16 +1998,16 @@ bool tox_conference_peer_number_is_ours(const Tox *tox, uint32_t conference_numb
}
SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
- return ret;
+ return ret != 0;
}
uint32_t tox_conference_offline_peer_count(const Tox *tox, uint32_t conference_number,
Tox_Err_Conference_Peer_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_number_peers(tox->m->conferences_object, conference_number, true);
- unlock(tox);
+ tox_unlock(tox);
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
@@ -1922,9 +2023,9 @@ size_t tox_conference_offline_peer_get_name_size(const Tox *tox, uint32_t confer
Tox_Err_Conference_Peer_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_peername_size(tox->m->conferences_object, conference_number, offline_peer_number, true);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -1947,9 +2048,9 @@ bool tox_conference_offline_peer_get_name(const Tox *tox, uint32_t conference_nu
Tox_Err_Conference_Peer_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_peername(tox->m->conferences_object, conference_number, offline_peer_number, name, true);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -1972,9 +2073,9 @@ bool tox_conference_offline_peer_get_public_key(const Tox *tox, uint32_t confere
uint8_t *public_key, Tox_Err_Conference_Peer_Query *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_peer_pubkey(tox->m->conferences_object, conference_number, offline_peer_number, public_key, true);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -1998,10 +2099,10 @@ uint64_t tox_conference_offline_peer_get_last_active(const Tox *tox, uint32_t co
{
assert(tox != nullptr);
uint64_t last_active = UINT64_MAX;
- lock(tox);
+ tox_lock(tox);
const int ret = group_frozen_last_active(tox->m->conferences_object, conference_number, offline_peer_number,
&last_active);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -2024,9 +2125,9 @@ bool tox_conference_set_max_offline(Tox *tox, uint32_t conference_number,
Tox_Err_Conference_Set_Max_Offline *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_set_max_frozen(tox->m->conferences_object, conference_number, max_offline_peers);
- unlock(tox);
+ tox_unlock(tox);
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_SET_MAX_OFFLINE_CONFERENCE_NOT_FOUND);
@@ -2041,9 +2142,9 @@ bool tox_conference_invite(Tox *tox, uint32_t friend_number, uint32_t conference
Tox_Err_Conference_Invite *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = invite_friend(tox->m->conferences_object, friend_number, conference_number);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -2070,9 +2171,9 @@ uint32_t tox_conference_join(Tox *tox, uint32_t friend_number, const uint8_t *co
Tox_Err_Conference_Join *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = join_groupchat(tox->m->conferences_object, friend_number, GROUPCHAT_TYPE_TEXT, cookie, length);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -2114,7 +2215,7 @@ bool tox_conference_send_message(Tox *tox, uint32_t conference_number, Tox_Messa
size_t length, Tox_Err_Conference_Send_Message *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
int ret = 0;
if (type == TOX_MESSAGE_TYPE_NORMAL) {
@@ -2123,7 +2224,7 @@ bool tox_conference_send_message(Tox *tox, uint32_t conference_number, Tox_Messa
ret = group_action_send(tox->m->conferences_object, conference_number, message, length);
}
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -2154,9 +2255,9 @@ bool tox_conference_send_message(Tox *tox, uint32_t conference_number, Tox_Messa
size_t tox_conference_get_title_size(const Tox *tox, uint32_t conference_number, Tox_Err_Conference_Title *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_title_get_size(tox->m->conferences_object, conference_number);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -2178,9 +2279,9 @@ bool tox_conference_get_title(const Tox *tox, uint32_t conference_number, uint8_
Tox_Err_Conference_Title *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_title_get(tox->m->conferences_object, conference_number, title);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -2202,9 +2303,9 @@ bool tox_conference_set_title(Tox *tox, uint32_t conference_number, const uint8_
Tox_Err_Conference_Title *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_title_send(tox->m->conferences_object, conference_number, title, length);
- unlock(tox);
+ tox_unlock(tox);
switch (ret) {
case -1: {
@@ -2230,28 +2331,28 @@ bool tox_conference_set_title(Tox *tox, uint32_t conference_number, const uint8_
size_t tox_conference_get_chatlist_size(const Tox *tox)
{
assert(tox != nullptr);
- lock(tox);
- size_t ret = count_chatlist(tox->m->conferences_object);
- unlock(tox);
+ tox_lock(tox);
+ const size_t ret = count_chatlist(tox->m->conferences_object);
+ tox_unlock(tox);
return ret;
}
void tox_conference_get_chatlist(const Tox *tox, uint32_t *chatlist)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const size_t list_size = count_chatlist(tox->m->conferences_object);
copy_chatlist(tox->m->conferences_object, chatlist, list_size);
- unlock(tox);
+ tox_unlock(tox);
}
Tox_Conference_Type tox_conference_get_type(const Tox *tox, uint32_t conference_number,
Tox_Err_Conference_Get_Type *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const int ret = group_get_type(tox->m->conferences_object, conference_number);
- unlock(tox);
+ tox_unlock(tox);
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_GET_TYPE_CONFERENCE_NOT_FOUND);
@@ -2265,9 +2366,9 @@ Tox_Conference_Type tox_conference_get_type(const Tox *tox, uint32_t conference_
bool tox_conference_get_id(const Tox *tox, uint32_t conference_number, uint8_t *id)
{
assert(tox != nullptr);
- lock(tox);
- bool ret = conference_get_id(tox->m->conferences_object, conference_number, id);
- unlock(tox);
+ tox_lock(tox);
+ const bool ret = conference_get_id(tox->m->conferences_object, conference_number, id);
+ tox_unlock(tox);
return ret;
}
@@ -2282,14 +2383,14 @@ uint32_t tox_conference_by_id(const Tox *tox, const uint8_t *id, Tox_Err_Confere
{
assert(tox != nullptr);
- if (!id) {
+ if (id == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_BY_ID_NULL);
return UINT32_MAX;
}
- lock(tox);
+ tox_lock(tox);
const int32_t ret = conference_by_id(tox->m->conferences_object, id);
- unlock(tox);
+ tox_unlock(tox);
if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_BY_ID_NOT_FOUND);
@@ -2297,7 +2398,8 @@ uint32_t tox_conference_by_id(const Tox *tox, const uint8_t *id, Tox_Err_Confere
}
SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_BY_ID_OK);
- return ret;
+ assert(ret >= 0);
+ return (uint32_t)ret;
}
// TODO(iphydf): Delete in 0.3.0.
@@ -2327,6 +2429,7 @@ uint32_t tox_conference_by_uid(const Tox *tox, const uint8_t *uid, Tox_Err_Confe
return res;
}
+nullable(2)
static void set_custom_packet_error(int ret, Tox_Err_Friend_Custom_Packet *error)
{
switch (ret) {
@@ -2367,32 +2470,28 @@ bool tox_friend_send_lossy_packet(Tox *tox, uint32_t friend_number, const uint8_
{
assert(tox != nullptr);
- if (!data) {
+ if (data == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_NULL);
- return 0;
+ return false;
}
if (length == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_EMPTY);
- return 0;
+ return false;
}
if (data[0] < PACKET_ID_RANGE_LOSSY_START || data[0] > PACKET_ID_RANGE_LOSSY_END) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_INVALID);
- return 0;
+ return false;
}
- lock(tox);
+ tox_lock(tox);
const int ret = m_send_custom_lossy_packet(tox->m, friend_number, data, length);
- unlock(tox);
+ tox_unlock(tox);
set_custom_packet_error(ret, error);
- if (ret == 0) {
- return 1;
- }
-
- return 0;
+ return ret == 0;
}
void tox_callback_friend_lossy_packet(Tox *tox, tox_friend_lossy_packet_cb *callback)
@@ -2405,41 +2504,28 @@ void tox_callback_friend_lossy_packet(Tox *tox, tox_friend_lossy_packet_cb *call
}
}
-void tox_callback_friend_lossy_packet_per_pktid(Tox *tox, tox_friend_lossy_packet_cb *callback, uint8_t pktid)
-{
- assert(tox != nullptr);
-
- if (pktid >= PACKET_ID_RANGE_LOSSY_START && pktid <= PACKET_ID_RANGE_LOSSY_END) {
- tox->friend_lossy_packet_callback_per_pktid[pktid] = callback;
- }
-}
-
bool tox_friend_send_lossless_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length,
Tox_Err_Friend_Custom_Packet *error)
{
assert(tox != nullptr);
- if (!data) {
+ if (data == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_NULL);
- return 0;
+ return false;
}
if (length == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_EMPTY);
- return 0;
+ return false;
}
- lock(tox);
+ tox_lock(tox);
const int ret = send_custom_lossless_packet(tox->m, friend_number, data, length);
- unlock(tox);
+ tox_unlock(tox);
set_custom_packet_error(ret, error);
- if (ret == 0) {
- return 1;
- }
-
- return 0;
+ return ret == 0;
}
void tox_callback_friend_lossless_packet(Tox *tox, tox_friend_lossless_packet_cb *callback)
@@ -2451,73 +2537,46 @@ void tox_callback_friend_lossless_packet(Tox *tox, tox_friend_lossless_packet_cb
}
}
-void tox_callback_friend_lossless_packet_per_pktid(Tox *tox, tox_friend_lossless_packet_cb *callback, uint8_t pktid)
-{
- assert(tox != nullptr);
-
- if ((pktid >= PACKET_ID_RANGE_LOSSLESS_CUSTOM_START && pktid <= PACKET_ID_RANGE_LOSSLESS_CUSTOM_END)
- || pktid == PACKET_ID_MSI) {
- tox->friend_lossless_packet_callback_per_pktid[pktid] = callback;
- }
-}
-
void tox_self_get_dht_id(const Tox *tox, uint8_t *dht_id)
{
assert(tox != nullptr);
- if (dht_id) {
- lock(tox);
+ if (dht_id != nullptr) {
+ tox_lock(tox);
memcpy(dht_id, dht_get_self_public_key(tox->m->dht), CRYPTO_PUBLIC_KEY_SIZE);
- unlock(tox);
+ tox_unlock(tox);
}
}
-void tox_set_av_object(Tox *tox, void *object)
-{
- assert(tox != nullptr);
- lock(tox);
- tox->toxav_object = object;
- unlock(tox);
-}
-
-void *tox_get_av_object(const Tox *tox)
-{
- assert(tox != nullptr);
- lock(tox);
- void *object = tox->toxav_object;
- unlock(tox);
- return object;
-}
-
uint16_t tox_self_get_udp_port(const Tox *tox, Tox_Err_Get_Port *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
const uint16_t port = net_htons(net_port(tox->m->net));
- unlock(tox);
+ tox_unlock(tox);
- if (port) {
- SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_OK);
- } else {
+ if (port == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_NOT_BOUND);
+ return 0;
}
+ SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_OK);
return port;
}
uint16_t tox_self_get_tcp_port(const Tox *tox, Tox_Err_Get_Port *error)
{
assert(tox != nullptr);
- lock(tox);
+ tox_lock(tox);
- if (tox->m->tcp_server) {
+ if (tox->m->tcp_server != nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_OK);
- uint16_t ret = tox->m->options.tcp_server_port;
- unlock(tox);
+ const uint16_t ret = tox->m->options.tcp_server_port;
+ tox_unlock(tox);
return ret;
}
SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_NOT_BOUND);
- unlock(tox);
+ tox_unlock(tox);
return 0;
}
diff --git a/protocols/Tox/libtox/src/toxcore/tox.h b/protocols/Tox/libtox/src/toxcore/tox.h
index 38ec9f4b5a..e8d55c6689 100644
--- a/protocols/Tox/libtox/src/toxcore/tox.h
+++ b/protocols/Tox/libtox/src/toxcore/tox.h
@@ -108,6 +108,8 @@
extern "C" {
#endif
+/** @{ @namespace tox */
+
#ifndef TOX_DEFINED
#define TOX_DEFINED
/**
@@ -156,14 +158,14 @@ uint32_t tox_version_minor(void);
* Incremented when bugfixes are applied without changing any functionality or
* API or ABI.
*/
-#define TOX_VERSION_PATCH 15
+#define TOX_VERSION_PATCH 18
uint32_t tox_version_patch(void);
//!TOKSTYLE-
/**
* @brief A macro to check at preprocessing time whether the client code is
- * compatible with the installed version of Tox.
+ * compatible with the installed version of Tox.
*
* Leading zeros in the version number are ignored. E.g. 0.1.5 is to 0.1.4
* what 1.5 is to 1.4, that is: it can add new features, but can't break the
@@ -187,13 +189,13 @@ uint32_t tox_version_patch(void);
/**
* @brief Return whether the compiled library version is compatible with the
- * passed version numbers.
+ * passed version numbers.
*/
bool tox_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch);
/**
* @brief A convenience macro to call tox_version_is_compatible with the
- * currently compiling API version.
+ * currently compiling API version.
*/
#define TOX_VERSION_IS_ABI_COMPATIBLE() \
tox_version_is_compatible(TOX_VERSION_MAJOR, TOX_VERSION_MINOR, TOX_VERSION_PATCH)
@@ -374,7 +376,7 @@ typedef enum Tox_User_Status {
/**
* @brief Represents message types for tox_friend_send_message and conference
- * messages.
+ * messages.
*/
typedef enum Tox_Message_Type {
@@ -465,12 +467,12 @@ typedef enum Tox_Log_Level {
TOX_LOG_LEVEL_INFO,
/**
- * Warnings about internal inconsistency or logic errors.
+ * Warnings about events_alloc inconsistency or logic errors.
*/
TOX_LOG_LEVEL_WARNING,
/**
- * Severe unexpected errors caused by external or internal inconsistency.
+ * Severe unexpected errors caused by external or events_alloc inconsistency.
*/
TOX_LOG_LEVEL_ERROR,
@@ -478,7 +480,7 @@ typedef enum Tox_Log_Level {
/**
- * @brief This event is triggered when the toxcore library logs an internal message.
+ * @brief This event is triggered when the toxcore library logs an events_alloc message.
*
* This is mostly useful for debugging. This callback can be called from any
* function, not just tox_iterate. This means the user data lifetime must at
@@ -500,6 +502,16 @@ typedef void tox_log_cb(Tox *tox, Tox_Log_Level level, const char *file, uint32_
/**
+ * @brief Operating system functions used by Tox.
+ *
+ * This struct is opaque and generally shouldn't be used in clients, but in
+ * combination with tox_private.h, it allows tests to inject non-IO (hermetic)
+ * versions of low level network, RNG, and time keeping functions.
+ */
+typedef struct Tox_System Tox_System;
+
+
+/**
* @brief This struct contains all the startup options for Tox.
*
* You must tox_options_new to allocate an object of this type.
@@ -509,9 +521,10 @@ typedef void tox_log_cb(Tox *tox, Tox_Log_Level level, const char *file, uint32_
* directly, as it *will* break binary compatibility frequently.
*
* @deprecated The memory layout of this struct (size, alignment, and field
- * order) is not part of the ABI. To remain compatible, prefer to use tox_options_new to
- * allocate the object and accessor functions to set the members. The struct
- * will become opaque (i.e. the definition will become private) in v0.3.0.
+ * order) is not part of the ABI. To remain compatible, prefer to use
+ * tox_options_new to allocate the object and accessor functions to set the
+ * members. The struct will become opaque (i.e. the definition will become
+ * private) in v0.3.0.
*/
struct Tox_Options {
@@ -547,6 +560,13 @@ struct Tox_Options {
/**
+ * Enable storing DHT announcements and forwarding corresponding requests.
+ *
+ * Disabling this will cause Tox to ignore the relevant packets.
+ */
+ bool dht_announcements_enabled;
+
+ /**
* Pass communications through a proxy.
*/
Tox_Proxy_Type proxy_type;
@@ -663,6 +683,12 @@ struct Tox_Options {
*/
bool experimental_thread_safety;
+ /**
+ * Low level operating system functionality such as send/recv and random
+ * number generation.
+ */
+ const Tox_System *operating_system;
+
};
@@ -678,6 +704,10 @@ bool tox_options_get_local_discovery_enabled(const struct Tox_Options *options);
void tox_options_set_local_discovery_enabled(struct Tox_Options *options, bool local_discovery_enabled);
+bool tox_options_get_dht_announcements_enabled(const struct Tox_Options *options);
+
+void tox_options_set_dht_announcements_enabled(struct Tox_Options *options, bool dht_announcements_enabled);
+
Tox_Proxy_Type tox_options_get_proxy_type(const struct Tox_Options *options);
void tox_options_set_proxy_type(struct Tox_Options *options, Tox_Proxy_Type type);
@@ -728,7 +758,11 @@ void tox_options_set_log_user_data(struct Tox_Options *options, void *user_data)
bool tox_options_get_experimental_thread_safety(const struct Tox_Options *options);
-void tox_options_set_experimental_thread_safety(struct Tox_Options *options, bool thread_safety);
+void tox_options_set_experimental_thread_safety(struct Tox_Options *options, bool experimental_thread_safety);
+
+const Tox_System *tox_options_get_operating_system(const struct Tox_Options *options);
+
+void tox_options_set_operating_system(struct Tox_Options *options, const Tox_System *operating_system);
/**
* @brief Initialises a Tox_Options object with the default options.
@@ -760,7 +794,7 @@ typedef enum Tox_Err_Options_New {
/**
* @brief Allocates a new Tox_Options object and initialises it with the default
- * options.
+ * options.
*
* This function can be used to preserve long term ABI compatibility by
* giving the responsibility of allocation and deallocation to the Tox library.
@@ -800,7 +834,7 @@ typedef enum Tox_Err_New {
TOX_ERR_NEW_NULL,
/**
- * The function was unable to allocate enough memory to store the internal
+ * The function was unable to allocate enough memory to store the events_alloc
* structures for the Tox object.
*/
TOX_ERR_NEW_MALLOC,
@@ -870,7 +904,7 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error);
/**
* @brief Releases all resources associated with the Tox instance and
- * disconnects from the network.
+ * disconnects from the network.
*
* After calling this function, the Tox pointer becomes invalid. No other
* functions can be called, and the pointer value can no longer be read.
@@ -879,7 +913,7 @@ void tox_kill(Tox *tox);
/**
* @brief Calculates the number of bytes required to store the tox instance with
- * tox_get_savedata.
+ * tox_get_savedata.
*
* This function cannot fail. The result is always greater than 0.
*
@@ -916,8 +950,9 @@ typedef enum Tox_Err_Bootstrap {
TOX_ERR_BOOTSTRAP_NULL,
/**
- * The hostname could not be resolved to an IP address, or the IP address
- * passed was invalid.
+ * The hostname could not be resolved to an IP address, the IP address
+ * passed was invalid, or the function failed to send the initial request
+ * packet to the bootstrap node or TCP relay.
*/
TOX_ERR_BOOTSTRAP_BAD_HOST,
@@ -930,8 +965,8 @@ typedef enum Tox_Err_Bootstrap {
/**
- * @brief Sends a "get nodes" request to the given bootstrap node with IP, port, and
- * public key to setup connections.
+ * @brief Sends a "get nodes" request to the given bootstrap node with IP, port,
+ * and public key to setup connections.
*
* This function will attempt to connect to the node using UDP. You must use
* this function even if Tox_Options.udp_enabled was set to false.
@@ -968,29 +1003,33 @@ bool tox_add_tcp_relay(Tox *tox, const char *host, uint16_t port, const uint8_t
typedef enum Tox_Connection {
/**
- * There is no connection. This instance, or the friend the state change is
- * about, is now offline.
+ * @brief There is no connection.
+ *
+ * This instance, or the friend the state change is about, is now offline.
*/
TOX_CONNECTION_NONE,
/**
- * A TCP connection has been established. For the own instance, this means it
- * is connected through a TCP relay, only. For a friend, this means that the
- * connection to that particular friend goes through a TCP relay.
+ * @brief A TCP connection has been established.
+ *
+ * For the own instance, this means it is connected through a TCP relay,
+ * only. For a friend, this means that the connection to that particular
+ * friend goes through a TCP relay.
*/
TOX_CONNECTION_TCP,
/**
- * A UDP connection has been established. For the own instance, this means it
- * is able to send UDP packets to DHT nodes, but may still be connected to
- * a TCP relay. For a friend, this means that the connection to that
- * particular friend was built using direct UDP packets.
+ * @brief A UDP connection has been established.
+ *
+ * For the own instance, this means it is able to send UDP packets to DHT
+ * nodes, but may still be connected to a TCP relay. For a friend, this
+ * means that the connection to that particular friend was built using
+ * direct UDP packets.
*/
TOX_CONNECTION_UDP,
} Tox_Connection;
-
/**
* @brief Return whether we are connected to the DHT.
*
@@ -998,7 +1037,7 @@ typedef enum Tox_Connection {
* `self_connection_status` callback.
*
* @deprecated This getter is deprecated. Use the event and store the status
- * in the client state.
+ * in the client state.
*/
Tox_Connection tox_self_get_connection_status(const Tox *tox);
@@ -1024,14 +1063,14 @@ typedef void tox_self_connection_status_cb(Tox *tox, Tox_Connection connection_s
void tox_callback_self_connection_status(Tox *tox, tox_self_connection_status_cb *callback);
/**
- * @brief Return the time in milliseconds before tox_iterate() should be called again
- * for optimal performance.
+ * @brief Return the time in milliseconds before `tox_iterate()` should be called again
+ * for optimal performance.
*/
uint32_t tox_iteration_interval(const Tox *tox);
/**
- * @brief The main loop that needs to be run in intervals of tox_iteration_interval()
- * milliseconds.
+ * @brief The main loop that needs to be run in intervals of `tox_iteration_interval()`
+ * milliseconds.
*/
void tox_iterate(Tox *tox, void *user_data);
@@ -1096,7 +1135,7 @@ void tox_self_get_secret_key(const Tox *tox, uint8_t *secret_key);
/**
* @brief Common error codes for all functions that set a piece of user-visible
- * client information.
+ * client information.
*/
typedef enum Tox_Err_Set_Info {
@@ -1652,10 +1691,10 @@ bool tox_friend_get_typing(const Tox *tox, uint32_t friend_number, Tox_Err_Frien
/**
* @param friend_number The friend number of the friend who started or stopped
* typing.
- * @param is_typing The result of calling tox_friend_get_typing on the passed
+ * @param typing The result of calling tox_friend_get_typing on the passed
* friend_number.
*/
-typedef void tox_friend_typing_cb(Tox *tox, uint32_t friend_number, bool is_typing, void *user_data);
+typedef void tox_friend_typing_cb(Tox *tox, uint32_t friend_number, bool typing, void *user_data);
/**
@@ -1845,7 +1884,7 @@ void tox_callback_friend_message(Tox *tox, tox_friend_message_cb *callback);
* If hash is NULL or data is NULL while length is not 0 the function returns false,
* otherwise it returns true.
*
- * This function is a wrapper to internal message-digest functions.
+ * This function is a wrapper to events_alloc message-digest functions.
*
* @param hash A valid memory location the hash data. It must be at least
* TOX_HASH_LENGTH bytes in size.
@@ -2147,8 +2186,8 @@ typedef enum Tox_Err_File_Send {
* should generally just be a file name, not a path with directory names.
*
* If a non-UINT64_MAX file size is provided, it can be used by both sides to
- * determine the sending progress. File size can be set to UINT64_MAX for streaming
- * data of unknown size.
+ * determine the sending progress. File size can be set to UINT64_MAX for
+ * streaming data of unknown size.
*
* File transmission occurs in chunks, which are requested through the
* `file_chunk_request` event.
@@ -2163,12 +2202,11 @@ typedef enum Tox_Err_File_Send {
* - If the file size was increased
* - and sending mode was streaming (file_size = UINT64_MAX), the behaviour
* will be as expected.
- * - and sending mode was file (file_size != UINT64_MAX), the
- * file_chunk_request callback will receive length = 0 when Core thinks
- * the file transfer has finished. If the client remembers the file size as
- * it was when sending the request, it will terminate the transfer normally.
- * If the client re-reads the size, it will think the friend cancelled the
- * transfer.
+ * - and sending mode was file (file_size != UINT64_MAX), the file_chunk_request
+ * callback will receive length = 0 when Core thinks the file transfer has
+ * finished. If the client remembers the file size as it was when sending the
+ * request, it will terminate the transfer normally. If the client re-reads the
+ * size, it will think the friend cancelled the transfer.
* - If the file size was decreased
* - and sending mode was streaming, the behaviour is as expected.
* - and sending mode was file, the callback will return 0 at the new
@@ -2189,7 +2227,7 @@ typedef enum Tox_Err_File_Send {
* unknown or streaming.
* @param file_id A file identifier of length TOX_FILE_ID_LENGTH that can be used to
* uniquely identify file transfers across core restarts. If NULL, a random one will
- * be generated by core. It can then be obtained by using tox_file_get_file_id().
+ * be generated by core. It can then be obtained by using `tox_file_get_file_id()`.
* @param filename Name of the file. Does not need to be the actual name. This
* name will be sent along with the file send request.
* @param filename_length Size in bytes of the filename.
@@ -2442,7 +2480,8 @@ typedef void tox_conference_connected_cb(Tox *tox, uint32_t conference_number, v
void tox_callback_conference_connected(Tox *tox, tox_conference_connected_cb *callback);
/**
- * @param conference_number The conference number of the conference the message is intended for.
+ * @param conference_number The conference number of the conference the message
+ * is intended for.
* @param peer_number The ID of the peer who sent the message.
* @param type The type of message (normal, action, ...).
* @param message The message data.
@@ -2462,7 +2501,8 @@ typedef void tox_conference_message_cb(Tox *tox, uint32_t conference_number, uin
void tox_callback_conference_message(Tox *tox, tox_conference_message_cb *callback);
/**
- * @param conference_number The conference number of the conference the title change is intended for.
+ * @param conference_number The conference number of the conference the title
+ * change is intended for.
* @param peer_number The ID of the peer who changed the title.
* @param title The title data.
* @param length The title length.
@@ -2663,7 +2703,8 @@ size_t tox_conference_offline_peer_get_name_size(const Tox *tox, uint32_t confer
/**
* @brief Copy the name of offline_peer_number who is in conference_number to name.
*
- * Call tox_conference_offline_peer_get_name_size to determine the allocation size for the `name` parameter.
+ * Call tox_conference_offline_peer_get_name_size to determine the allocation
+ * size for the `name` parameter.
*
* @param name A valid memory region large enough to store the peer's name.
*
@@ -2845,7 +2886,8 @@ typedef enum Tox_Err_Conference_Send_Message {
* must be split by the client and sent as separate messages. Other clients can
* then reassemble the fragments.
*
- * @param conference_number The conference number of the conference the message is intended for.
+ * @param conference_number The conference number of the conference the message
+ * is intended for.
* @param type Message type (normal, action, ...).
* @param message A non-NULL pointer to the first element of a byte array
* containing the message text.
@@ -3225,6 +3267,8 @@ uint16_t tox_self_get_tcp_port(const Tox *tox, Tox_Err_Get_Port *error);
/** @} */
+/** @} */
+
#ifdef __cplusplus
}
#endif
diff --git a/protocols/Tox/libtox/src/toxcore/tox_api.c b/protocols/Tox/libtox/src/toxcore/tox_api.c
index 89524ae4e2..00051e497b 100644
--- a/protocols/Tox/libtox/src/toxcore/tox_api.c
+++ b/protocols/Tox/libtox/src/toxcore/tox_api.c
@@ -7,41 +7,97 @@
#include <string.h>
#include "ccompat.h"
+#include "tox_private.h"
#define SET_ERROR_PARAMETER(param, x) \
do { \
- if (param) { \
+ if (param != nullptr) { \
*param = x; \
} \
} while (0)
-//!TOKSTYLE-
-
-#define CONST_FUNCTION(lowercase, uppercase) \
-uint32_t tox_##lowercase(void) \
-{ \
- return TOX_##uppercase; \
+uint32_t tox_version_major(void)
+{
+ return TOX_VERSION_MAJOR;
+}
+uint32_t tox_version_minor(void)
+{
+ return TOX_VERSION_MINOR;
+}
+uint32_t tox_version_patch(void)
+{
+ return TOX_VERSION_PATCH;
+}
+uint32_t tox_public_key_size(void)
+{
+ return TOX_PUBLIC_KEY_SIZE;
+}
+uint32_t tox_secret_key_size(void)
+{
+ return TOX_SECRET_KEY_SIZE;
+}
+uint32_t tox_conference_uid_size(void)
+{
+ return TOX_CONFERENCE_UID_SIZE;
+}
+uint32_t tox_conference_id_size(void)
+{
+ return TOX_CONFERENCE_ID_SIZE;
+}
+uint32_t tox_nospam_size(void)
+{
+ return TOX_NOSPAM_SIZE;
+}
+uint32_t tox_address_size(void)
+{
+ return TOX_ADDRESS_SIZE;
+}
+uint32_t tox_max_name_length(void)
+{
+ return TOX_MAX_NAME_LENGTH;
+}
+uint32_t tox_max_status_message_length(void)
+{
+ return TOX_MAX_STATUS_MESSAGE_LENGTH;
+}
+uint32_t tox_max_friend_request_length(void)
+{
+ return TOX_MAX_FRIEND_REQUEST_LENGTH;
+}
+uint32_t tox_max_message_length(void)
+{
+ return TOX_MAX_MESSAGE_LENGTH;
+}
+uint32_t tox_max_custom_packet_size(void)
+{
+ return TOX_MAX_CUSTOM_PACKET_SIZE;
+}
+uint32_t tox_hash_length(void)
+{
+ return TOX_HASH_LENGTH;
+}
+uint32_t tox_file_id_length(void)
+{
+ return TOX_FILE_ID_LENGTH;
+}
+uint32_t tox_max_filename_length(void)
+{
+ return TOX_MAX_FILENAME_LENGTH;
+}
+uint32_t tox_max_hostname_length(void)
+{
+ return TOX_MAX_HOSTNAME_LENGTH;
+}
+uint32_t tox_dht_node_ip_string_size(void)
+{
+ return TOX_DHT_NODE_IP_STRING_SIZE;
+}
+uint32_t tox_dht_node_public_key_size(void)
+{
+ return TOX_DHT_NODE_PUBLIC_KEY_SIZE;
}
-CONST_FUNCTION(version_major, VERSION_MAJOR)
-CONST_FUNCTION(version_minor, VERSION_MINOR)
-CONST_FUNCTION(version_patch, VERSION_PATCH)
-CONST_FUNCTION(public_key_size, PUBLIC_KEY_SIZE)
-CONST_FUNCTION(secret_key_size, SECRET_KEY_SIZE)
-CONST_FUNCTION(conference_uid_size, CONFERENCE_UID_SIZE)
-CONST_FUNCTION(conference_id_size, CONFERENCE_ID_SIZE)
-CONST_FUNCTION(nospam_size, NOSPAM_SIZE)
-CONST_FUNCTION(address_size, ADDRESS_SIZE)
-CONST_FUNCTION(max_name_length, MAX_NAME_LENGTH)
-CONST_FUNCTION(max_status_message_length, MAX_STATUS_MESSAGE_LENGTH)
-CONST_FUNCTION(max_friend_request_length, MAX_FRIEND_REQUEST_LENGTH)
-CONST_FUNCTION(max_message_length, MAX_MESSAGE_LENGTH)
-CONST_FUNCTION(max_custom_packet_size, MAX_CUSTOM_PACKET_SIZE)
-CONST_FUNCTION(hash_length, HASH_LENGTH)
-CONST_FUNCTION(file_id_length, FILE_ID_LENGTH)
-CONST_FUNCTION(max_filename_length, MAX_FILENAME_LENGTH)
-CONST_FUNCTION(max_hostname_length, MAX_HOSTNAME_LENGTH)
-
+//!TOKSTYLE-
#define ACCESSORS(type, ns, name) \
type tox_options_get_##ns##name(const struct Tox_Options *options) \
@@ -67,7 +123,9 @@ ACCESSORS(size_t, savedata_, length)
ACCESSORS(tox_log_cb *, log_, callback)
ACCESSORS(void *, log_, user_data)
ACCESSORS(bool,, local_discovery_enabled)
+ACCESSORS(bool,, dht_announcements_enabled)
ACCESSORS(bool,, experimental_thread_safety)
+ACCESSORS(const Tox_System *,, operating_system)
//!TOKSTYLE+
@@ -84,14 +142,15 @@ void tox_options_set_savedata_data(struct Tox_Options *options, const uint8_t *d
void tox_options_default(struct Tox_Options *options)
{
- if (options) {
- struct Tox_Options default_options = { 0 };
+ if (options != nullptr) {
+ const struct Tox_Options default_options = {0};
*options = default_options;
tox_options_set_ipv6_enabled(options, true);
tox_options_set_udp_enabled(options, true);
tox_options_set_proxy_type(options, TOX_PROXY_TYPE_NONE);
tox_options_set_hole_punching_enabled(options, true);
tox_options_set_local_discovery_enabled(options, true);
+ tox_options_set_dht_announcements_enabled(options, true);
tox_options_set_experimental_thread_safety(options, false);
}
}
@@ -100,7 +159,7 @@ struct Tox_Options *tox_options_new(Tox_Err_Options_New *error)
{
struct Tox_Options *options = (struct Tox_Options *)calloc(1, sizeof(struct Tox_Options));
- if (options) {
+ if (options != nullptr) {
tox_options_default(options);
SET_ERROR_PARAMETER(error, TOX_ERR_OPTIONS_NEW_OK);
return options;
diff --git a/protocols/Tox/libtox/src/toxcore/tox_dispatch.c b/protocols/Tox/libtox/src/toxcore/tox_dispatch.c
new file mode 100644
index 0000000000..4b4546e4a4
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/tox_dispatch.c
@@ -0,0 +1,484 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022 The TokTok team.
+ */
+
+#include "tox_dispatch.h"
+
+#include <stdlib.h>
+
+#include "ccompat.h"
+
+struct Tox_Dispatch {
+ tox_events_conference_connected_cb *conference_connected_callback;
+ tox_events_conference_invite_cb *conference_invite_callback;
+ tox_events_conference_message_cb *conference_message_callback;
+ tox_events_conference_peer_list_changed_cb *conference_peer_list_changed_callback;
+ tox_events_conference_peer_name_cb *conference_peer_name_callback;
+ tox_events_conference_title_cb *conference_title_callback;
+ tox_events_file_chunk_request_cb *file_chunk_request_callback;
+ tox_events_file_recv_cb *file_recv_callback;
+ tox_events_file_recv_chunk_cb *file_recv_chunk_callback;
+ tox_events_file_recv_control_cb *file_recv_control_callback;
+ tox_events_friend_connection_status_cb *friend_connection_status_callback;
+ tox_events_friend_lossless_packet_cb *friend_lossless_packet_callback;
+ tox_events_friend_lossy_packet_cb *friend_lossy_packet_callback;
+ tox_events_friend_message_cb *friend_message_callback;
+ tox_events_friend_name_cb *friend_name_callback;
+ tox_events_friend_read_receipt_cb *friend_read_receipt_callback;
+ tox_events_friend_request_cb *friend_request_callback;
+ tox_events_friend_status_cb *friend_status_callback;
+ tox_events_friend_status_message_cb *friend_status_message_callback;
+ tox_events_friend_typing_cb *friend_typing_callback;
+ tox_events_self_connection_status_cb *self_connection_status_callback;
+};
+
+Tox_Dispatch *tox_dispatch_new(Tox_Err_Dispatch_New *error)
+{
+ Tox_Dispatch *dispatch = (Tox_Dispatch *)calloc(1, sizeof(Tox_Dispatch));
+
+ if (dispatch == nullptr) {
+ if (error != nullptr) {
+ *error = TOX_ERR_DISPATCH_NEW_MALLOC;
+ }
+
+ return nullptr;
+ }
+
+ *dispatch = (Tox_Dispatch) {
+ nullptr
+ };
+ if (error != nullptr) {
+ *error = TOX_ERR_DISPATCH_NEW_OK;
+ }
+ return dispatch;
+}
+
+void tox_dispatch_free(Tox_Dispatch *dispatch)
+{
+ free(dispatch);
+}
+
+void tox_events_callback_conference_connected(
+ Tox_Dispatch *dispatch, tox_events_conference_connected_cb *callback)
+{
+ dispatch->conference_connected_callback = callback;
+}
+void tox_events_callback_conference_invite(
+ Tox_Dispatch *dispatch, tox_events_conference_invite_cb *callback)
+{
+ dispatch->conference_invite_callback = callback;
+}
+void tox_events_callback_conference_message(
+ Tox_Dispatch *dispatch, tox_events_conference_message_cb *callback)
+{
+ dispatch->conference_message_callback = callback;
+}
+void tox_events_callback_conference_peer_list_changed(
+ Tox_Dispatch *dispatch, tox_events_conference_peer_list_changed_cb *callback)
+{
+ dispatch->conference_peer_list_changed_callback = callback;
+}
+void tox_events_callback_conference_peer_name(
+ Tox_Dispatch *dispatch, tox_events_conference_peer_name_cb *callback)
+{
+ dispatch->conference_peer_name_callback = callback;
+}
+void tox_events_callback_conference_title(
+ Tox_Dispatch *dispatch, tox_events_conference_title_cb *callback)
+{
+ dispatch->conference_title_callback = callback;
+}
+void tox_events_callback_file_chunk_request(
+ Tox_Dispatch *dispatch, tox_events_file_chunk_request_cb *callback)
+{
+ dispatch->file_chunk_request_callback = callback;
+}
+void tox_events_callback_file_recv(
+ Tox_Dispatch *dispatch, tox_events_file_recv_cb *callback)
+{
+ dispatch->file_recv_callback = callback;
+}
+void tox_events_callback_file_recv_chunk(
+ Tox_Dispatch *dispatch, tox_events_file_recv_chunk_cb *callback)
+{
+ dispatch->file_recv_chunk_callback = callback;
+}
+void tox_events_callback_file_recv_control(
+ Tox_Dispatch *dispatch, tox_events_file_recv_control_cb *callback)
+{
+ dispatch->file_recv_control_callback = callback;
+}
+void tox_events_callback_friend_connection_status(
+ Tox_Dispatch *dispatch, tox_events_friend_connection_status_cb *callback)
+{
+ dispatch->friend_connection_status_callback = callback;
+}
+void tox_events_callback_friend_lossless_packet(
+ Tox_Dispatch *dispatch, tox_events_friend_lossless_packet_cb *callback)
+{
+ dispatch->friend_lossless_packet_callback = callback;
+}
+void tox_events_callback_friend_lossy_packet(
+ Tox_Dispatch *dispatch, tox_events_friend_lossy_packet_cb *callback)
+{
+ dispatch->friend_lossy_packet_callback = callback;
+}
+void tox_events_callback_friend_message(
+ Tox_Dispatch *dispatch, tox_events_friend_message_cb *callback)
+{
+ dispatch->friend_message_callback = callback;
+}
+void tox_events_callback_friend_name(
+ Tox_Dispatch *dispatch, tox_events_friend_name_cb *callback)
+{
+ dispatch->friend_name_callback = callback;
+}
+void tox_events_callback_friend_read_receipt(
+ Tox_Dispatch *dispatch, tox_events_friend_read_receipt_cb *callback)
+{
+ dispatch->friend_read_receipt_callback = callback;
+}
+void tox_events_callback_friend_request(
+ Tox_Dispatch *dispatch, tox_events_friend_request_cb *callback)
+{
+ dispatch->friend_request_callback = callback;
+}
+void tox_events_callback_friend_status(
+ Tox_Dispatch *dispatch, tox_events_friend_status_cb *callback)
+{
+ dispatch->friend_status_callback = callback;
+}
+void tox_events_callback_friend_status_message(
+ Tox_Dispatch *dispatch, tox_events_friend_status_message_cb *callback)
+{
+ dispatch->friend_status_message_callback = callback;
+}
+void tox_events_callback_friend_typing(
+ Tox_Dispatch *dispatch, tox_events_friend_typing_cb *callback)
+{
+ dispatch->friend_typing_callback = callback;
+}
+void tox_events_callback_self_connection_status(
+ Tox_Dispatch *dispatch, tox_events_self_connection_status_cb *callback)
+{
+ dispatch->self_connection_status_callback = callback;
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_conference_connected(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_conference_connected_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->conference_connected_callback != nullptr) {
+ dispatch->conference_connected_callback(
+ tox, tox_events_get_conference_connected(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_conference_invite(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_conference_invite_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->conference_invite_callback != nullptr) {
+ dispatch->conference_invite_callback(
+ tox, tox_events_get_conference_invite(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_conference_message(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_conference_message_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->conference_message_callback != nullptr) {
+ dispatch->conference_message_callback(
+ tox, tox_events_get_conference_message(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_conference_peer_list_changed(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_conference_peer_list_changed_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->conference_peer_list_changed_callback != nullptr) {
+ dispatch->conference_peer_list_changed_callback(
+ tox, tox_events_get_conference_peer_list_changed(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_conference_peer_name(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_conference_peer_name_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->conference_peer_name_callback != nullptr) {
+ dispatch->conference_peer_name_callback(
+ tox, tox_events_get_conference_peer_name(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_conference_title(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_conference_title_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->conference_title_callback != nullptr) {
+ dispatch->conference_title_callback(
+ tox, tox_events_get_conference_title(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_file_chunk_request(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_file_chunk_request_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->file_chunk_request_callback != nullptr) {
+ dispatch->file_chunk_request_callback(
+ tox, tox_events_get_file_chunk_request(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_file_recv(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_file_recv_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->file_recv_callback != nullptr) {
+ dispatch->file_recv_callback(
+ tox, tox_events_get_file_recv(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_file_recv_chunk(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_file_recv_chunk_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->file_recv_chunk_callback != nullptr) {
+ dispatch->file_recv_chunk_callback(
+ tox, tox_events_get_file_recv_chunk(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_file_recv_control(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_file_recv_control_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->file_recv_control_callback != nullptr) {
+ dispatch->file_recv_control_callback(
+ tox, tox_events_get_file_recv_control(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_friend_connection_status(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_friend_connection_status_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->friend_connection_status_callback != nullptr) {
+ dispatch->friend_connection_status_callback(
+ tox, tox_events_get_friend_connection_status(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_friend_lossless_packet(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_friend_lossless_packet_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->friend_lossless_packet_callback != nullptr) {
+ dispatch->friend_lossless_packet_callback(
+ tox, tox_events_get_friend_lossless_packet(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_friend_lossy_packet(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_friend_lossy_packet_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->friend_lossy_packet_callback != nullptr) {
+ dispatch->friend_lossy_packet_callback(
+ tox, tox_events_get_friend_lossy_packet(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_friend_message(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_friend_message_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->friend_message_callback != nullptr) {
+ dispatch->friend_message_callback(
+ tox, tox_events_get_friend_message(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_friend_name(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_friend_name_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->friend_name_callback != nullptr) {
+ dispatch->friend_name_callback(
+ tox, tox_events_get_friend_name(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_friend_read_receipt(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_friend_read_receipt_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->friend_read_receipt_callback != nullptr) {
+ dispatch->friend_read_receipt_callback(
+ tox, tox_events_get_friend_read_receipt(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_friend_request(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_friend_request_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->friend_request_callback != nullptr) {
+ dispatch->friend_request_callback(
+ tox, tox_events_get_friend_request(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_friend_status(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_friend_status_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->friend_status_callback != nullptr) {
+ dispatch->friend_status_callback(
+ tox, tox_events_get_friend_status(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_friend_status_message(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_friend_status_message_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->friend_status_message_callback != nullptr) {
+ dispatch->friend_status_message_callback(
+ tox, tox_events_get_friend_status_message(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_friend_typing(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_friend_typing_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->friend_typing_callback != nullptr) {
+ dispatch->friend_typing_callback(
+ tox, tox_events_get_friend_typing(events, i), user_data);
+ }
+ }
+}
+
+non_null(1, 3) nullable(2, 4)
+static void tox_dispatch_invoke_self_connection_status(
+ const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ const uint32_t size = tox_events_get_self_connection_status_size(events);
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (dispatch->self_connection_status_callback != nullptr) {
+ dispatch->self_connection_status_callback(
+ tox, tox_events_get_self_connection_status(events, i), user_data);
+ }
+ }
+}
+
+void tox_dispatch_invoke(const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data)
+{
+ tox_dispatch_invoke_conference_connected(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_conference_invite(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_conference_message(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_conference_peer_list_changed(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_conference_peer_name(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_conference_title(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_file_chunk_request(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_file_recv(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_file_recv_chunk(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_file_recv_control(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_friend_connection_status(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_friend_lossless_packet(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_friend_lossy_packet(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_friend_message(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_friend_name(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_friend_read_receipt(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_friend_request(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_friend_status(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_friend_status_message(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_friend_typing(dispatch, events, tox, user_data);
+ tox_dispatch_invoke_self_connection_status(dispatch, events, tox, user_data);
+}
diff --git a/protocols/Tox/libtox/src/toxcore/tox_dispatch.h b/protocols/Tox/libtox/src/toxcore/tox_dispatch.h
new file mode 100644
index 0000000000..7e8fbba376
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/tox_dispatch.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_DISPATCH_H
+#define C_TOXCORE_TOXCORE_TOX_DISPATCH_H
+
+#include "tox_events.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief The events dispatch table.
+ *
+ * This holds all the callbacks registered with `tox_events_callback_*`
+ * functions below.
+ */
+typedef struct Tox_Dispatch Tox_Dispatch;
+
+typedef enum Tox_Err_Dispatch_New {
+ /**
+ * The function returned successfully.
+ */
+ TOX_ERR_DISPATCH_NEW_OK,
+
+ /**
+ * The function failed to allocate memory for the dispatch table.
+ */
+ TOX_ERR_DISPATCH_NEW_MALLOC,
+} Tox_Err_Dispatch_New;
+
+/**
+ * @brief Creates a new empty event dispatch table.
+ */
+Tox_Dispatch *tox_dispatch_new(Tox_Err_Dispatch_New *error);
+
+/**
+ * @brief Deallocate an event dispatch table.
+ */
+void tox_dispatch_free(Tox_Dispatch *dispatch);
+
+/**
+ * @brief Invoke registered callbacks for each of the events.
+ *
+ * @param dispatch The events dispatch table.
+ * @param events The events object received from @ref tox_events_iterate.
+ * @param tox The tox object to pass down to the callbacks.
+ * @param user_data User data pointer to pass down to the callbacks.
+ */
+void tox_dispatch_invoke(const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data);
+
+typedef void tox_events_conference_connected_cb(
+ Tox *tox, const Tox_Event_Conference_Connected *event, void *user_data);
+typedef void tox_events_conference_invite_cb(
+ Tox *tox, const Tox_Event_Conference_Invite *event, void *user_data);
+typedef void tox_events_conference_message_cb(
+ Tox *tox, const Tox_Event_Conference_Message *event, void *user_data);
+typedef void tox_events_conference_peer_list_changed_cb(
+ Tox *tox, const Tox_Event_Conference_Peer_List_Changed *event, void *user_data);
+typedef void tox_events_conference_peer_name_cb(
+ Tox *tox, const Tox_Event_Conference_Peer_Name *event, void *user_data);
+typedef void tox_events_conference_title_cb(
+ Tox *tox, const Tox_Event_Conference_Title *event, void *user_data);
+typedef void tox_events_file_chunk_request_cb(
+ Tox *tox, const Tox_Event_File_Chunk_Request *event, void *user_data);
+typedef void tox_events_file_recv_cb(
+ Tox *tox, const Tox_Event_File_Recv *event, void *user_data);
+typedef void tox_events_file_recv_chunk_cb(
+ Tox *tox, const Tox_Event_File_Recv_Chunk *event, void *user_data);
+typedef void tox_events_file_recv_control_cb(
+ Tox *tox, const Tox_Event_File_Recv_Control *event, void *user_data);
+typedef void tox_events_friend_connection_status_cb(
+ Tox *tox, const Tox_Event_Friend_Connection_Status *event, void *user_data);
+typedef void tox_events_friend_lossless_packet_cb(
+ Tox *tox, const Tox_Event_Friend_Lossless_Packet *event, void *user_data);
+typedef void tox_events_friend_lossy_packet_cb(
+ Tox *tox, const Tox_Event_Friend_Lossy_Packet *event, void *user_data);
+typedef void tox_events_friend_message_cb(
+ Tox *tox, const Tox_Event_Friend_Message *event, void *user_data);
+typedef void tox_events_friend_name_cb(
+ Tox *tox, const Tox_Event_Friend_Name *event, void *user_data);
+typedef void tox_events_friend_read_receipt_cb(
+ Tox *tox, const Tox_Event_Friend_Read_Receipt *event, void *user_data);
+typedef void tox_events_friend_request_cb(
+ Tox *tox, const Tox_Event_Friend_Request *event, void *user_data);
+typedef void tox_events_friend_status_cb(
+ Tox *tox, const Tox_Event_Friend_Status *event, void *user_data);
+typedef void tox_events_friend_status_message_cb(
+ Tox *tox, const Tox_Event_Friend_Status_Message *event, void *user_data);
+typedef void tox_events_friend_typing_cb(
+ Tox *tox, const Tox_Event_Friend_Typing *event, void *user_data);
+typedef void tox_events_self_connection_status_cb(
+ Tox *tox, const Tox_Event_Self_Connection_Status *event, void *user_data);
+
+void tox_events_callback_conference_connected(
+ Tox_Dispatch *dispatch, tox_events_conference_connected_cb *callback);
+void tox_events_callback_conference_invite(
+ Tox_Dispatch *dispatch, tox_events_conference_invite_cb *callback);
+void tox_events_callback_conference_message(
+ Tox_Dispatch *dispatch, tox_events_conference_message_cb *callback);
+void tox_events_callback_conference_peer_list_changed(
+ Tox_Dispatch *dispatch, tox_events_conference_peer_list_changed_cb *callback);
+void tox_events_callback_conference_peer_name(
+ Tox_Dispatch *dispatch, tox_events_conference_peer_name_cb *callback);
+void tox_events_callback_conference_title(
+ Tox_Dispatch *dispatch, tox_events_conference_title_cb *callback);
+void tox_events_callback_file_chunk_request(
+ Tox_Dispatch *dispatch, tox_events_file_chunk_request_cb *callback);
+void tox_events_callback_file_recv(
+ Tox_Dispatch *dispatch, tox_events_file_recv_cb *callback);
+void tox_events_callback_file_recv_chunk(
+ Tox_Dispatch *dispatch, tox_events_file_recv_chunk_cb *callback);
+void tox_events_callback_file_recv_control(
+ Tox_Dispatch *dispatch, tox_events_file_recv_control_cb *callback);
+void tox_events_callback_friend_connection_status(
+ Tox_Dispatch *dispatch, tox_events_friend_connection_status_cb *callback);
+void tox_events_callback_friend_lossless_packet(
+ Tox_Dispatch *dispatch, tox_events_friend_lossless_packet_cb *callback);
+void tox_events_callback_friend_lossy_packet(
+ Tox_Dispatch *dispatch, tox_events_friend_lossy_packet_cb *callback);
+void tox_events_callback_friend_message(
+ Tox_Dispatch *dispatch, tox_events_friend_message_cb *callback);
+void tox_events_callback_friend_name(
+ Tox_Dispatch *dispatch, tox_events_friend_name_cb *callback);
+void tox_events_callback_friend_read_receipt(
+ Tox_Dispatch *dispatch, tox_events_friend_read_receipt_cb *callback);
+void tox_events_callback_friend_request(
+ Tox_Dispatch *dispatch, tox_events_friend_request_cb *callback);
+void tox_events_callback_friend_status(
+ Tox_Dispatch *dispatch, tox_events_friend_status_cb *callback);
+void tox_events_callback_friend_status_message(
+ Tox_Dispatch *dispatch, tox_events_friend_status_message_cb *callback);
+void tox_events_callback_friend_typing(
+ Tox_Dispatch *dispatch, tox_events_friend_typing_cb *callback);
+void tox_events_callback_self_connection_status(
+ Tox_Dispatch *dispatch, tox_events_self_connection_status_cb *callback);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // C_TOXCORE_TOXCORE_TOX_DISPATCH_H
diff --git a/protocols/Tox/libtox/src/toxcore/tox_events.c b/protocols/Tox/libtox/src/toxcore/tox_events.c
new file mode 100644
index 0000000000..91a7767f7b
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/tox_events.c
@@ -0,0 +1,290 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022 The TokTok team.
+ */
+
+#include "tox_events.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bin_unpack.h"
+#include "ccompat.h"
+#include "events/events_alloc.h"
+#include "tox.h"
+
+
+/*****************************************************
+ *
+ * :: Set up event handlers.
+ *
+ *****************************************************/
+
+
+void tox_events_init(Tox *tox)
+{
+ tox_callback_conference_connected(tox, tox_events_handle_conference_connected);
+ tox_callback_conference_invite(tox, tox_events_handle_conference_invite);
+ tox_callback_conference_message(tox, tox_events_handle_conference_message);
+ tox_callback_conference_peer_list_changed(tox, tox_events_handle_conference_peer_list_changed);
+ tox_callback_conference_peer_name(tox, tox_events_handle_conference_peer_name);
+ tox_callback_conference_title(tox, tox_events_handle_conference_title);
+ tox_callback_file_chunk_request(tox, tox_events_handle_file_chunk_request);
+ tox_callback_file_recv_chunk(tox, tox_events_handle_file_recv_chunk);
+ tox_callback_file_recv_control(tox, tox_events_handle_file_recv_control);
+ tox_callback_file_recv(tox, tox_events_handle_file_recv);
+ tox_callback_friend_connection_status(tox, tox_events_handle_friend_connection_status);
+ tox_callback_friend_lossless_packet(tox, tox_events_handle_friend_lossless_packet);
+ tox_callback_friend_lossy_packet(tox, tox_events_handle_friend_lossy_packet);
+ tox_callback_friend_message(tox, tox_events_handle_friend_message);
+ tox_callback_friend_name(tox, tox_events_handle_friend_name);
+ tox_callback_friend_read_receipt(tox, tox_events_handle_friend_read_receipt);
+ tox_callback_friend_request(tox, tox_events_handle_friend_request);
+ tox_callback_friend_status_message(tox, tox_events_handle_friend_status_message);
+ tox_callback_friend_status(tox, tox_events_handle_friend_status);
+ tox_callback_friend_typing(tox, tox_events_handle_friend_typing);
+ tox_callback_self_connection_status(tox, tox_events_handle_self_connection_status);
+}
+
+Tox_Events *tox_events_iterate(Tox *tox, bool fail_hard, Tox_Err_Events_Iterate *error)
+{
+ Tox_Events_State state = {TOX_ERR_EVENTS_ITERATE_OK};
+ tox_iterate(tox, &state);
+
+ if (error != nullptr) {
+ *error = state.error;
+ }
+
+ if (fail_hard && state.error != TOX_ERR_EVENTS_ITERATE_OK) {
+ tox_events_free(state.events);
+ return nullptr;
+ }
+
+ return state.events;
+}
+
+bool tox_events_pack(const Tox_Events *events, Bin_Pack *bp)
+{
+ const uint32_t count = tox_events_get_conference_connected_size(events)
+ + tox_events_get_conference_invite_size(events)
+ + tox_events_get_conference_message_size(events)
+ + tox_events_get_conference_peer_list_changed_size(events)
+ + tox_events_get_conference_peer_name_size(events)
+ + tox_events_get_conference_title_size(events)
+ + tox_events_get_file_chunk_request_size(events)
+ + tox_events_get_file_recv_chunk_size(events)
+ + tox_events_get_file_recv_control_size(events)
+ + tox_events_get_file_recv_size(events)
+ + tox_events_get_friend_connection_status_size(events)
+ + tox_events_get_friend_lossless_packet_size(events)
+ + tox_events_get_friend_lossy_packet_size(events)
+ + tox_events_get_friend_message_size(events)
+ + tox_events_get_friend_name_size(events)
+ + tox_events_get_friend_read_receipt_size(events)
+ + tox_events_get_friend_request_size(events)
+ + tox_events_get_friend_status_message_size(events)
+ + tox_events_get_friend_status_size(events)
+ + tox_events_get_friend_typing_size(events)
+ + tox_events_get_self_connection_status_size(events);
+
+ return bin_pack_array(bp, count)
+ && tox_events_pack_conference_connected(events, bp)
+ && tox_events_pack_conference_invite(events, bp)
+ && tox_events_pack_conference_message(events, bp)
+ && tox_events_pack_conference_peer_list_changed(events, bp)
+ && tox_events_pack_conference_peer_name(events, bp)
+ && tox_events_pack_conference_title(events, bp)
+ && tox_events_pack_file_chunk_request(events, bp)
+ && tox_events_pack_file_recv_chunk(events, bp)
+ && tox_events_pack_file_recv_control(events, bp)
+ && tox_events_pack_file_recv(events, bp)
+ && tox_events_pack_friend_connection_status(events, bp)
+ && tox_events_pack_friend_lossless_packet(events, bp)
+ && tox_events_pack_friend_lossy_packet(events, bp)
+ && tox_events_pack_friend_message(events, bp)
+ && tox_events_pack_friend_name(events, bp)
+ && tox_events_pack_friend_read_receipt(events, bp)
+ && tox_events_pack_friend_request(events, bp)
+ && tox_events_pack_friend_status_message(events, bp)
+ && tox_events_pack_friend_status(events, bp)
+ && tox_events_pack_friend_typing(events, bp)
+ && tox_events_pack_self_connection_status(events, bp);
+}
+
+non_null()
+static bool tox_event_unpack(Tox_Events *events, Bin_Unpack *bu)
+{
+ uint32_t size;
+ if (!bin_unpack_array(bu, &size)) {
+ return false;
+ }
+
+ if (size != 2) {
+ return false;
+ }
+
+ uint8_t type;
+ if (!bin_unpack_u08(bu, &type)) {
+ return false;
+ }
+
+ switch (type) {
+ case TOX_EVENT_CONFERENCE_CONNECTED:
+ return tox_events_unpack_conference_connected(events, bu);
+
+ case TOX_EVENT_CONFERENCE_INVITE:
+ return tox_events_unpack_conference_invite(events, bu);
+
+ case TOX_EVENT_CONFERENCE_MESSAGE:
+ return tox_events_unpack_conference_message(events, bu);
+
+ case TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED:
+ return tox_events_unpack_conference_peer_list_changed(events, bu);
+
+ case TOX_EVENT_CONFERENCE_PEER_NAME:
+ return tox_events_unpack_conference_peer_name(events, bu);
+
+ case TOX_EVENT_CONFERENCE_TITLE:
+ return tox_events_unpack_conference_title(events, bu);
+
+ case TOX_EVENT_FILE_CHUNK_REQUEST:
+ return tox_events_unpack_file_chunk_request(events, bu);
+
+ case TOX_EVENT_FILE_RECV_CHUNK:
+ return tox_events_unpack_file_recv_chunk(events, bu);
+
+ case TOX_EVENT_FILE_RECV_CONTROL:
+ return tox_events_unpack_file_recv_control(events, bu);
+
+ case TOX_EVENT_FILE_RECV:
+ return tox_events_unpack_file_recv(events, bu);
+
+ case TOX_EVENT_FRIEND_CONNECTION_STATUS:
+ return tox_events_unpack_friend_connection_status(events, bu);
+
+ case TOX_EVENT_FRIEND_LOSSLESS_PACKET:
+ return tox_events_unpack_friend_lossless_packet(events, bu);
+
+ case TOX_EVENT_FRIEND_LOSSY_PACKET:
+ return tox_events_unpack_friend_lossy_packet(events, bu);
+
+ case TOX_EVENT_FRIEND_MESSAGE:
+ return tox_events_unpack_friend_message(events, bu);
+
+ case TOX_EVENT_FRIEND_NAME:
+ return tox_events_unpack_friend_name(events, bu);
+
+ case TOX_EVENT_FRIEND_READ_RECEIPT:
+ return tox_events_unpack_friend_read_receipt(events, bu);
+
+ case TOX_EVENT_FRIEND_REQUEST:
+ return tox_events_unpack_friend_request(events, bu);
+
+ case TOX_EVENT_FRIEND_STATUS_MESSAGE:
+ return tox_events_unpack_friend_status_message(events, bu);
+
+ case TOX_EVENT_FRIEND_STATUS:
+ return tox_events_unpack_friend_status(events, bu);
+
+ case TOX_EVENT_FRIEND_TYPING:
+ return tox_events_unpack_friend_typing(events, bu);
+
+ case TOX_EVENT_SELF_CONNECTION_STATUS:
+ return tox_events_unpack_self_connection_status(events, bu);
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+bool tox_events_unpack(Tox_Events *events, Bin_Unpack *bu)
+{
+ uint32_t size;
+ if (!bin_unpack_array(bu, &size)) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (!tox_event_unpack(events, bu)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+non_null(1) nullable(2)
+static bool tox_events_bin_pack_handler(Bin_Pack *bp, const void *obj)
+{
+ return tox_events_pack((const Tox_Events *)obj, bp);
+}
+
+uint32_t tox_events_bytes_size(const Tox_Events *events)
+{
+ return bin_pack_obj_size(tox_events_bin_pack_handler, events);
+}
+
+void tox_events_get_bytes(const Tox_Events *events, uint8_t *bytes)
+{
+ bin_pack_obj(tox_events_bin_pack_handler, events, bytes, UINT32_MAX);
+}
+
+Tox_Events *tox_events_load(const uint8_t *bytes, uint32_t bytes_size)
+{
+ Bin_Unpack *bu = bin_unpack_new(bytes, bytes_size);
+
+ if (bu == nullptr) {
+ return nullptr;
+ }
+
+ Tox_Events *events = (Tox_Events *)calloc(1, sizeof(Tox_Events));
+
+ if (events == nullptr) {
+ bin_unpack_free(bu);
+ return nullptr;
+ }
+
+ *events = (Tox_Events) {
+ nullptr
+ };
+
+ if (!tox_events_unpack(events, bu)) {
+ tox_events_free(events);
+ bin_unpack_free(bu);
+ return nullptr;
+ }
+
+ bin_unpack_free(bu);
+ return events;
+}
+
+bool tox_events_equal(const Tox_Events *a, const Tox_Events *b)
+{
+ const uint32_t a_size = tox_events_bytes_size(a);
+ const uint32_t b_size = tox_events_bytes_size(b);
+
+ if (a_size != b_size) {
+ return false;
+ }
+
+ uint8_t *a_bytes = (uint8_t *)malloc(a_size);
+ uint8_t *b_bytes = (uint8_t *)malloc(b_size);
+
+ if (a_bytes == nullptr || b_bytes == nullptr) {
+ free(b_bytes);
+ free(a_bytes);
+ return false;
+ }
+
+ tox_events_get_bytes(a, a_bytes);
+ tox_events_get_bytes(b, b_bytes);
+
+ const bool ret = memcmp(a_bytes, b_bytes, a_size) == 0;
+
+ free(b_bytes);
+ free(a_bytes);
+
+ return ret;
+}
diff --git a/protocols/Tox/libtox/src/toxcore/tox_events.h b/protocols/Tox/libtox/src/toxcore/tox_events.h
new file mode 100644
index 0000000000..f33a71b29c
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/tox_events.h
@@ -0,0 +1,354 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_EVENTS_H
+#define C_TOXCORE_TOXCORE_TOX_EVENTS_H
+
+#include "tox.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct Tox_Event_Conference_Connected Tox_Event_Conference_Connected;
+uint32_t tox_event_conference_connected_get_conference_number(
+ const Tox_Event_Conference_Connected *conference_connected);
+
+typedef struct Tox_Event_Conference_Invite Tox_Event_Conference_Invite;
+const uint8_t *tox_event_conference_invite_get_cookie(
+ const Tox_Event_Conference_Invite *conference_invite);
+uint32_t tox_event_conference_invite_get_cookie_length(
+ const Tox_Event_Conference_Invite *conference_invite);
+Tox_Conference_Type tox_event_conference_invite_get_type(
+ const Tox_Event_Conference_Invite *conference_invite);
+uint32_t tox_event_conference_invite_get_friend_number(
+ const Tox_Event_Conference_Invite *conference_invite);
+
+typedef struct Tox_Event_Conference_Message Tox_Event_Conference_Message;
+const uint8_t *tox_event_conference_message_get_message(
+ const Tox_Event_Conference_Message *conference_message);
+uint32_t tox_event_conference_message_get_message_length(
+ const Tox_Event_Conference_Message *conference_message);
+Tox_Message_Type tox_event_conference_message_get_type(
+ const Tox_Event_Conference_Message *conference_message);
+uint32_t tox_event_conference_message_get_conference_number(
+ const Tox_Event_Conference_Message *conference_message);
+uint32_t tox_event_conference_message_get_peer_number(
+ const Tox_Event_Conference_Message *conference_message);
+
+typedef struct Tox_Event_Conference_Peer_List_Changed Tox_Event_Conference_Peer_List_Changed;
+uint32_t tox_event_conference_peer_list_changed_get_conference_number(
+ const Tox_Event_Conference_Peer_List_Changed *conference_peer_list_changed);
+
+typedef struct Tox_Event_Conference_Peer_Name Tox_Event_Conference_Peer_Name;
+const uint8_t *tox_event_conference_peer_name_get_name(
+ const Tox_Event_Conference_Peer_Name *conference_peer_name);
+uint32_t tox_event_conference_peer_name_get_name_length(
+ const Tox_Event_Conference_Peer_Name *conference_peer_name);
+uint32_t tox_event_conference_peer_name_get_conference_number(
+ const Tox_Event_Conference_Peer_Name *conference_peer_name);
+uint32_t tox_event_conference_peer_name_get_peer_number(
+ const Tox_Event_Conference_Peer_Name *conference_peer_name);
+
+typedef struct Tox_Event_Conference_Title Tox_Event_Conference_Title;
+const uint8_t *tox_event_conference_title_get_title(
+ const Tox_Event_Conference_Title *conference_title);
+uint32_t tox_event_conference_title_get_title_length(
+ const Tox_Event_Conference_Title *conference_title);
+uint32_t tox_event_conference_title_get_conference_number(
+ const Tox_Event_Conference_Title *conference_title);
+uint32_t tox_event_conference_title_get_peer_number(
+ const Tox_Event_Conference_Title *conference_title);
+
+typedef struct Tox_Event_File_Chunk_Request Tox_Event_File_Chunk_Request;
+uint16_t tox_event_file_chunk_request_get_length(
+ const Tox_Event_File_Chunk_Request *file_chunk_request);
+uint32_t tox_event_file_chunk_request_get_file_number(
+ const Tox_Event_File_Chunk_Request *file_chunk_request);
+uint32_t tox_event_file_chunk_request_get_friend_number(
+ const Tox_Event_File_Chunk_Request *file_chunk_request);
+uint64_t tox_event_file_chunk_request_get_position(
+ const Tox_Event_File_Chunk_Request *file_chunk_request);
+
+typedef struct Tox_Event_File_Recv Tox_Event_File_Recv;
+const uint8_t *tox_event_file_recv_get_filename(
+ const Tox_Event_File_Recv *file_recv);
+uint32_t tox_event_file_recv_get_filename_length(
+ const Tox_Event_File_Recv *file_recv);
+uint32_t tox_event_file_recv_get_file_number(
+ const Tox_Event_File_Recv *file_recv);
+uint64_t tox_event_file_recv_get_file_size(
+ const Tox_Event_File_Recv *file_recv);
+uint32_t tox_event_file_recv_get_friend_number(
+ const Tox_Event_File_Recv *file_recv);
+uint32_t tox_event_file_recv_get_kind(
+ const Tox_Event_File_Recv *file_recv);
+
+typedef struct Tox_Event_File_Recv_Chunk Tox_Event_File_Recv_Chunk;
+const uint8_t *tox_event_file_recv_chunk_get_data(
+ const Tox_Event_File_Recv_Chunk *file_recv_chunk);
+uint32_t tox_event_file_recv_chunk_get_length(
+ const Tox_Event_File_Recv_Chunk *file_recv_chunk);
+uint32_t tox_event_file_recv_chunk_get_file_number(
+ const Tox_Event_File_Recv_Chunk *file_recv_chunk);
+uint32_t tox_event_file_recv_chunk_get_friend_number(
+ const Tox_Event_File_Recv_Chunk *file_recv_chunk);
+uint64_t tox_event_file_recv_chunk_get_position(
+ const Tox_Event_File_Recv_Chunk *file_recv_chunk);
+
+typedef struct Tox_Event_File_Recv_Control Tox_Event_File_Recv_Control;
+Tox_File_Control tox_event_file_recv_control_get_control(
+ const Tox_Event_File_Recv_Control *file_recv_control);
+uint32_t tox_event_file_recv_control_get_file_number(
+ const Tox_Event_File_Recv_Control *file_recv_control);
+uint32_t tox_event_file_recv_control_get_friend_number(
+ const Tox_Event_File_Recv_Control *file_recv_control);
+
+typedef struct Tox_Event_Friend_Connection_Status Tox_Event_Friend_Connection_Status;
+Tox_Connection tox_event_friend_connection_status_get_connection_status(
+ const Tox_Event_Friend_Connection_Status *friend_connection_status);
+uint32_t tox_event_friend_connection_status_get_friend_number(
+ const Tox_Event_Friend_Connection_Status *friend_connection_status);
+
+typedef struct Tox_Event_Friend_Lossless_Packet Tox_Event_Friend_Lossless_Packet;
+const uint8_t *tox_event_friend_lossless_packet_get_data(
+ const Tox_Event_Friend_Lossless_Packet *friend_lossless_packet);
+uint32_t tox_event_friend_lossless_packet_get_data_length(
+ const Tox_Event_Friend_Lossless_Packet *friend_lossless_packet);
+uint32_t tox_event_friend_lossless_packet_get_friend_number(
+ const Tox_Event_Friend_Lossless_Packet *friend_lossless_packet);
+
+typedef struct Tox_Event_Friend_Lossy_Packet Tox_Event_Friend_Lossy_Packet;
+const uint8_t *tox_event_friend_lossy_packet_get_data(
+ const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet);
+uint32_t tox_event_friend_lossy_packet_get_data_length(
+ const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet);
+uint32_t tox_event_friend_lossy_packet_get_friend_number(
+ const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet);
+
+typedef struct Tox_Event_Friend_Message Tox_Event_Friend_Message;
+uint32_t tox_event_friend_message_get_friend_number(
+ const Tox_Event_Friend_Message *friend_message);
+Tox_Message_Type tox_event_friend_message_get_type(
+ const Tox_Event_Friend_Message *friend_message);
+uint32_t tox_event_friend_message_get_message_length(
+ const Tox_Event_Friend_Message *friend_message);
+const uint8_t *tox_event_friend_message_get_message(
+ const Tox_Event_Friend_Message *friend_message);
+
+typedef struct Tox_Event_Friend_Name Tox_Event_Friend_Name;
+const uint8_t *tox_event_friend_name_get_name(
+ const Tox_Event_Friend_Name *friend_name);
+uint32_t tox_event_friend_name_get_name_length(
+ const Tox_Event_Friend_Name *friend_name);
+uint32_t tox_event_friend_name_get_friend_number(
+ const Tox_Event_Friend_Name *friend_name);
+
+typedef struct Tox_Event_Friend_Read_Receipt Tox_Event_Friend_Read_Receipt;
+uint32_t tox_event_friend_read_receipt_get_friend_number(
+ const Tox_Event_Friend_Read_Receipt *friend_read_receipt);
+uint32_t tox_event_friend_read_receipt_get_message_id(
+ const Tox_Event_Friend_Read_Receipt *friend_read_receipt);
+
+typedef struct Tox_Event_Friend_Request Tox_Event_Friend_Request;
+const uint8_t *tox_event_friend_request_get_message(
+ const Tox_Event_Friend_Request *friend_request);
+const uint8_t *tox_event_friend_request_get_public_key(
+ const Tox_Event_Friend_Request *friend_request);
+uint32_t tox_event_friend_request_get_message_length(
+ const Tox_Event_Friend_Request *friend_request);
+
+typedef struct Tox_Event_Friend_Status Tox_Event_Friend_Status;
+Tox_User_Status tox_event_friend_status_get_status(
+ const Tox_Event_Friend_Status *friend_status);
+uint32_t tox_event_friend_status_get_friend_number(
+ const Tox_Event_Friend_Status *friend_status);
+
+typedef struct Tox_Event_Friend_Status_Message Tox_Event_Friend_Status_Message;
+const uint8_t *tox_event_friend_status_message_get_message(
+ const Tox_Event_Friend_Status_Message *friend_status_message);
+uint32_t tox_event_friend_status_message_get_message_length(
+ const Tox_Event_Friend_Status_Message *friend_status_message);
+uint32_t tox_event_friend_status_message_get_friend_number(
+ const Tox_Event_Friend_Status_Message *friend_status_message);
+
+typedef struct Tox_Event_Friend_Typing Tox_Event_Friend_Typing;
+bool tox_event_friend_typing_get_typing(
+ const Tox_Event_Friend_Typing *friend_typing);
+uint32_t tox_event_friend_typing_get_friend_number(
+ const Tox_Event_Friend_Typing *friend_typing);
+
+typedef struct Tox_Event_Self_Connection_Status Tox_Event_Self_Connection_Status;
+Tox_Connection tox_event_self_connection_status_get_connection_status(
+ const Tox_Event_Self_Connection_Status *self_connection_status);
+
+
+typedef enum Tox_Event {
+ TOX_EVENT_SELF_CONNECTION_STATUS = 0,
+
+ TOX_EVENT_FRIEND_REQUEST = 1,
+ TOX_EVENT_FRIEND_CONNECTION_STATUS = 2,
+ TOX_EVENT_FRIEND_LOSSY_PACKET = 3,
+ TOX_EVENT_FRIEND_LOSSLESS_PACKET = 4,
+
+ TOX_EVENT_FRIEND_NAME = 5,
+ TOX_EVENT_FRIEND_STATUS = 6,
+ TOX_EVENT_FRIEND_STATUS_MESSAGE = 7,
+
+ TOX_EVENT_FRIEND_MESSAGE = 8,
+ TOX_EVENT_FRIEND_READ_RECEIPT = 9,
+ TOX_EVENT_FRIEND_TYPING = 10,
+
+ TOX_EVENT_FILE_CHUNK_REQUEST = 11,
+ TOX_EVENT_FILE_RECV = 12,
+ TOX_EVENT_FILE_RECV_CHUNK = 13,
+ TOX_EVENT_FILE_RECV_CONTROL = 14,
+
+ TOX_EVENT_CONFERENCE_INVITE = 15,
+ TOX_EVENT_CONFERENCE_CONNECTED = 16,
+ TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED = 17,
+ TOX_EVENT_CONFERENCE_PEER_NAME = 18,
+ TOX_EVENT_CONFERENCE_TITLE = 19,
+
+ TOX_EVENT_CONFERENCE_MESSAGE = 20,
+} Tox_Event;
+
+/**
+ * Container object for all Tox core events.
+ *
+ * This is an immutable object once created.
+ */
+typedef struct Tox_Events Tox_Events;
+
+uint32_t tox_events_get_conference_connected_size(const Tox_Events *events);
+uint32_t tox_events_get_conference_invite_size(const Tox_Events *events);
+uint32_t tox_events_get_conference_message_size(const Tox_Events *events);
+uint32_t tox_events_get_conference_peer_list_changed_size(const Tox_Events *events);
+uint32_t tox_events_get_conference_peer_name_size(const Tox_Events *events);
+uint32_t tox_events_get_conference_title_size(const Tox_Events *events);
+uint32_t tox_events_get_file_chunk_request_size(const Tox_Events *events);
+uint32_t tox_events_get_file_recv_chunk_size(const Tox_Events *events);
+uint32_t tox_events_get_file_recv_control_size(const Tox_Events *events);
+uint32_t tox_events_get_file_recv_size(const Tox_Events *events);
+uint32_t tox_events_get_friend_connection_status_size(const Tox_Events *events);
+uint32_t tox_events_get_friend_lossless_packet_size(const Tox_Events *events);
+uint32_t tox_events_get_friend_lossy_packet_size(const Tox_Events *events);
+uint32_t tox_events_get_friend_message_size(const Tox_Events *events);
+uint32_t tox_events_get_friend_name_size(const Tox_Events *events);
+uint32_t tox_events_get_friend_read_receipt_size(const Tox_Events *events);
+uint32_t tox_events_get_friend_request_size(const Tox_Events *events);
+uint32_t tox_events_get_friend_status_message_size(const Tox_Events *events);
+uint32_t tox_events_get_friend_status_size(const Tox_Events *events);
+uint32_t tox_events_get_friend_typing_size(const Tox_Events *events);
+uint32_t tox_events_get_self_connection_status_size(const Tox_Events *events);
+
+const Tox_Event_Conference_Connected *tox_events_get_conference_connected(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Conference_Invite *tox_events_get_conference_invite(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Conference_Message *tox_events_get_conference_message(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Conference_Peer_List_Changed *tox_events_get_conference_peer_list_changed(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Conference_Peer_Name *tox_events_get_conference_peer_name(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Conference_Title *tox_events_get_conference_title(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_File_Chunk_Request *tox_events_get_file_chunk_request(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_File_Recv_Chunk *tox_events_get_file_recv_chunk(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_File_Recv_Control *tox_events_get_file_recv_control(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_File_Recv *tox_events_get_file_recv(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Friend_Connection_Status *tox_events_get_friend_connection_status(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Friend_Lossless_Packet *tox_events_get_friend_lossless_packet(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Friend_Lossy_Packet *tox_events_get_friend_lossy_packet(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Friend_Message *tox_events_get_friend_message(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Friend_Name *tox_events_get_friend_name(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Friend_Read_Receipt *tox_events_get_friend_read_receipt(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Friend_Request *tox_events_get_friend_request(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Friend_Status_Message *tox_events_get_friend_status_message(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Friend_Status *tox_events_get_friend_status(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Friend_Typing *tox_events_get_friend_typing(
+ const Tox_Events *events, uint32_t index);
+const Tox_Event_Self_Connection_Status *tox_events_get_self_connection_status(
+ const Tox_Events *events, uint32_t index);
+
+/**
+ * Initialise the events recording system.
+ *
+ * All callbacks will be set to handlers inside the events recording system.
+ * After this function returns, no user-defined event handlers will be
+ * invoked. If the client sets their own handlers after calling this function,
+ * the events associated with that handler will not be recorded.
+ */
+void tox_events_init(Tox *tox);
+
+typedef enum Tox_Err_Events_Iterate {
+ /**
+ * The function returned successfully.
+ */
+ TOX_ERR_EVENTS_ITERATE_OK,
+
+ /**
+ * The function failed to allocate enough memory to store the events.
+ *
+ * Some events may still be stored if the return value is NULL. The events
+ * object will always be valid (or NULL) but if this error code is set,
+ * the function may have missed some events.
+ */
+ TOX_ERR_EVENTS_ITERATE_MALLOC,
+} Tox_Err_Events_Iterate;
+
+/**
+ * Run a single `tox_iterate` iteration and record all the events.
+ *
+ * If allocation of the top level events object fails, this returns NULL.
+ * Otherwise it returns an object with the recorded events in it. If an
+ * allocation fails while recording events, some events may be dropped.
+ *
+ * If @p fail_hard is `true`, any failure will result in NULL, so all recorded
+ * events will be dropped.
+ *
+ * The result must be freed using `tox_events_free`.
+ *
+ * @param tox The Tox instance to iterate on.
+ * @param fail_hard Drop all events when any allocation fails.
+ * @param error An error code. Will be set to OK on success.
+ *
+ * @return the recorded events structure.
+ */
+Tox_Events *tox_events_iterate(Tox *tox, bool fail_hard, Tox_Err_Events_Iterate *error);
+
+/**
+ * Frees all memory associated with the events structure.
+ *
+ * All pointers into this object and its sub-objects, including byte buffers,
+ * will be invalid once this function returns.
+ */
+void tox_events_free(Tox_Events *events);
+
+uint32_t tox_events_bytes_size(const Tox_Events *events);
+void tox_events_get_bytes(const Tox_Events *events, uint8_t *bytes);
+
+Tox_Events *tox_events_load(const uint8_t *bytes, uint32_t bytes_size);
+
+bool tox_events_equal(const Tox_Events *a, const Tox_Events *b);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // C_TOXCORE_TOXCORE_TOX_EVENTS_H
diff --git a/protocols/Tox/libtox/src/toxcore/tox_private.c b/protocols/Tox/libtox/src/toxcore/tox_private.c
new file mode 100644
index 0000000000..847e96d426
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/tox_private.c
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2016-2022 The TokTok team.
+ * Copyright © 2013 Tox project.
+ */
+
+/**
+ * The Tox private API (for tests).
+ */
+#include "tox_private.h"
+
+#include <assert.h>
+
+#include "ccompat.h"
+#include "network.h"
+#include "tox_struct.h"
+
+#define SET_ERROR_PARAMETER(param, x) \
+ do { \
+ if (param != nullptr) { \
+ *param = x; \
+ } \
+ } while (0)
+
+Tox_System tox_default_system(void)
+{
+ const Tox_System sys = {
+ nullptr, // mono_time_callback
+ nullptr, // mono_time_user_data
+ system_random(),
+ system_network(),
+ };
+ return sys;
+}
+
+void tox_lock(const Tox *tox)
+{
+ if (tox->mutex != nullptr) {
+ pthread_mutex_lock(tox->mutex);
+ }
+}
+
+void tox_unlock(const Tox *tox)
+{
+ if (tox->mutex != nullptr) {
+ pthread_mutex_unlock(tox->mutex);
+ }
+}
+
+void tox_callback_friend_lossy_packet_per_pktid(Tox *tox, tox_friend_lossy_packet_cb *callback, uint8_t pktid)
+{
+ assert(tox != nullptr);
+
+ if (pktid >= PACKET_ID_RANGE_LOSSY_START && pktid <= PACKET_ID_RANGE_LOSSY_END) {
+ tox->friend_lossy_packet_callback_per_pktid[pktid] = callback;
+ }
+}
+
+void tox_callback_friend_lossless_packet_per_pktid(Tox *tox, tox_friend_lossless_packet_cb *callback, uint8_t pktid)
+{
+ assert(tox != nullptr);
+
+ if ((pktid >= PACKET_ID_RANGE_LOSSLESS_CUSTOM_START && pktid <= PACKET_ID_RANGE_LOSSLESS_CUSTOM_END)
+ || pktid == PACKET_ID_MSI) {
+ tox->friend_lossless_packet_callback_per_pktid[pktid] = callback;
+ }
+}
+
+void tox_set_av_object(Tox *tox, void *object)
+{
+ assert(tox != nullptr);
+ tox_lock(tox);
+ tox->toxav_object = object;
+ tox_unlock(tox);
+}
+
+void *tox_get_av_object(const Tox *tox)
+{
+ assert(tox != nullptr);
+ tox_lock(tox);
+ void *object = tox->toxav_object;
+ tox_unlock(tox);
+ return object;
+}
+
+void tox_callback_dht_get_nodes_response(Tox *tox, tox_dht_get_nodes_response_cb *callback)
+{
+ assert(tox != nullptr);
+ tox->dht_get_nodes_response_callback = callback;
+}
+
+bool tox_dht_get_nodes(const Tox *tox, const uint8_t *public_key, const char *ip, uint16_t port,
+ const uint8_t *target_public_key, Tox_Err_Dht_Get_Nodes *error)
+{
+ assert(tox != nullptr);
+
+ tox_lock(tox);
+
+ if (tox->m->options.udp_disabled) {
+ SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_UDP_DISABLED);
+ tox_unlock(tox);
+ return false;
+ }
+
+ if (public_key == nullptr || ip == nullptr || target_public_key == nullptr) {
+ SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_NULL);
+ tox_unlock(tox);
+ return false;
+ }
+
+ if (port == 0) {
+ SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_BAD_PORT);
+ tox_unlock(tox);
+ return false;
+ }
+
+ IP_Port *root;
+
+ const int32_t count = net_getipport(ip, &root, TOX_SOCK_DGRAM);
+
+ if (count < 1) {
+ SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_BAD_IP);
+ net_freeipport(root);
+ tox_unlock(tox);
+ return false;
+ }
+
+ bool success = false;
+
+ for (int32_t i = 0; i < count; ++i) {
+ root[i].port = net_htons(port);
+
+ if (dht_getnodes(tox->m->dht, &root[i], public_key, target_public_key)) {
+ success = true;
+ }
+ }
+
+ tox_unlock(tox);
+
+ net_freeipport(root);
+
+ if (!success) {
+ SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_FAIL);
+ return false;
+ }
+
+ SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_OK);
+
+ return true;
+}
diff --git a/protocols/Tox/libtox/src/toxcore/tox_private.h b/protocols/Tox/libtox/src/toxcore/tox_private.h
index 593282f7c8..71d7a97601 100644
--- a/protocols/Tox/libtox/src/toxcore/tox_private.h
+++ b/protocols/Tox/libtox/src/toxcore/tox_private.h
@@ -10,10 +10,26 @@
#include <stddef.h>
#include <stdint.h>
+#include "tox.h"
+
#ifdef __cplusplus
extern "C" {
#endif
+typedef uint64_t tox_mono_time_cb(void *user_data);
+
+struct Tox_System {
+ tox_mono_time_cb *mono_time_callback;
+ void *mono_time_user_data;
+ const struct Random *rng;
+ const struct Network *ns;
+};
+
+Tox_System tox_default_system(void);
+
+void tox_lock(const Tox *tox);
+void tox_unlock(const Tox *tox);
+
/**
* Set the callback for the `friend_lossy_packet` event for a specific packet ID.
* Pass NULL to unset.
@@ -37,6 +53,92 @@ void tox_callback_friend_lossless_packet_per_pktid(Tox *tox, tox_friend_lossless
void tox_set_av_object(Tox *tox, void *object);
void *tox_get_av_object(const Tox *tox);
+
+/*******************************************************************************
+ *
+ * :: DHT network queries.
+ *
+ ******************************************************************************/
+
+/**
+ * The minimum size of an IP string buffer in bytes.
+ */
+#define TOX_DHT_NODE_IP_STRING_SIZE 96
+
+uint32_t tox_dht_node_ip_string_size(void);
+
+/**
+ * The size of a DHT node public key in bytes.
+ */
+#define TOX_DHT_NODE_PUBLIC_KEY_SIZE 32
+
+uint32_t tox_dht_node_public_key_size(void);
+
+/**
+ * @param public_key The node's public key.
+ * @param ip The node's IP address, represented as a null terminated string.
+ * @param port The node's port.
+ */
+typedef void tox_dht_get_nodes_response_cb(Tox *tox, const uint8_t *public_key, const char *ip, uint16_t port,
+ void *user_data);
+
+
+/**
+ * Set the callback for the `dht_get_nodes_response` event. Pass NULL to unset.
+ *
+ * This event is triggered when a getnodes response is received from a DHT peer.
+ */
+void tox_callback_dht_get_nodes_response(Tox *tox, tox_dht_get_nodes_response_cb *callback);
+
+
+typedef enum Tox_Err_Dht_Get_Nodes {
+ /**
+ * The function returned successfully.
+ */
+ TOX_ERR_DHT_GET_NODES_OK,
+
+ /**
+ * UDP is disabled in tox options; the DHT can only be queried when UDP is enabled.
+ */
+ TOX_ERR_DHT_GET_NODES_UDP_DISABLED,
+
+ /**
+ * One of the arguments to the function was NULL when it was not expected.
+ */
+ TOX_ERR_DHT_GET_NODES_NULL,
+
+ /**
+ * The supplied port is invalid.
+ */
+ TOX_ERR_DHT_GET_NODES_BAD_PORT,
+
+ /**
+ * The supplied IP address is invalid.
+ */
+ TOX_ERR_DHT_GET_NODES_BAD_IP,
+
+ /**
+ * The getnodes request failed. This usually means the packet failed to send.
+ */
+ TOX_ERR_DHT_GET_NODES_FAIL,
+} Tox_Err_Dht_Get_Nodes;
+
+/**
+ * This function sends a getnodes request to a DHT node for its peers that
+ * are "close" to the passed target public key according to the distance metric used
+ * by the DHT implementation.
+ *
+ * @param public_key The public key of the node that we wish to query. This key must be
+ * at least `TOX_DHT_NODE_PUBLIC_KEY_SIZE` bytes in length.
+ * @param ip A NULL terminated string representing the IP address of the node we wish to query.
+ * @param port The port of the node we wish to query.
+ * @param target_public_key The public key for which we want to find close nodes.
+ *
+ * @return true on success.
+ */
+bool tox_dht_get_nodes(const Tox *tox, const uint8_t *public_key, const char *ip, uint16_t port,
+ const uint8_t *target_public_key, Tox_Err_Dht_Get_Nodes *error);
+
#ifdef __cplusplus
}
#endif
diff --git a/protocols/Tox/libtox/src/toxcore/tox_struct.h b/protocols/Tox/libtox/src/toxcore/tox_struct.h
new file mode 100644
index 0000000000..22d1c54a27
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/tox_struct.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2016-2022 The TokTok team.
+ * Copyright © 2013 Tox project.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_STRUCT_H
+#define C_TOXCORE_TOXCORE_TOX_STRUCT_H
+
+#include "Messenger.h"
+#include "tox.h"
+#include "tox_private.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Tox {
+ Messenger *m;
+ Mono_Time *mono_time;
+ Random rng;
+ Network ns;
+ pthread_mutex_t *mutex;
+
+ tox_log_cb *log_callback;
+ tox_self_connection_status_cb *self_connection_status_callback;
+ tox_friend_name_cb *friend_name_callback;
+ tox_friend_status_message_cb *friend_status_message_callback;
+ tox_friend_status_cb *friend_status_callback;
+ tox_friend_connection_status_cb *friend_connection_status_callback;
+ tox_friend_typing_cb *friend_typing_callback;
+ tox_friend_read_receipt_cb *friend_read_receipt_callback;
+ tox_friend_request_cb *friend_request_callback;
+ tox_friend_message_cb *friend_message_callback;
+ tox_file_recv_control_cb *file_recv_control_callback;
+ tox_file_chunk_request_cb *file_chunk_request_callback;
+ tox_file_recv_cb *file_recv_callback;
+ tox_file_recv_chunk_cb *file_recv_chunk_callback;
+ tox_conference_invite_cb *conference_invite_callback;
+ tox_conference_connected_cb *conference_connected_callback;
+ tox_conference_message_cb *conference_message_callback;
+ tox_conference_title_cb *conference_title_callback;
+ tox_conference_peer_name_cb *conference_peer_name_callback;
+ tox_conference_peer_list_changed_cb *conference_peer_list_changed_callback;
+ tox_dht_get_nodes_response_cb *dht_get_nodes_response_callback;
+ tox_friend_lossy_packet_cb *friend_lossy_packet_callback_per_pktid[UINT8_MAX + 1];
+ tox_friend_lossless_packet_cb *friend_lossless_packet_callback_per_pktid[UINT8_MAX + 1];
+
+ void *toxav_object; // workaround to store a ToxAV object (setter and getter functions are available)
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // C_TOXCORE_TOXCORE_TOX_STRUCT_H
diff --git a/protocols/Tox/libtox/src/toxcore/tox_unpack.c b/protocols/Tox/libtox/src/toxcore/tox_unpack.c
new file mode 100644
index 0000000000..6508399e7e
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/tox_unpack.c
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022 The TokTok team.
+ */
+
+#include "tox_unpack.h"
+
+#include <stdint.h>
+
+#include "bin_unpack.h"
+#include "ccompat.h"
+
+bool tox_unpack_conference_type(Bin_Unpack *bu, Tox_Conference_Type *val)
+{
+ uint32_t u32;
+
+ if (!bin_unpack_u32(bu, &u32)) {
+ return false;
+ }
+
+ *val = (Tox_Conference_Type)u32;
+ return true;
+}
+
+bool tox_unpack_connection(Bin_Unpack *bu, Tox_Connection *val)
+{
+ uint32_t u32;
+
+ if (!bin_unpack_u32(bu, &u32)) {
+ return false;
+ }
+
+ *val = (Tox_Connection)u32;
+ return true;
+}
+
+bool tox_unpack_file_control(Bin_Unpack *bu, Tox_File_Control *val)
+{
+ uint32_t u32;
+
+ if (!bin_unpack_u32(bu, &u32)) {
+ return false;
+ }
+
+ *val = (Tox_File_Control)u32;
+ return true;
+}
+
+bool tox_unpack_message_type(Bin_Unpack *bu, Tox_Message_Type *val)
+{
+ uint32_t u32;
+
+ if (!bin_unpack_u32(bu, &u32)) {
+ return false;
+ }
+
+ *val = (Tox_Message_Type)u32;
+ return true;
+}
+
+bool tox_unpack_user_status(Bin_Unpack *bu, Tox_User_Status *val)
+{
+ uint32_t u32;
+
+ if (!bin_unpack_u32(bu, &u32)) {
+ return false;
+ }
+
+ *val = (Tox_User_Status)u32;
+ return true;
+}
diff --git a/protocols/Tox/libtox/src/toxcore/tox_unpack.h b/protocols/Tox/libtox/src/toxcore/tox_unpack.h
new file mode 100644
index 0000000000..5f0b18ad1b
--- /dev/null
+++ b/protocols/Tox/libtox/src/toxcore/tox_unpack.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_UNPACK_H
+#define C_TOXCORE_TOXCORE_TOX_UNPACK_H
+
+#include "attributes.h"
+#include "bin_unpack.h"
+#include "tox.h"
+
+non_null() bool tox_unpack_conference_type(Bin_Unpack *bu, Tox_Conference_Type *val);
+non_null() bool tox_unpack_connection(Bin_Unpack *bu, Tox_Connection *val);
+non_null() bool tox_unpack_file_control(Bin_Unpack *bu, Tox_File_Control *val);
+non_null() bool tox_unpack_message_type(Bin_Unpack *bu, Tox_Message_Type *val);
+non_null() bool tox_unpack_user_status(Bin_Unpack *bu, Tox_User_Status *val);
+
+#endif // C_TOXCORE_TOXCORE_TOX_UNPACK_H
diff --git a/protocols/Tox/libtox/src/toxcore/util.c b/protocols/Tox/libtox/src/toxcore/util.c
index 8174ef3fa2..c70c3a76f5 100644
--- a/protocols/Tox/libtox/src/toxcore/util.c
+++ b/protocols/Tox/libtox/src/toxcore/util.c
@@ -17,19 +17,39 @@
#include <string.h>
#include <time.h>
-#include "crypto_core.h" /* for CRYPTO_PUBLIC_KEY_SIZE */
+#include "ccompat.h"
+bool is_power_of_2(uint64_t x)
+{
+ return x != 0 && (x & (~x + 1)) == x;
+}
-/** id functions */
-bool id_equal(const uint8_t *dest, const uint8_t *src)
+void free_uint8_t_pointer_array(uint8_t **ary, size_t n_items)
{
- return public_key_cmp(dest, src) == 0;
+ if (ary == nullptr) {
+ return;
+ }
+
+ for (size_t i = 0; i < n_items; ++i) {
+ if (ary[i] != nullptr) {
+ free(ary[i]);
+ }
+ }
+
+ free(ary);
}
-uint32_t id_copy(uint8_t *dest, const uint8_t *src)
+uint16_t data_checksum(const uint8_t *data, uint32_t length)
{
- memcpy(dest, src, CRYPTO_PUBLIC_KEY_SIZE);
- return CRYPTO_PUBLIC_KEY_SIZE;
+ uint8_t checksum[2] = {0};
+ uint16_t check;
+
+ for (uint32_t i = 0; i < length; ++i) {
+ checksum[i % 2] ^= data[i];
+ }
+
+ memcpy(&check, checksum, sizeof(check));
+ return check;
}
int create_recursive_mutex(pthread_mutex_t *mutex)
@@ -56,6 +76,11 @@ int create_recursive_mutex(pthread_mutex_t *mutex)
return 0;
}
+bool memeq(const uint8_t *a, size_t a_size, const uint8_t *b, size_t b_size)
+{
+ return a_size == b_size && memcmp(a, b, a_size) == 0;
+}
+
int16_t max_s16(int16_t a, int16_t b)
{
return a > b ? a : b;
@@ -107,3 +132,19 @@ uint64_t min_u64(uint64_t a, uint64_t b)
{
return a < b ? a : b;
}
+
+uint32_t jenkins_one_at_a_time_hash(const uint8_t *key, size_t len)
+{
+ uint32_t hash = 0;
+
+ for (uint32_t i = 0; i < len; ++i) {
+ hash += key[i];
+ hash += hash << 10;
+ hash ^= hash >> 6;
+ }
+
+ hash += hash << 3;
+ hash ^= hash >> 11;
+ hash += hash << 15;
+ return hash;
+}
diff --git a/protocols/Tox/libtox/src/toxcore/util.h b/protocols/Tox/libtox/src/toxcore/util.h
index bcae4f4a67..588feb5694 100644
--- a/protocols/Tox/libtox/src/toxcore/util.h
+++ b/protocols/Tox/libtox/src/toxcore/util.h
@@ -12,20 +12,33 @@
#include <pthread.h>
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
-#include "logger.h"
+#include "attributes.h"
#ifdef __cplusplus
extern "C" {
#endif
-/** id functions */
-bool id_equal(const uint8_t *dest, const uint8_t *src);
-uint32_t id_copy(uint8_t *dest, const uint8_t *src); /* return value is CLIENT_ID_SIZE */
+bool is_power_of_2(uint64_t x);
+
+/** @brief Frees all pointers in a uint8_t pointer array, as well as the array itself. */
+nullable(1)
+void free_uint8_t_pointer_array(uint8_t **ary, size_t n_items);
/** Returns -1 if failed or 0 if success */
-int create_recursive_mutex(pthread_mutex_t *mutex);
+non_null() int create_recursive_mutex(pthread_mutex_t *mutex);
+
+/**
+ * @brief Checks whether two buffers are the same length and contents.
+ *
+ * Calls `memcmp` after checking the sizes are equal.
+ *
+ * @retval true if sizes and contents are equal.
+ * @retval false otherwise.
+ */
+non_null() bool memeq(const uint8_t *a, size_t a_size, const uint8_t *b, size_t b_size);
// Safe min/max functions with specific types. This forces the conversion to the
// desired type before the comparison expression, giving the choice of
@@ -47,6 +60,20 @@ uint16_t min_u16(uint16_t a, uint16_t b);
uint32_t min_u32(uint32_t a, uint32_t b);
uint64_t min_u64(uint64_t a, uint64_t b);
+/** @brief Returns a 32-bit hash of key of size len */
+non_null()
+uint32_t jenkins_one_at_a_time_hash(const uint8_t *key, size_t len);
+
+/** @brief Computes a checksum of a byte array.
+ *
+ * @param data The byte array used to compute the checksum.
+ * @param length The length in bytes of the passed data.
+ *
+ * @retval The resulting checksum.
+ */
+non_null()
+uint16_t data_checksum(const uint8_t *data, uint32_t length);
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.c b/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.c
index 1d464534d0..45003055f4 100644
--- a/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.c
+++ b/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.c
@@ -27,7 +27,7 @@ static_assert(TOX_PASS_ENCRYPTION_EXTRA_LENGTH == (crypto_box_MACBYTES + crypto_
#define SET_ERROR_PARAMETER(param, x) \
do { \
- if (param) { \
+ if (param != nullptr) { \
*param = x; \
} \
} while (0)
@@ -50,9 +50,9 @@ struct Tox_Pass_Key {
uint8_t key[TOX_PASS_KEY_LENGTH];
};
-void tox_pass_key_free(Tox_Pass_Key *pass_key)
+void tox_pass_key_free(Tox_Pass_Key *key)
{
- free(pass_key);
+ free(key);
}
/* Clients should consider alerting their users that, unlike plain data, if even one bit
@@ -60,64 +60,92 @@ void tox_pass_key_free(Tox_Pass_Key *pass_key)
* Ditto if they forget their password, there is no way to recover the data.
*/
-/* This retrieves the salt used to encrypt the given data, which can then be passed to
- * tox_pass_key_derive_with_salt to produce the same key as was previously used. Any encrpyted
- * data with this module can be used as input.
+/**
+ * Retrieves the salt used to encrypt the given data.
+ *
+ * The retrieved salt can then be passed to tox_pass_key_derive_with_salt to
+ * produce the same key as was previously used. Any data encrypted with this
+ * module can be used as input.
+ *
+ * The cipher text must be at least TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes in length.
+ * The salt must be TOX_PASS_SALT_LENGTH bytes in length.
+ * If the passed byte arrays are smaller than required, the behaviour is
+ * undefined.
*
- * returns true if magic number matches
- * success does not say anything about the validity of the data, only that data of
- * the appropriate size was copied
+ * If the cipher text pointer or the salt is NULL, this function returns false.
+ *
+ * Success does not say anything about the validity of the data, only that
+ * data of the appropriate size was copied.
+ *
+ * @return true on success.
*/
-bool tox_get_salt(const uint8_t *data, uint8_t *salt, Tox_Err_Get_Salt *error)
+bool tox_get_salt(const uint8_t *ciphertext, uint8_t *salt, Tox_Err_Get_Salt *error)
{
- if (!data || !salt) {
+ if (ciphertext == nullptr || salt == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_GET_SALT_NULL);
return false;
}
- if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) {
+ if (memcmp(ciphertext, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_GET_SALT_BAD_FORMAT);
return false;
}
- data += TOX_ENC_SAVE_MAGIC_LENGTH;
- memcpy(salt, data, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
+ ciphertext += TOX_ENC_SAVE_MAGIC_LENGTH;
+ memcpy(salt, ciphertext, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
SET_ERROR_PARAMETER(error, TOX_ERR_GET_SALT_OK);
return true;
}
-/* Generates a secret symmetric key from the given passphrase. out_key must be at least
- * TOX_PASS_KEY_LENGTH bytes long.
- * Be sure to not compromise the key! Only keep it in memory, do not write to disk.
- * The password is zeroed after key derivation.
- * The key should only be used with the other functions in this module, as it
- * includes a salt.
- * Note that this function is not deterministic; to derive the same key from a
- * password, you also must know the random salt that was used. See below.
+/**
+ * Generates a secret symmetric key from the given passphrase.
+ *
+ * Be sure to not compromise the key! Only keep it in memory, do not write
+ * it to disk.
+ *
+ * Note that this function is not deterministic; to derive the same key from
+ * a password, you also must know the random salt that was used. A
+ * deterministic version of this function is `tox_pass_key_derive_with_salt`.
+ *
+ * @param passphrase The user-provided password. Can be empty.
+ * @param passphrase_len The length of the password.
*
- * returns true on success
+ * @return new symmetric key on success, NULL on failure.
*/
-Tox_Pass_Key *tox_pass_key_derive(const uint8_t *passphrase, size_t pplength,
+Tox_Pass_Key *tox_pass_key_derive(const uint8_t *passphrase, size_t passphrase_len,
Tox_Err_Key_Derivation *error)
{
+ const Random *rng = system_random();
+
+ if (rng == nullptr) {
+ SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_FAILED);
+ return nullptr;
+ }
+
uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
- random_bytes(salt, sizeof(salt));
- return tox_pass_key_derive_with_salt(passphrase, pplength, salt, error);
+ random_bytes(rng, salt, sizeof(salt));
+ return tox_pass_key_derive_with_salt(passphrase, passphrase_len, salt, error);
}
-/* Same as above, except with use the given salt for deterministic key derivation.
- * The salt must be TOX_PASS_SALT_LENGTH bytes in length.
+/**
+ * Same as above, except use the given salt for deterministic key derivation.
+ *
+ * @param passphrase The user-provided password. Can be empty.
+ * @param passphrase_len The length of the password.
+ * @param salt An array of at least TOX_PASS_SALT_LENGTH bytes.
+ *
+ * @return new symmetric key on success, NULL on failure.
*/
-Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, size_t pplength,
+Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, size_t passphrase_len,
const uint8_t *salt, Tox_Err_Key_Derivation *error)
{
- if (!salt || (!passphrase && pplength != 0)) {
+ if (salt == nullptr || (passphrase == nullptr && passphrase_len != 0)) {
SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_NULL);
return nullptr;
}
uint8_t passkey[crypto_hash_sha256_BYTES];
- crypto_hash_sha256(passkey, passphrase, pplength);
+ crypto_hash_sha256(passkey, passphrase, passphrase_len);
uint8_t key[CRYPTO_SHARED_KEY_SIZE];
@@ -138,7 +166,7 @@ Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, size_t pp
Tox_Pass_Key *out_key = (Tox_Pass_Key *)calloc(1, sizeof(Tox_Pass_Key));
- if (!out_key) {
+ if (out_key == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_FAILED);
return nullptr;
}
@@ -164,9 +192,16 @@ Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, size_t pp
bool tox_pass_key_encrypt(const Tox_Pass_Key *key, const uint8_t *plaintext, size_t plaintext_len,
uint8_t *ciphertext, Tox_Err_Encryption *error)
{
- if (plaintext_len == 0 || !plaintext || !key || !ciphertext) {
+ const Random *rng = system_random();
+
+ if (rng == nullptr) {
+ SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_FAILED);
+ return false;
+ }
+
+ if (plaintext_len == 0 || plaintext == nullptr || key == nullptr || ciphertext == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_NULL);
- return 0;
+ return false;
}
// the output data consists of, in order:
@@ -185,7 +220,7 @@ bool tox_pass_key_encrypt(const Tox_Pass_Key *key, const uint8_t *plaintext, siz
ciphertext += crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
uint8_t nonce[crypto_box_NONCEBYTES];
- random_nonce(nonce);
+ random_nonce(rng, nonce);
memcpy(ciphertext, nonce, crypto_box_NONCEBYTES);
ciphertext += crypto_box_NONCEBYTES;
@@ -193,135 +228,161 @@ bool tox_pass_key_encrypt(const Tox_Pass_Key *key, const uint8_t *plaintext, siz
if (encrypt_data_symmetric(key->key, nonce, plaintext, plaintext_len, ciphertext)
!= plaintext_len + crypto_box_MACBYTES) {
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_FAILED);
- return 0;
+ return false;
}
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_OK);
- return 1;
+ return true;
}
-/* Encrypts the given data with the given passphrase. The output array must be
- * at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
- * to tox_derive_key and tox_pass_key_encrypt.
+/**
+ * Encrypts the given data with the given passphrase.
*
- * returns true on success
+ * The output array must be at least `plaintext_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH`
+ * bytes long. This delegates to tox_pass_key_derive and
+ * tox_pass_key_encrypt.
+ *
+ * @param plaintext A byte array of length `plaintext_len`.
+ * @param plaintext_len The length of the plain text array. Bigger than 0.
+ * @param passphrase The user-provided password. Can be empty.
+ * @param passphrase_len The length of the password.
+ * @param ciphertext The cipher text array to write the encrypted data to.
+ *
+ * @return true on success.
*/
-bool tox_pass_encrypt(const uint8_t *data, size_t data_len, const uint8_t *passphrase, size_t pplength, uint8_t *out,
- Tox_Err_Encryption *error)
+bool tox_pass_encrypt(const uint8_t *plaintext, size_t plaintext_len, const uint8_t *passphrase, size_t passphrase_len,
+ uint8_t *ciphertext, Tox_Err_Encryption *error)
{
Tox_Err_Key_Derivation err;
- Tox_Pass_Key *key = tox_pass_key_derive(passphrase, pplength, &err);
+ Tox_Pass_Key *key = tox_pass_key_derive(passphrase, passphrase_len, &err);
- if (!key) {
+ if (key == nullptr) {
if (err == TOX_ERR_KEY_DERIVATION_NULL) {
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_NULL);
} else if (err == TOX_ERR_KEY_DERIVATION_FAILED) {
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_KEY_DERIVATION_FAILED);
}
- return 0;
+ return false;
}
- bool result = tox_pass_key_encrypt(key, data, data_len, out, error);
+ const bool result = tox_pass_key_encrypt(key, plaintext, plaintext_len, ciphertext, error);
tox_pass_key_free(key);
return result;
}
-/* This is the inverse of tox_pass_key_encrypt, also using only keys produced by
- * tox_derive_key.
+/**
+ * This is the inverse of tox_pass_key_encrypt, also using only keys produced by
+ * tox_pass_key_derive or tox_pass_key_derive_with_salt.
*
- * the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
+ * @param ciphertext A byte array of length `ciphertext_len`.
+ * @param ciphertext_len The length of the cipher text array. At least TOX_PASS_ENCRYPTION_EXTRA_LENGTH.
+ * @param plaintext The plain text array to write the decrypted data to.
*
- * returns true on success
+ * @return true on success.
*/
-bool tox_pass_key_decrypt(const Tox_Pass_Key *key, const uint8_t *data, size_t length, uint8_t *out,
- Tox_Err_Decryption *error)
+bool tox_pass_key_decrypt(const Tox_Pass_Key *key, const uint8_t *ciphertext, size_t ciphertext_len,
+ uint8_t *plaintext, Tox_Err_Decryption *error)
{
- if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) {
+ if (ciphertext_len <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_INVALID_LENGTH);
- return 0;
+ return false;
}
- if (!data || !key || !out) {
+ if (ciphertext == nullptr || key == nullptr || plaintext == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_NULL);
- return 0;
+ return false;
}
- if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) {
+ if (memcmp(ciphertext, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_BAD_FORMAT);
- return 0;
+ return false;
}
- data += TOX_ENC_SAVE_MAGIC_LENGTH;
- data += crypto_pwhash_scryptsalsa208sha256_SALTBYTES; // salt only affects key derivation
+ ciphertext += TOX_ENC_SAVE_MAGIC_LENGTH;
+ ciphertext += crypto_pwhash_scryptsalsa208sha256_SALTBYTES; // salt only affects key derivation
- size_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
+ const size_t decrypt_length = ciphertext_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t nonce[crypto_box_NONCEBYTES];
- memcpy(nonce, data, crypto_box_NONCEBYTES);
- data += crypto_box_NONCEBYTES;
+ memcpy(nonce, ciphertext, crypto_box_NONCEBYTES);
+ ciphertext += crypto_box_NONCEBYTES;
- /* decrypt the data */
- if (decrypt_data_symmetric(key->key, nonce, data, decrypt_length + crypto_box_MACBYTES, out)
+ /* decrypt the ciphertext */
+ if (decrypt_data_symmetric(key->key, nonce, ciphertext, decrypt_length + crypto_box_MACBYTES, plaintext)
!= decrypt_length) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_FAILED);
- return 0;
+ return false;
}
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_OK);
- return 1;
+ return true;
}
-/* Decrypts the given data with the given passphrase. The output array must be
- * at least data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
- * to tox_pass_key_decrypt.
+/**
+ * Decrypts the given data with the given passphrase.
*
- * the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
+ * The output array must be at least `ciphertext_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH`
+ * bytes long. This delegates to tox_pass_key_decrypt.
*
- * returns true on success
+ * @param ciphertext A byte array of length `ciphertext_len`.
+ * @param ciphertext_len The length of the cipher text array. At least TOX_PASS_ENCRYPTION_EXTRA_LENGTH.
+ * @param passphrase The user-provided password. Can be empty.
+ * @param passphrase_len The length of the password.
+ * @param plaintext The plain text array to write the decrypted data to.
+ *
+ * @return true on success.
*/
-bool tox_pass_decrypt(const uint8_t *data, size_t length, const uint8_t *passphrase, size_t pplength, uint8_t *out,
- Tox_Err_Decryption *error)
+bool tox_pass_decrypt(const uint8_t *ciphertext, size_t ciphertext_len, const uint8_t *passphrase,
+ size_t passphrase_len, uint8_t *plaintext, Tox_Err_Decryption *error)
{
- if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) {
+ if (ciphertext_len <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_INVALID_LENGTH);
- return 0;
+ return false;
}
- if (!data || !passphrase || !out) {
+ if (ciphertext == nullptr || passphrase == nullptr || plaintext == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_NULL);
- return 0;
+ return false;
}
- if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) {
+ if (memcmp(ciphertext, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_BAD_FORMAT);
- return 0;
+ return false;
}
uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
- memcpy(salt, data + TOX_ENC_SAVE_MAGIC_LENGTH, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
+ memcpy(salt, ciphertext + TOX_ENC_SAVE_MAGIC_LENGTH, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
/* derive the key */
- Tox_Pass_Key *key = tox_pass_key_derive_with_salt(passphrase, pplength, salt, nullptr);
+ Tox_Pass_Key *key = tox_pass_key_derive_with_salt(passphrase, passphrase_len, salt, nullptr);
- if (!key) {
+ if (key == nullptr) {
/* out of memory most likely */
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_KEY_DERIVATION_FAILED);
- return 0;
+ return false;
}
- bool result = tox_pass_key_decrypt(key, data, length, out, error);
+ const bool result = tox_pass_key_decrypt(key, ciphertext, ciphertext_len, plaintext, error);
tox_pass_key_free(key);
return result;
}
-/* Determines whether or not the given data is encrypted (by checking the magic number)
+/**
+ * Determines whether or not the given data is encrypted by this module.
+ *
+ * It does this check by verifying that the magic number is the one put in
+ * place by the encryption functions.
+ *
+ * The data must be at least TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes in length.
+ * If the passed byte array is smaller than required, the behaviour is
+ * undefined.
+ *
+ * If the data pointer is NULL, the behaviour is undefined
+ *
+ * @return true if the data is encrypted by this module.
*/
bool tox_is_data_encrypted(const uint8_t *data)
{
- if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) == 0) {
- return 1;
- }
-
- return 0;
+ return memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) == 0;
}
diff --git a/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.h b/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.h
index 3f22b75884..90d8255eec 100644
--- a/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.h
+++ b/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.h
@@ -223,8 +223,8 @@ bool tox_pass_decrypt(const uint8_t *ciphertext, size_t ciphertext_len, const ui
*
* A pass-key and a password are two different concepts: a password is given
* by the user in plain text. A pass-key is the generated symmetric key used
- * for encryption and decryption. It is derived from a salt and the user-
- * provided password.
+ * for encryption and decryption. It is derived from a salt and the
+ * user-provided password.
*
* The Tox_Pass_Key structure is hidden in the implementation. It can be created
* using tox_pass_key_derive or tox_pass_key_derive_with_salt and must be deallocated using tox_pass_key_free.
@@ -235,10 +235,10 @@ typedef struct Tox_Pass_Key Tox_Pass_Key;
#endif /* TOX_PASS_KEY_DEFINED */
/**
- * Deallocate a Tox_Pass_Key. This function behaves like free(), so NULL is an
+ * Deallocate a Tox_Pass_Key. This function behaves like `free()`, so NULL is an
* acceptable argument value.
*/
-void tox_pass_key_free(struct Tox_Pass_Key *key);
+void tox_pass_key_free(Tox_Pass_Key *key);
/**
* Generates a secret symmetric key from the given passphrase.
@@ -253,9 +253,9 @@ void tox_pass_key_free(struct Tox_Pass_Key *key);
* @param passphrase The user-provided password. Can be empty.
* @param passphrase_len The length of the password.
*
- * @return true on success.
+ * @return new symmetric key on success, NULL on failure.
*/
-struct Tox_Pass_Key *tox_pass_key_derive(const uint8_t *passphrase, size_t passphrase_len,
+Tox_Pass_Key *tox_pass_key_derive(const uint8_t *passphrase, size_t passphrase_len,
Tox_Err_Key_Derivation *error);
/**
@@ -265,9 +265,9 @@ struct Tox_Pass_Key *tox_pass_key_derive(const uint8_t *passphrase, size_t passp
* @param passphrase_len The length of the password.
* @param salt An array of at least TOX_PASS_SALT_LENGTH bytes.
*
- * @return true on success.
+ * @return new symmetric key on success, NULL on failure.
*/
-struct Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, size_t passphrase_len,
+Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, size_t passphrase_len,
const uint8_t *salt, Tox_Err_Key_Derivation *error);
/**
@@ -282,7 +282,7 @@ struct Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, si
*
* @return true on success.
*/
-bool tox_pass_key_encrypt(const struct Tox_Pass_Key *key, const uint8_t *plaintext, size_t plaintext_len,
+bool tox_pass_key_encrypt(const Tox_Pass_Key *key, const uint8_t *plaintext, size_t plaintext_len,
uint8_t *ciphertext, Tox_Err_Encryption *error);
/**
@@ -295,7 +295,7 @@ bool tox_pass_key_encrypt(const struct Tox_Pass_Key *key, const uint8_t *plainte
*
* @return true on success.
*/
-bool tox_pass_key_decrypt(const struct Tox_Pass_Key *key, const uint8_t *ciphertext, size_t ciphertext_len,
+bool tox_pass_key_decrypt(const Tox_Pass_Key *key, const uint8_t *ciphertext, size_t ciphertext_len,
uint8_t *plaintext, Tox_Err_Decryption *error);
typedef enum Tox_Err_Get_Salt {