diff --git a/.clang-tidy b/.clang-tidy index d41d6628..8af9c0bd 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -42,7 +42,7 @@ CheckOptions: - key: concurrency-mt-unsafe.FunctionSet value: posix - key: readability-function-cognitive-complexity.Threshold - value: 153 # TODO(iphydf): Decrease. tox_new is the highest at the moment. + value: 159 # TODO(iphydf): Decrease. tox_new_system is the highest at the moment. - key: cppcoreguidelines-avoid-do-while.IgnoreMacros value: true - key: readability-simplify-boolean-expr.SimplifyDeMorgan diff --git a/.github/scripts/flags-gcc.sh b/.github/scripts/flags-gcc.sh index 50a0531a..df3ba21b 100644 --- a/.github/scripts/flags-gcc.sh +++ b/.github/scripts/flags-gcc.sh @@ -22,7 +22,6 @@ add_flag -Wframe-larger-than=9000 add_flag -Wignored-attributes add_flag -Wignored-qualifiers add_flag -Winit-self -add_flag -Winline add_flag -Wlarger-than=530000 add_flag -Wmaybe-uninitialized add_flag -Wmemset-transposed-args @@ -45,6 +44,8 @@ add_flag -Wunused-value # Disable specific warning flags for both C and C++. +# Not important, and bothersome with lambdas in C++. +add_flag -Wno-inline # struct Foo foo = {0}; is a common idiom. add_flag -Wno-missing-field-initializers # Checked by clang, but gcc is warning when it's not necessary. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10b3b9d6..29bed65d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: strategy: fail-fast: false matrix: - tool: [autotools, clang-tidy, compcert, cppcheck, doxygen, goblint, infer, misra, modules, pkgsrc, rpm, slimcc, sparse, tcc, tokstyle] + tool: [autotools, clang-tidy, compcert, cppcheck, doxygen, infer, misra, modules, pkgsrc, rpm, slimcc, sparse, tcc, tokstyle] runs-on: ubuntu-22.04 steps: - name: Set up Docker Buildx @@ -178,10 +178,10 @@ jobs: - uses: actions/checkout@v4 with: submodules: recursive - - name: Set up Python 3.9 + - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: 3.12 - name: Install mypy run: pip install mypy - name: Run mypy diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..c37efe45 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,57 @@ +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '39 10 * * 0' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libconfig-dev \ + libopus-dev \ + libsodium-dev \ + libvpx-dev \ + ninja-build \ + pkg-config + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + queries: security-and-quality + + - name: Build + run: | + cmake -GNinja -B _build -S . + cmake --build _build --parallel $(nproc) + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c800d6e1..74daccdb 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -138,7 +138,7 @@ jobs: - name: Build and store to local Docker daemon uses: docker/build-push-action@v5 with: - context: other/docker/windows + file: other/docker/windows/windows.Dockerfile load: true tags: toxchat/windows:win${{ matrix.bits }} cache-from: type=registry,ref=toxchat/windows:win${{ matrix.bits }} @@ -150,7 +150,7 @@ jobs: if: ${{ github.event_name == 'push' }} uses: docker/build-push-action@v5 with: - context: other/docker/windows + file: other/docker/windows/windows.Dockerfile push: ${{ github.event_name == 'push' }} tags: toxchat/windows:win${{ matrix.bits }} build-args: | diff --git a/.gitignore b/.gitignore index 33d31c03..bef464b5 100644 --- a/.gitignore +++ b/.gitignore @@ -98,5 +98,10 @@ tox.spec .cache/ compile_commands.json +# gtags +/GPATH +/GRTAGS +/GTAGS + /infer .idea/ diff --git a/BUILD.bazel b/BUILD.bazel index a5c465af..5e8dc4dc 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -8,6 +8,7 @@ genrule( srcs = [ "//c-toxcore/toxav:toxav.h", "//c-toxcore/toxcore:tox.h", + "//c-toxcore/toxcore:tox_attributes.h", "//c-toxcore/toxcore:tox_dispatch.h", "//c-toxcore/toxcore:tox_events.h", "//c-toxcore/toxcore:tox_log_level.h", @@ -18,6 +19,7 @@ genrule( outs = [ "tox/toxav.h", "tox/tox.h", + "tox/tox_attributes.h", "tox/tox_dispatch.h", "tox/tox_events.h", "tox/tox_log_level.h", @@ -28,6 +30,7 @@ genrule( cmd = """ cp $(location //c-toxcore/toxav:toxav.h) $(GENDIR)/c-toxcore/tox/toxav.h cp $(location //c-toxcore/toxcore:tox.h) $(GENDIR)/c-toxcore/tox/tox.h + cp $(location //c-toxcore/toxcore:tox_attributes.h) $(GENDIR)/c-toxcore/tox/tox_attributes.h cp $(location //c-toxcore/toxcore:tox_dispatch.h) $(GENDIR)/c-toxcore/tox/tox_dispatch.h cp $(location //c-toxcore/toxcore:tox_events.h) $(GENDIR)/c-toxcore/tox/tox_events.h cp $(location //c-toxcore/toxcore:tox_log_level.h) $(GENDIR)/c-toxcore/tox/tox_log_level.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a061821..ebb9ccec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,8 @@ endif() set_source_files_properties( toxcore/mono_time.c - toxcore/network.c + toxcore/os_event.c + toxcore/os_network.c toxcore/tox.c toxcore/util.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION TRUE) @@ -70,13 +71,27 @@ include(CTest) include(ModulePackage) include(StrictAbi) include(GNUInstallDirs) +include(CheckIncludeFile) +include(CheckSymbolExists) if(APPLE) include(MacRpath) endif() +if(CMAKE_SYSTEM_NAME MATCHES "Linux") + check_include_file(sys/epoll.h HAVE_SYS_EPOLL_H) + if(HAVE_SYS_EPOLL_H) + check_symbol_exists(epoll_create "sys/epoll.h" HAVE_EPOLL_CREATE) + if(HAVE_EPOLL_CREATE) + add_definitions(-DEV_USE_EPOLL=1) + add_definitions(-DTCP_SERVER_USE_EPOLL=1) + endif() + endif() +endif() + enable_testing() find_package(GTest) +find_package(benchmark QUIET) set(CMAKE_MACOSX_RPATH ON) @@ -232,6 +247,8 @@ set(toxcore_SOURCES toxcore/crypto_core_pack.h toxcore/DHT.c toxcore/DHT.h + toxcore/ev.c + toxcore/ev.h toxcore/events/conference_connected.c toxcore/events/conference_invite.c toxcore/events/conference_message.c @@ -307,6 +324,8 @@ set(toxcore_SOURCES toxcore/mem.h toxcore/mono_time.c toxcore/mono_time.h + toxcore/net.c + toxcore/net.h toxcore/net_crypto.c toxcore/net_crypto.h toxcore/net_log.c @@ -321,14 +340,20 @@ set(toxcore_SOURCES toxcore/onion_client.c toxcore/onion_client.h toxcore/onion.h + toxcore/os_event.c + toxcore/os_event.h toxcore/os_memory.c toxcore/os_memory.h + toxcore/os_network.c + toxcore/os_network.h toxcore/os_random.c toxcore/os_random.h toxcore/ping_array.c toxcore/ping_array.h toxcore/ping.c toxcore/ping.h + toxcore/rng.c + toxcore/rng.h toxcore/shared_key_cache.c toxcore/shared_key_cache.h toxcore/sort.c @@ -357,18 +382,12 @@ set(toxcore_SOURCES toxcore/tox_events.h toxcore/tox_log_level.c toxcore/tox_log_level.h - toxcore/tox_memory.c - toxcore/tox_memory.h - toxcore/tox_memory_impl.h toxcore/tox_options.c toxcore/tox_options.h toxcore/tox_private.c toxcore/tox_private.h toxcore/tox_pack.c toxcore/tox_pack.h - toxcore/tox_random.c - toxcore/tox_random.h - toxcore/tox_random_impl.h toxcore/tox_unpack.c toxcore/tox_unpack.h toxcore/util.c @@ -390,10 +409,10 @@ set(toxcore_API_HEADERS ${toxcore_SOURCE_DIR}/toxcore/tox_options.h^tox) if(EXPERIMENTAL_API) set(toxcore_API_HEADERS ${toxcore_API_HEADERS} + ${toxcore_SOURCE_DIR}/toxcore/tox_attributes.h^tox ${toxcore_SOURCE_DIR}/toxcore/tox_dispatch.h^tox ${toxcore_SOURCE_DIR}/toxcore/tox_events.h^tox - ${toxcore_SOURCE_DIR}/toxcore/tox_private.h^tox - ${toxcore_SOURCE_DIR}/toxcore/tox_random.h^tox) + ${toxcore_SOURCE_DIR}/toxcore/tox_private.h^tox) endif() ################################################################################ @@ -547,12 +566,22 @@ if(UNITTEST) toxcore/DHT_test_util.hh toxcore/crypto_core_test_util.cc toxcore/crypto_core_test_util.hh + toxcore/ev_test_util.cc + toxcore/ev_test_util.hh toxcore/mono_time_test_util.cc toxcore/mono_time_test_util.hh toxcore/network_test_util.cc toxcore/network_test_util.hh + toxcore/sort_test_util.cc + toxcore/sort_test_util.hh toxcore/test_util.cc toxcore/test_util.hh) + target_link_libraries(test_util PUBLIC support) + if(TARGET toxcore_static) + target_link_libraries(test_util PRIVATE toxcore_static) + else() + target_link_libraries(test_util PRIVATE toxcore_shared) + endif() endif() function(unit_test subdir target) @@ -563,12 +592,8 @@ function(unit_test subdir target) else() target_link_libraries(unit_${target}_test PRIVATE toxcore_shared) endif() - if(TARGET pthreads4w::pthreads4w) - target_link_libraries(unit_${target}_test PRIVATE pthreads4w::pthreads4w) - elseif(TARGET PThreads4W::PThreads4W) - target_link_libraries(unit_${target}_test PRIVATE PThreads4W::PThreads4W) - elseif(TARGET Threads::Threads) - target_link_libraries(unit_${target}_test PRIVATE Threads::Threads) + if(WIN32) + target_link_libraries(unit_${target}_test PRIVATE ws2_32) endif() target_link_libraries(unit_${target}_test PRIVATE GTest::gtest GTest::gtest_main GTest::gmock) set_target_properties(unit_${target}_test PROPERTIES COMPILE_FLAGS "${TEST_CXX_FLAGS}") @@ -588,7 +613,7 @@ if(UNITTEST AND TARGET GTest::gtest AND TARGET GTest::gmock) else() target_link_libraries(av_test_support PRIVATE toxcore_shared) endif() - target_link_libraries(av_test_support PRIVATE GTest::gtest) + target_link_libraries(av_test_support PUBLIC support GTest::gtest) unit_test(toxav audio) target_link_libraries(unit_audio_test PRIVATE av_test_support) @@ -601,16 +626,27 @@ if(UNITTEST AND TARGET GTest::gtest AND TARGET GTest::gmock) endif() unit_test(toxcore DHT) + unit_test(toxcore TCP_client) + unit_test(toxcore TCP_common) + unit_test(toxcore TCP_connection) unit_test(toxcore bin_pack) unit_test(toxcore crypto_core) + unit_test(toxcore ev) + unit_test(toxcore friend_connection) unit_test(toxcore group_announce) unit_test(toxcore group_moderation) unit_test(toxcore list) unit_test(toxcore mem) unit_test(toxcore mono_time) + unit_test(toxcore net_crypto) + unit_test(toxcore network) + unit_test(toxcore onion_client) unit_test(toxcore ping_array) + unit_test(toxcore shared_key_cache) + unit_test(toxcore sort) unit_test(toxcore test_util) unit_test(toxcore tox) + unit_test(toxcore tox_events) unit_test(toxcore util) endif() @@ -645,13 +681,6 @@ if(DHT_BOOTSTRAP) elseif(TARGET unofficial-sodium::sodium) target_link_libraries(DHT_bootstrap PRIVATE unofficial-sodium::sodium) endif() - if(TARGET pthreads4w::pthreads4w) - target_link_libraries(DHT_bootstrap PRIVATE pthreads4w::pthreads4w) - elseif(TARGET PThreads4W::PThreads4W) - target_link_libraries(DHT_bootstrap PRIVATE PThreads4W::PThreads4W) - elseif(TARGET Threads::Threads) - target_link_libraries(DHT_bootstrap PRIVATE Threads::Threads) - endif() install(TARGETS DHT_bootstrap RUNTIME DESTINATION bin) endif() @@ -671,3 +700,53 @@ endif() if (BUILD_FUZZ_TESTS) add_subdirectory(testing/fuzzing) endif() + +################################################################################ +# +# :: Benchmarks +# +################################################################################ + +if(UNITTEST AND benchmark_FOUND) + if(BUILD_TOXAV AND TARGET av_test_support) + add_executable(rtp_bench toxav/rtp_bench.cc) + target_link_libraries(rtp_bench PRIVATE + toxcore_static + av_test_support + benchmark::benchmark + ) + + add_executable(audio_bench toxav/audio_bench.cc) + target_link_libraries(audio_bench PRIVATE + toxcore_static + av_test_support + benchmark::benchmark + ) + + add_executable(video_bench toxav/video_bench.cc) + target_link_libraries(video_bench PRIVATE + toxcore_static + av_test_support + benchmark::benchmark + ) + endif() + + add_executable(sort_bench + toxcore/sort_bench.cc + toxcore/sort_test_util.cc + toxcore/sort_test_util.hh + ) + target_link_libraries(sort_bench PRIVATE + toxcore_static + benchmark::benchmark + ) + + add_executable(ev_bench + toxcore/ev_bench.cc + ) + target_link_libraries(ev_bench PRIVATE + test_util + toxcore_static + benchmark::benchmark + ) +endif() diff --git a/INSTALL.md b/INSTALL.md index 67e83717..15736178 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -286,7 +286,7 @@ requirements are that you have Docker version of >= 1.9.0 and you are running 64-bit system. The cross-compilation is fully automated by a parameterized -[Dockerfile](/other/docker/windows/Dockerfile). +[Dockerfile](/other/docker/windows/windows.Dockerfile). Install Docker @@ -313,10 +313,10 @@ available to customize the building of the container image. Example of building a container image with options ```sh -cd other/docker/windows docker build \ --build-arg SUPPORT_TEST=true \ -t toxcore \ + -f other/docker/windows/windows.Dockerfile \ . ``` diff --git a/README.md b/README.md index e7a2b6ba..6b9ea04f 100644 --- a/README.md +++ b/README.md @@ -195,8 +195,6 @@ This project uses various tools supporting Static Application Security Testing: - [cppcheck](https://cppcheck.sourceforge.io/): A static analyzer for C/C++ code. - [cpplint](https://github.com/cpplint/cpplint): Static code checker for C++ -- [goblint](https://goblint.in.tum.de/): A static analyzer for multi-threaded C - programs, specializing in finding concurrency bugs. - [infer](https://github.com/facebook/infer): A static analyzer for Java, C, C++, and Objective-C. - [PVS-Studio](https://pvs-studio.com/en/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source): diff --git a/auto_tests/BUILD.bazel b/auto_tests/BUILD.bazel index 6cfc2f20..4fa2a704 100644 --- a/auto_tests/BUILD.bazel +++ b/auto_tests/BUILD.bazel @@ -4,6 +4,8 @@ cc_library( name = "check_compat", testonly = True, hdrs = ["check_compat.h"], + visibility = ["//c-toxcore/auto_tests:__subpackages__"], + deps = ["//c-toxcore/toxcore:ccompat"], ) cc_library( @@ -14,11 +16,14 @@ cc_library( deps = [ ":check_compat", "//c-toxcore/testing:misc_tools", + "//c-toxcore/toxcore:DHT", "//c-toxcore/toxcore:Messenger", "//c-toxcore/toxcore:mono_time", + "//c-toxcore/toxcore:net_crypto", "//c-toxcore/toxcore:tox", "//c-toxcore/toxcore:tox_dispatch", "//c-toxcore/toxcore:tox_events", + "//c-toxcore/toxcore:tox_log_level", ], ) @@ -72,6 +77,8 @@ extra_data = { "//c-toxcore/toxcore:onion", "//c-toxcore/toxcore:onion_announce", "//c-toxcore/toxcore:onion_client", + "//c-toxcore/toxcore:os_memory", + "//c-toxcore/toxcore:os_random", "//c-toxcore/toxcore:tox", "//c-toxcore/toxcore:tox_dispatch", "//c-toxcore/toxcore:tox_events", diff --git a/auto_tests/Makefile.inc b/auto_tests/Makefile.inc index f786f2f2..7591609f 100644 --- a/auto_tests/Makefile.inc +++ b/auto_tests/Makefile.inc @@ -43,6 +43,7 @@ TESTS = \ scenario_friend_read_receipt_test \ scenario_friend_request_spam_test \ scenario_friend_request_test \ + scenario_group_by_id_test \ scenario_group_general_test \ scenario_group_invite_test \ scenario_group_message_test \ @@ -232,6 +233,10 @@ scenario_friend_request_spam_test_SOURCES = ../auto_tests/scenarios/scenario_fri scenario_friend_request_spam_test_CFLAGS = $(AUTOTEST_CFLAGS) scenario_friend_request_spam_test_LDADD = $(AUTOTEST_LDADD) libscenario_framework.la +scenario_group_by_id_test_SOURCES = ../auto_tests/scenarios/scenario_group_by_id_test.c +scenario_group_by_id_test_CFLAGS = $(AUTOTEST_CFLAGS) +scenario_group_by_id_test_LDADD = $(AUTOTEST_LDADD) libscenario_framework.la + scenario_group_general_test_SOURCES = ../auto_tests/scenarios/scenario_group_general_test.c scenario_group_general_test_CFLAGS = $(AUTOTEST_CFLAGS) scenario_group_general_test_LDADD = $(AUTOTEST_LDADD) libscenario_framework.la diff --git a/auto_tests/check_compat.h b/auto_tests/check_compat.h index 660ff40b..fbee0002 100644 --- a/auto_tests/check_compat.h +++ b/auto_tests/check_compat.h @@ -7,28 +7,53 @@ #include #ifndef ck_assert -#define ck_assert(ok) do { \ - if (!(ok)) { \ - fprintf(stderr, "%s:%d: failed `%s'\n", __FILE__, __LINE__, #ok); \ - exit(7); \ - } \ -} while (0) +#define ck_assert(ok) \ + do { \ + if (!(ok)) { \ + fprintf(stderr, "%s:%d: failed `%s'\n", __FILE__, __LINE__, #ok); \ + exit(7); \ + } \ + } while (0) -#define ck_assert_msg(ok, ...) do { \ - if (!(ok)) { \ - fprintf(stderr, "%s:%d: failed `%s': ", __FILE__, __LINE__, #ok); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, "\n"); \ - exit(7); \ - } \ -} while (0) +#define ck_assert_msg(ok, ...) \ + do { \ + if (!(ok)) { \ + fprintf(stderr, "%s:%d: failed `%s': ", __FILE__, __LINE__, #ok); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + exit(7); \ + } \ + } while (0) -#define ck_abort_msg(...) do { \ - fprintf(stderr, "%s:%d: ", __FILE__, __LINE__); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, "\n"); \ - exit(7); \ -} while (0) +#define ck_assert_int_eq(a, b) \ + do { \ + const int32_t _a = (a); \ + const int32_t _b = (b); \ + if (_a != _b) { \ + fprintf(stderr, "%s:%d: failed `%s == %s` (%d != %d)\n", __FILE__, __LINE__, #a, #b, \ + _a, _b); \ + exit(7); \ + } \ + } while (0) + +#define ck_assert_uint_eq(a, b) \ + do { \ + const uint32_t _a = (a); \ + const uint32_t _b = (b); \ + if (_a != _b) { \ + fprintf(stderr, "%s:%d: failed `%s == %s` (%u != %u)\n", __FILE__, __LINE__, #a, #b, \ + _a, _b); \ + exit(7); \ + } \ + } while (0) + +#define ck_abort_msg(...) \ + do { \ + fprintf(stderr, "%s:%d: ", __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + exit(7); \ + } while (0) #endif #endif // C_TOXCORE_AUTO_TESTS_CHECK_COMPAT_H diff --git a/auto_tests/scenarios/BUILD.bazel b/auto_tests/scenarios/BUILD.bazel index 05da628a..034c4e2d 100644 --- a/auto_tests/scenarios/BUILD.bazel +++ b/auto_tests/scenarios/BUILD.bazel @@ -7,7 +7,10 @@ cc_library( hdrs = ["framework/framework.h"], visibility = ["//visibility:public"], deps = [ + "//c-toxcore/auto_tests:check_compat", "//c-toxcore/testing:misc_tools", + "//c-toxcore/toxcore:attributes", + "//c-toxcore/toxcore:ccompat", "//c-toxcore/toxcore:mono_time", "//c-toxcore/toxcore:network", "//c-toxcore/toxcore:tox", diff --git a/auto_tests/scenarios/CMakeLists.txt b/auto_tests/scenarios/CMakeLists.txt index ee70e420..551993c4 100644 --- a/auto_tests/scenarios/CMakeLists.txt +++ b/auto_tests/scenarios/CMakeLists.txt @@ -45,6 +45,7 @@ scenario_test(scenario_friend_query) scenario_test(scenario_friend_read_receipt) scenario_test(scenario_friend_request) scenario_test(scenario_friend_request_spam) +scenario_test(scenario_group_by_id) scenario_test(scenario_group_general) scenario_test(scenario_group_invite) scenario_test(scenario_group_message) diff --git a/auto_tests/scenarios/framework/framework.c b/auto_tests/scenarios/framework/framework.c index dfb18caa..17640605 100644 --- a/auto_tests/scenarios/framework/framework.c +++ b/auto_tests/scenarios/framework/framework.c @@ -159,15 +159,16 @@ ToxScenario *tox_scenario_new(int argc, char *const argv[], uint64_t timeout_ms) ToxScenario *s = (ToxScenario *)calloc(1, sizeof(ToxScenario)); ck_assert(s != nullptr); - s->timeout_ms = timeout_ms; - s->virtual_clock = 1000; - s->trace_enabled = (getenv("TOX_TRACE") != nullptr); - s->event_log_enabled = (getenv("TOX_EVENT_LOG") != nullptr); pthread_mutex_init(&s->mutex, nullptr); pthread_mutex_init(&s->clock_mutex, nullptr); pthread_cond_init(&s->cond_runner, nullptr); pthread_cond_init(&s->cond_nodes, nullptr); + + s->timeout_ms = timeout_ms; + s->virtual_clock = 1000; + s->trace_enabled = (getenv("TOX_TRACE") != nullptr); + s->event_log_enabled = (getenv("TOX_EVENT_LOG") != nullptr); return s; } @@ -660,10 +661,10 @@ ToxScenarioStatus tox_scenario_run(ToxScenario *s) pthread_mutex_lock(&s->mutex); - uint64_t start_clock = s->virtual_clock; + uint64_t start_clock = tox_scenario_get_time(s); uint64_t deadline = start_clock + s->timeout_ms; - while (s->num_active > 0 && s->virtual_clock < deadline) { + while (s->num_active > 0 && tox_scenario_get_time(s) < deadline) { // 1. Wait until all nodes (including finished ones) have reached the barrier while (s->num_ready < s->num_nodes) { pthread_cond_wait(&s->cond_runner, &s->mutex); @@ -700,7 +701,7 @@ ToxScenarioStatus tox_scenario_run(ToxScenario *s) ToxScenarioStatus result = (s->num_active == 0) ? TOX_SCENARIO_DONE : TOX_SCENARIO_TIMEOUT; if (result == TOX_SCENARIO_TIMEOUT) { - tox_scenario_log(s, "Scenario TIMEOUT after %lu ms (virtual time)", (unsigned long)(s->virtual_clock - start_clock)); + tox_scenario_log(s, "Scenario TIMEOUT after %lu ms (virtual time)", (unsigned long)(tox_scenario_get_time(s) - start_clock)); } // Stop nodes diff --git a/auto_tests/scenarios/framework/framework.h b/auto_tests/scenarios/framework/framework.h index bde450c2..25b99c89 100644 --- a/auto_tests/scenarios/framework/framework.h +++ b/auto_tests/scenarios/framework/framework.h @@ -6,6 +6,7 @@ #include #include +#include "../../check_compat.h" #include "../../../toxcore/attributes.h" #include "../../../toxcore/ccompat.h" #include "../../../toxcore/tox.h" @@ -194,22 +195,4 @@ void tox_node_friend_add(ToxNode *a, ToxNode *b); */ void tox_node_reload(ToxNode *node); -#ifndef ck_assert -#define ck_assert(ok) do { \ - if (!(ok)) { \ - fprintf(stderr, "%s:%d: failed `%s'\n", __FILE__, __LINE__, #ok); \ - exit(7); \ - } \ -} while (0) - -#define ck_assert_msg(ok, ...) do { \ - if (!(ok)) { \ - fprintf(stderr, "%s:%d: failed `%s': ", __FILE__, __LINE__, #ok); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, "\n"); \ - exit(7); \ - } \ -} while (0) -#endif - #endif // TOX_TEST_FRAMEWORK_H diff --git a/auto_tests/scenarios/scenario_file_transfer_test.c b/auto_tests/scenarios/scenario_file_transfer_test.c index c6e44422..942ef841 100644 --- a/auto_tests/scenarios/scenario_file_transfer_test.c +++ b/auto_tests/scenarios/scenario_file_transfer_test.c @@ -106,6 +106,34 @@ static void sender_script(ToxNode *self, void *ctx) uint32_t fnum = tox_file_send(tox_node_get_tox(self), 0, TOX_FILE_KIND_DATA, FILE_SIZE, nullptr, (const uint8_t *)FILENAME, sizeof(FILENAME), nullptr); tox_node_log(self, "Started sending file %u", fnum); + Tox_File_Id file_id; + if (!tox_file_get_file_id(tox_node_get_tox(self), 0, fnum, file_id, nullptr)) { + tox_node_log(self, "Failed to get file id for file %u", fnum); + abort(); + } + + Tox_Err_File_By_Id err; + Tox_File_Number found_fnum = tox_file_by_id(tox_node_get_tox(self), 0, file_id, &err); + if (found_fnum != fnum) { + tox_node_log(self, "tox_file_by_id failed: expected %u, got %u, error %u", fnum, found_fnum, err); + abort(); + } + + /* Test with invalid friend number */ + found_fnum = tox_file_by_id(tox_node_get_tox(self), 1234, file_id, &err); + if (found_fnum != UINT32_MAX || err != TOX_ERR_FILE_BY_ID_FRIEND_NOT_FOUND) { + tox_node_log(self, "tox_file_by_id with invalid friend failed: expected UINT32_MAX and FRIEND_NOT_FOUND, got %u and error %u", found_fnum, err); + abort(); + } + + /* Test with invalid file id */ + Tox_File_Id invalid_id = {0}; + found_fnum = tox_file_by_id(tox_node_get_tox(self), 0, invalid_id, &err); + if (found_fnum != UINT32_MAX || err != TOX_ERR_FILE_BY_ID_NOT_FOUND) { + tox_node_log(self, "tox_file_by_id with invalid id failed: expected UINT32_MAX and NOT_FOUND, got %u and error %u", found_fnum, err); + abort(); + } + WAIT_UNTIL(state->file_sending_done); tox_node_log(self, "Done"); } diff --git a/auto_tests/scenarios/scenario_group_by_id_test.c b/auto_tests/scenarios/scenario_group_by_id_test.c new file mode 100644 index 00000000..b530f61d --- /dev/null +++ b/auto_tests/scenarios/scenario_group_by_id_test.c @@ -0,0 +1,96 @@ +#include "framework/framework.h" +#include +#include +#include + +#define GROUP_NAME "NASA Headquarters" +#define GROUP_NAME_LEN (sizeof(GROUP_NAME) - 1) +#define PEER_NICK "Test Nick" +#define PEER_NICK_LEN (sizeof(PEER_NICK) - 1) + +typedef struct { + uint32_t group_number; + uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE]; +} GroupState; + +static void script(ToxNode *self, void *ctx) +{ + GroupState *state = (GroupState *)ctx; + Tox *tox = tox_node_get_tox(self); + + Tox_Err_Group_New err_new; + state->group_number = tox_group_new(tox, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)GROUP_NAME, GROUP_NAME_LEN, (const uint8_t *)PEER_NICK, PEER_NICK_LEN, &err_new); + ck_assert_int_eq(err_new, TOX_ERR_GROUP_NEW_OK); + tox_node_log(self, "Group created: %u", state->group_number); + + Tox_Err_Group_State_Query err_query; + ck_assert(tox_group_get_chat_id(tox, state->group_number, state->chat_id, &err_query)); + ck_assert_int_eq(err_query, TOX_ERR_GROUP_STATE_QUERY_OK); + + { + // Test tox_group_by_id + Tox_Err_Group_By_Id err_by_id; + uint32_t group_number = tox_group_by_id(tox, state->chat_id, &err_by_id); + ck_assert_int_eq(err_by_id, TOX_ERR_GROUP_BY_ID_OK); + ck_assert_uint_eq(group_number, state->group_number); + + // Test tox_group_by_id with invalid ID + uint8_t invalid_chat_id[TOX_GROUP_CHAT_ID_SIZE]; + memset(invalid_chat_id, 0xAA, TOX_GROUP_CHAT_ID_SIZE); + group_number = tox_group_by_id(tox, invalid_chat_id, &err_by_id); + ck_assert_int_eq(err_by_id, TOX_ERR_GROUP_BY_ID_NOT_FOUND); + ck_assert_uint_eq(group_number, UINT32_MAX); + + // Test tox_group_by_id with NULL ID + group_number = tox_group_by_id(tox, NULL, &err_by_id); + ck_assert_int_eq(err_by_id, TOX_ERR_GROUP_BY_ID_NULL); + ck_assert_uint_eq(group_number, UINT32_MAX); + } + + // Create another group to ensure it works with multiple groups + Tox_Group_Number group2 = tox_group_new(tox, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)"Group 2", 7, (const uint8_t *)PEER_NICK, PEER_NICK_LEN, &err_new); + ck_assert_int_eq(err_new, TOX_ERR_GROUP_NEW_OK); + uint8_t chat_id2[TOX_GROUP_CHAT_ID_SIZE]; + ck_assert(tox_group_get_chat_id(tox, group2, chat_id2, &err_query)); + ck_assert_int_eq(err_query, TOX_ERR_GROUP_STATE_QUERY_OK); + + { + Tox_Err_Group_By_Id err_by_id; + ck_assert_uint_eq(tox_group_by_id(tox, state->chat_id, &err_by_id), state->group_number); + ck_assert_int_eq(err_by_id, TOX_ERR_GROUP_BY_ID_OK); + + ck_assert_uint_eq(tox_group_by_id(tox, chat_id2, &err_by_id), group2); + ck_assert_int_eq(err_by_id, TOX_ERR_GROUP_BY_ID_OK); + } + + Tox_Err_Group_Leave err_leave; + ck_assert(tox_group_leave(tox, state->group_number, nullptr, 0, &err_leave)); + ck_assert_int_eq(err_leave, TOX_ERR_GROUP_LEAVE_OK); + + // Yield to allow the group to be actually deleted + tox_scenario_yield(self); + + { + // After leaving, it should not be found + Tox_Err_Group_By_Id err_by_id; + uint32_t group_number = tox_group_by_id(tox, state->chat_id, &err_by_id); + ck_assert_int_eq(err_by_id, TOX_ERR_GROUP_BY_ID_NOT_FOUND); + ck_assert_uint_eq(group_number, UINT32_MAX); + } +} + +int main(int argc, char *argv[]) +{ + ToxScenario *s = tox_scenario_new(argc, argv, 60000); + GroupState state = {0}; + + tox_scenario_add_node(s, "Node", script, &state, sizeof(GroupState)); + + ToxScenarioStatus res = tox_scenario_run(s); + if (res != TOX_SCENARIO_DONE) { + return 1; + } + + tox_scenario_free(s); + return 0; +} diff --git a/auto_tests/scenarios/scenario_group_topic_test.c b/auto_tests/scenarios/scenario_group_topic_test.c index c6f3b5a6..cf244616 100644 --- a/auto_tests/scenarios/scenario_group_topic_test.c +++ b/auto_tests/scenarios/scenario_group_topic_test.c @@ -305,6 +305,8 @@ int main(int argc, char *argv[]) return 0; } -#undef GROUP_NAME +#undef TOPIC2 +#undef TOPIC1 #undef GROUP_NAME_LEN +#undef GROUP_NAME #undef NUM_PEERS diff --git a/configure.ac b/configure.ac index ee095b1a..5fe35cd9 100644 --- a/configure.ac +++ b/configure.ac @@ -158,6 +158,7 @@ AX_HAVE_EPOLL if test "$enable_epoll" != "no"; then if test "${ax_cv_have_epoll}" = "yes"; then AC_DEFINE([TCP_SERVER_USE_EPOLL],[1],[define to 1 to enable epoll support]) + AC_DEFINE([EV_USE_EPOLL],[1],[define to 1 to enable epoll support]) enable_epoll='yes' else if test "$enable_epoll" = "yes"; then diff --git a/other/BUILD.bazel b/other/BUILD.bazel index 3239c5c1..c314b82c 100644 --- a/other/BUILD.bazel +++ b/other/BUILD.bazel @@ -31,6 +31,8 @@ cc_binary( "//c-toxcore/toxcore:network", "//c-toxcore/toxcore:onion", "//c-toxcore/toxcore:onion_announce", + "//c-toxcore/toxcore:os_memory", + "//c-toxcore/toxcore:os_random", "//c-toxcore/toxcore:tox", ], ) diff --git a/other/analysis/run-clang b/other/analysis/run-clang index bafcc6f2..bfcba0c8 100755 --- a/other/analysis/run-clang +++ b/other/analysis/run-clang @@ -10,7 +10,7 @@ run() { "${CPPFLAGS[@]}" \ "${LDFLAGS[@]}" \ "$@" \ - -std=c++17 \ + -std=c++20 \ -Werror \ -Weverything \ -Wno-alloca \ diff --git a/other/analysis/run-gcc b/other/analysis/run-gcc index c3252982..2fa4df48 100755 --- a/other/analysis/run-gcc +++ b/other/analysis/run-gcc @@ -11,7 +11,7 @@ run() { "${CPPFLAGS[@]}" \ "${LDFLAGS[@]}" \ "$@" \ - -std=c++17 \ + -std=c++20 \ -fdiagnostics-color=always \ -Wall \ -Wextra \ @@ -20,6 +20,7 @@ run() { -Wno-aggressive-loop-optimizations \ -Wno-float-conversion \ -Wno-format-signedness \ + -Wno-inline \ -Wno-missing-field-initializers \ -Wno-nonnull-compare \ -Wno-padded \ @@ -46,7 +47,6 @@ run() { -Wignored-attributes \ -Wignored-qualifiers \ -Winit-self \ - -Winline \ -Wlarger-than=530000 \ -Wmaybe-uninitialized \ -Wmemset-transposed-args \ diff --git a/other/bootstrap_daemon/BUILD.bazel b/other/bootstrap_daemon/BUILD.bazel index 35b09ef2..824fcdd1 100644 --- a/other/bootstrap_daemon/BUILD.bazel +++ b/other/bootstrap_daemon/BUILD.bazel @@ -25,6 +25,7 @@ cc_binary( "//c-toxcore/toxcore:network", "//c-toxcore/toxcore:onion", "//c-toxcore/toxcore:onion_announce", + "//c-toxcore/toxcore:os_memory", "//c-toxcore/toxcore:os_random", "//c-toxcore/toxcore:tox", "@libconfig", diff --git a/other/bootstrap_daemon/src/tox-bootstrapd.c b/other/bootstrap_daemon/src/tox-bootstrapd.c index e5e24a22..5b479013 100644 --- a/other/bootstrap_daemon/src/tox-bootstrapd.c +++ b/other/bootstrap_daemon/src/tox-bootstrapd.c @@ -290,8 +290,8 @@ int main(int argc, char *argv[]) IP ip; ip_init(&ip, enable_ipv6); - const Tox_Memory *mem = os_memory(); - const Tox_Random *rng = os_random(); + const Memory *mem = os_memory(); + const Random *rng = os_random(); const Network *ns = os_network(); Logger *logger = logger_new(mem); diff --git a/other/docker/codeql/build.sh b/other/docker/codeql/build.sh new file mode 100644 index 00000000..2de97444 --- /dev/null +++ b/other/docker/codeql/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +cmake -GNinja -B build -S . +cmake --build build --parallel "$(nproc)" diff --git a/other/docker/codeql/codeql.Dockerfile b/other/docker/codeql/codeql.Dockerfile new file mode 100644 index 00000000..1140cab1 --- /dev/null +++ b/other/docker/codeql/codeql.Dockerfile @@ -0,0 +1,53 @@ +# other/docker/codeql/codeql.Dockerfile +FROM toxchat/c-toxcore:sources AS sources +FROM ubuntu:22.04 + +RUN apt-get update && \ + DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + cmake \ + curl \ + git \ + libconfig-dev \ + libopus-dev \ + libsodium-dev \ + libvpx-dev \ + ninja-build \ + pkg-config \ + unzip \ + wget \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Install CodeQL +ARG CODEQL_VERSION=v2.23.9 +RUN curl -L -o /tmp/codeql.zip https://github.com/github/codeql-cli-binaries/releases/download/${CODEQL_VERSION}/codeql-linux64.zip && \ + unzip -q /tmp/codeql.zip -d /opt && \ + rm /tmp/codeql.zip + +ENV PATH="/opt/codeql:$PATH" + +RUN groupadd -r -g 1000 builder \ + && useradd -m --no-log-init -r -g builder -u 1000 builder + +WORKDIR /home/builder/c-toxcore + +# Copy sources +COPY --chown=builder:builder --from=sources /src/ /home/builder/c-toxcore/ + +# Pre-create build directory +RUN mkdir -p build codeql-db && chown builder:builder codeql-db build + +# Copy scripts +COPY --chown=builder:builder other/docker/codeql/build.sh . +COPY --chown=builder:builder other/docker/codeql/run-analysis.sh . + +RUN chmod +x build.sh run-analysis.sh + +USER builder + +# Download standard queries as builder +RUN codeql pack download codeql/cpp-queries + +CMD ["./run-analysis.sh"] diff --git a/other/docker/codeql/run b/other/docker/codeql/run new file mode 100755 index 00000000..c081b1c7 --- /dev/null +++ b/other/docker/codeql/run @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -eux + +BUILD=codeql + +# Ensure the sources image is built +other/docker/sources/build.sh + +# Build the codeql image +docker build -t "toxchat/c-toxcore:$BUILD" -f "other/docker/$BUILD/$BUILD.Dockerfile" . + +# Run the container +echo "Running CodeQL analysis..." +docker run --rm "toxchat/c-toxcore:$BUILD" diff --git a/other/docker/codeql/run-analysis.sh b/other/docker/codeql/run-analysis.sh new file mode 100644 index 00000000..77d62236 --- /dev/null +++ b/other/docker/codeql/run-analysis.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e +echo "Creating CodeQL Database..." +codeql database create codeql-db --language=cpp --overwrite --command="./build.sh" +echo "Analyzing..." +codeql database analyze codeql-db codeql/cpp-queries:codeql-suites/cpp-security-and-quality.qls --format=csv --output=codeql-db/results.csv +echo "Analysis complete. Results in codeql-db/results.csv" +cat codeql-db/results.csv diff --git a/other/docker/goblint/BUILD.bazel b/other/docker/goblint/BUILD.bazel deleted file mode 100644 index afd687a3..00000000 --- a/other/docker/goblint/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@rules_cc//cc:defs.bzl", "cc_library") - -cc_library( - name = "sodium", - testonly = True, - srcs = ["sodium.c"], - deps = ["@libsodium"], -) diff --git a/other/docker/goblint/analysis.json b/other/docker/goblint/analysis.json deleted file mode 100644 index a682b89b..00000000 --- a/other/docker/goblint/analysis.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "ana": { - "activated": [ - "base","mallocWrapper","escape","mutex","mutexEvents","access","assert","expRelation" - ], - "arrayoob": true, - "wp": true, - "apron": { - "strengthening": true - }, - "base": { - "structs" : { - "domain" : "combined-sk" - }, - "arrays": { - "domain": "partitioned" - } - }, - "malloc": { - "wrappers": [ - "mem_balloc", - "mem_alloc", - "mem_valloc", - "mem_vrealloc" - ] - } - }, - "warn": { - "behavior": false, - "call": false, - "integer": true, - "float": false, - "race": false, - "deadcode": false, - "unsound": false, - "imprecise": false, - "success": false, - "unknown": false - }, - "exp": { - "earlyglobs": true - } -} diff --git a/other/docker/goblint/goblint.Dockerfile b/other/docker/goblint/goblint.Dockerfile deleted file mode 100644 index 90941525..00000000 --- a/other/docker/goblint/goblint.Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM toxchat/c-toxcore:sources AS sources -FROM ghcr.io/goblint/analyzer:2.5.0 - -RUN apt-get update && \ - DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends \ - libsodium-dev \ - tcc \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /work -COPY --from=sources /src/ /work/ - -COPY other/deploy/single-file/make_single_file /work/ - -RUN ./make_single_file -core auto_tests/tox_new_test.c other/docker/goblint/sodium.c > analysis.c -# Try compiling+linking just to make sure we have all the fake functions. -RUN tcc analysis.c - -COPY other/docker/goblint/analysis.json /work/other/docker/goblint/ -RUN /opt/goblint/analyzer/bin/goblint --conf /work/other/docker/goblint/analysis.json analysis.c diff --git a/other/docker/goblint/run b/other/docker/goblint/run deleted file mode 100755 index 8c97cb51..00000000 --- a/other/docker/goblint/run +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -set -eux -BUILD=goblint -other/docker/sources/build.sh -docker build -t "toxchat/c-toxcore:$BUILD" -f "other/docker/$BUILD/$BUILD.Dockerfile" . diff --git a/other/docker/goblint/sodium.c b/other/docker/goblint/sodium.c deleted file mode 100644 index c0c45d48..00000000 --- a/other/docker/goblint/sodium.c +++ /dev/null @@ -1,123 +0,0 @@ -#include - -#include - -int crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk, const unsigned char *seed) -{ - memset(pk, 0, 32); - memset(sk, 0, 32); - return 0; -} -int crypto_sign_ed25519_pk_to_curve25519(unsigned char *curve25519_pk, - const unsigned char *ed25519_pk) -{ - memset(curve25519_pk, 0, 32); - return 0; -} -int crypto_sign_ed25519_sk_to_curve25519(unsigned char *curve25519_sk, - const unsigned char *ed25519_sk) -{ - memset(curve25519_sk, 0, 32); - return 0; -} -void sodium_memzero(void *const pnt, const size_t len) -{ - memset(pnt, 0, len); -} -int sodium_mlock(void *const addr, const size_t len) -{ - return 0; -} -int sodium_munlock(void *const addr, const size_t len) -{ - return 0; -} -int crypto_verify_32(const unsigned char *x, const unsigned char *y) -{ - return memcmp(x, y, 32); -} -int crypto_verify_64(const unsigned char *x, const unsigned char *y) -{ - return memcmp(x, y, 64); -} -int crypto_sign_detached(unsigned char *sig, unsigned long long *siglen_p, - const unsigned char *m, unsigned long long mlen, - const unsigned char *sk) -{ - return 0; -} -int crypto_sign_verify_detached(const unsigned char *sig, - const unsigned char *m, - unsigned long long mlen, - const unsigned char *pk) -{ - return 0; -} -int crypto_box_beforenm(unsigned char *k, const unsigned char *pk, - const unsigned char *sk) -{ - memset(k, 0, 32); - return 0; -} -int crypto_box_afternm(unsigned char *c, const unsigned char *m, - unsigned long long mlen, const unsigned char *n, - const unsigned char *k) -{ - memset(c, 0, 32); - return 0; -} -int crypto_box_open_afternm(unsigned char *m, const unsigned char *c, - unsigned long long clen, const unsigned char *n, - const unsigned char *k) -{ - return 0; -} -int crypto_scalarmult_curve25519_base(unsigned char *q, - const unsigned char *n) -{ - memset(q, 0, 32); - return 0; -} -int crypto_auth(unsigned char *out, const unsigned char *in, - unsigned long long inlen, const unsigned char *k) -{ - return 0; -} -int crypto_auth_verify(const unsigned char *h, const unsigned char *in, - unsigned long long inlen, const unsigned char *k) -{ - return 0; -} -int crypto_hash_sha256(unsigned char *out, const unsigned char *in, - unsigned long long inlen) -{ - return 0; -} -int crypto_hash_sha512(unsigned char *out, const unsigned char *in, - unsigned long long inlen) -{ - return 0; -} -int crypto_pwhash_scryptsalsa208sha256(unsigned char *const out, - unsigned long long outlen, - const char *const passwd, - unsigned long long passwdlen, - const unsigned char *const salt, - unsigned long long opslimit, - size_t memlimit) -{ - memset(out, 0, outlen); - return 0; -} -void randombytes(unsigned char *const buf, const unsigned long long buf_len) -{ - memset(buf, 0, buf_len); -} -uint32_t randombytes_uniform(const uint32_t upper_bound) -{ - return upper_bound; -} -int sodium_init(void) -{ - return 0; -} diff --git a/other/docker/modules/check b/other/docker/modules/check index 8cbaa87a..4ea4c137 100755 --- a/other/docker/modules/check +++ b/other/docker/modules/check @@ -6,92 +6,79 @@ import sys from dataclasses import dataclass from dataclasses import field from typing import Any -from typing import Dict -from typing import List -STD_MODULE = """module std [system] { - textual header "/usr/include/alloca.h" - textual header "/usr/include/assert.h" - textual header "/usr/include/c++/14.2.0/algorithm" - textual header "/usr/include/c++/14.2.0/array" - textual header "/usr/include/c++/14.2.0/atomic" - textual header "/usr/include/c++/14.2.0/cassert" - textual header "/usr/include/c++/14.2.0/cerrno" - textual header "/usr/include/c++/14.2.0/chrono" - textual header "/usr/include/c++/14.2.0/climits" - textual header "/usr/include/c++/14.2.0/compare" - textual header "/usr/include/c++/14.2.0/cstddef" - textual header "/usr/include/c++/14.2.0/cstdint" - textual header "/usr/include/c++/14.2.0/cstdio" - textual header "/usr/include/c++/14.2.0/cstdlib" - textual header "/usr/include/c++/14.2.0/cstring" - textual header "/usr/include/c++/14.2.0/deque" - textual header "/usr/include/c++/14.2.0/functional" - textual header "/usr/include/c++/14.2.0/iomanip" - textual header "/usr/include/c++/14.2.0/iosfwd" - textual header "/usr/include/c++/14.2.0/iostream" - textual header "/usr/include/c++/14.2.0/limits" - textual header "/usr/include/c++/14.2.0/map" - textual header "/usr/include/c++/14.2.0/memory" - textual header "/usr/include/c++/14.2.0/mutex" - textual header "/usr/include/c++/14.2.0/new" - textual header "/usr/include/c++/14.2.0/optional" - textual header "/usr/include/c++/14.2.0/ostream" - textual header "/usr/include/c++/14.2.0/queue" - textual header "/usr/include/c++/14.2.0/random" - textual header "/usr/include/c++/14.2.0/stdlib.h" - textual header "/usr/include/c++/14.2.0/string" - textual header "/usr/include/c++/14.2.0/thread" - textual header "/usr/include/c++/14.2.0/type_traits" - textual header "/usr/include/c++/14.2.0/vector" - textual header "/usr/include/errno.h" - textual header "/usr/include/fortify/stdio.h" - textual header "/usr/include/fortify/string.h" - textual header "/usr/include/fortify/unistd.h" - textual header "/usr/include/limits.h" - textual header "/usr/include/stdarg.h" - textual header "/usr/include/stdbool.h" - textual header "/usr/include/stddef.h" - textual header "/usr/include/stdint.h" - textual header "/usr/include/sys/time.h" - textual header "/usr/include/sys/types.h" - textual header "/usr/include/time.h" +# We no longer define 'std' or 'musl' manually. +# We rely on -fimplicit-module-maps to find the system-provided ones. +# We only define modules for our project and third-party libraries. +EXTRA_MODULES = """ +module "_system" [system] { + header "/usr/include/stdint.h" + header "/usr/include/stdbool.h" + header "/usr/include/stddef.h" + header "/usr/include/stdio.h" + header "/usr/include/stdlib.h" + header "/usr/include/string.h" + header "/usr/include/inttypes.h" + header "/usr/include/limits.h" + header "/usr/include/errno.h" + header "/usr/include/unistd.h" + header "/usr/include/fcntl.h" + header "/usr/include/time.h" + header "/usr/include/assert.h" + header "/usr/include/pthread.h" + header "/usr/include/arpa/inet.h" + header "/usr/include/netdb.h" + header "/usr/include/netinet/in.h" + header "/usr/include/sys/types.h" + header "/usr/include/sys/stat.h" + header "/usr/include/sys/time.h" + header "/usr/include/sys/socket.h" + header "/usr/include/sys/epoll.h" + header "/usr/include/sys/ioctl.h" + header "/usr/include/sys/resource.h" + header "/usr/include/sys/uio.h" + header "/usr/include/sys/mman.h" + header "/usr/include/sys/wait.h" + header "/usr/include/sys/select.h" + header "/usr/include/poll.h" + header "/usr/include/sched.h" + header "/usr/include/signal.h" + header "/usr/include/ctype.h" + header "/usr/include/alloca.h" + header "/usr/include/malloc.h" + header "/usr/include/dirent.h" + export * } -module "c_toxcore_third_party_cmp" { - header "third_party/cmp/cmp.h" - use std +module "_libsodium" [system] { + header "/usr/include/sodium.h" + use _system + export * } -module "c_toxcore_toxencryptsave_defines" { - header "toxencryptsave/defines.h" +module "_benchmark" [system] { + header "/usr/include/benchmark/benchmark.h" + use _system + export * } -module "_benchmark" { - textual header "/usr/include/benchmark/benchmark.h" - use std +module "_com_google_googletest___gtest" [system] { + header "/usr/include/gtest/gtest.h" + header "/usr/include/gmock/gmock.h" + use _system + export * } -module "_com_google_googletest___gtest" { - textual header "/usr/include/gmock/gmock.h" - textual header "/usr/include/gtest/gtest.h" - use std +module "_com_google_googletest___gtest_main" [system] { + use _com_google_googletest___gtest + export * } -module "_com_google_googletest___gtest_main" { - // Dummy module for gtest_main, assuming it links but headers are in gtest - use "_com_google_googletest___gtest" +module "_opus" [system] { + header "/usr/include/opus/opus.h" + use _system + export * } -module "_libsodium" { - textual header "/usr/include/sodium.h" -} -module "_pthread" { - textual header "/usr/include/pthread.h" -} -module "_psocket" { - textual header "/usr/include/arpa/inet.h" - textual header "/usr/include/fcntl.h" - textual header "/usr/include/fortify/sys/socket.h" - textual header "/usr/include/linux/if.h" - textual header "/usr/include/netdb.h" - textual header "/usr/include/netinet/in.h" - textual header "/usr/include/sys/epoll.h" - textual header "/usr/include/sys/ioctl.h" +module "_libvpx" [system] { + header "/usr/include/vpx/vpx_encoder.h" + header "/usr/include/vpx/vpx_decoder.h" + use _system + export * } """ @@ -100,24 +87,16 @@ module "_psocket" { class Target: name: str package: str - srcs: List[str] = field(default_factory=list) - hdrs: List[str] = field(default_factory=list) - deps: List[str] = field(default_factory=list) + srcs: list[str] = field(default_factory=list) + hdrs: list[str] = field(default_factory=list) + deps: list[str] = field(default_factory=list) @property def label(self) -> str: - # Sanitize label for module name - sanitized = (f"//c-toxcore/{self.package}:{self.name}".replace( - "/", "_").replace(":", - "_").replace(".", - "_").replace("-", - "_").replace("@", "_")) - if sanitized.startswith("__"): - sanitized = sanitized[2:] - return sanitized + return f"c-toxcore/{self.package}:{self.name}" -TARGETS: List[Target] = [] +TARGETS: list[Target] = [] class BuildContext: @@ -140,16 +119,29 @@ class BuildContext: def bzl_cc_fuzz_test(self, *args: Any, **kwargs: Any) -> None: pass - def bzl_select(self, selector: Dict[str, List[str]]) -> List[str]: + def bzl_select(self, selector: dict[str, list[str]]) -> list[str]: return selector.get("//tools/config:linux", selector.get("//conditions:default", [])) - def bzl_glob(self, include: List[str]) -> List[str]: + def bzl_glob(self, + include: list[str], + exclude: list[str] | None = None, + **kwargs: Any) -> list[str]: results = [] for pattern in include: full_pattern = os.path.join(self.package, pattern) - files = glob.glob(full_pattern) + files = glob.glob(full_pattern, recursive=True) results.extend([os.path.relpath(f, self.package) for f in files]) + + if exclude: + excluded_files = set() + for pattern in exclude: + full_pattern = os.path.join(self.package, pattern) + files = glob.glob(full_pattern, recursive=True) + excluded_files.update( + [os.path.relpath(f, self.package) for f in files]) + results = [f for f in results if f not in excluded_files] + return results def _add_target(self, name: str, srcs: Any, hdrs: Any, deps: Any) -> None: @@ -174,6 +166,9 @@ class BuildContext: **kwargs: Any) -> None: self._add_target(name, srcs, hdrs, deps) + def bzl_project(self, *args: Any, **kwargs: Any) -> None: + pass + def bzl_cc_test(self, name: str, srcs: Any = (), @@ -184,29 +179,44 @@ class BuildContext: def resolve_module_name(dep: str, current_pkg: str) -> str: - # Resolve to canonical label first - label = dep - if dep.startswith("@"): - label = dep - elif dep.startswith("//"): - if ":" in dep: - label = dep - else: - pkg_name = os.path.basename(dep) - label = f"{dep}:{pkg_name}" - elif dep.startswith(":"): - label = f"//c-toxcore/{current_pkg}{dep}" + if dep in ["@psocket", "@pthread"]: + return "_system" + if dep == "@libsodium": + return "_libsodium" + if dep == "@benchmark": + return "_benchmark" + if dep == "@com_google_googletest//:gtest": + return "_com_google_googletest___gtest" + if dep == "@com_google_googletest//:gtest_main": + return "_com_google_googletest___gtest_main" + if dep == "@opus": + return "_opus" + if dep == "@libvpx": + return "_libvpx" - # Sanitize - sanitized = (label.replace("/", "_").replace(":", "_").replace( - ".", "_").replace("-", "_").replace("@", "_")) - if sanitized.startswith("__"): - sanitized = sanitized[2:] - return sanitized + # Resolve to canonical label first + if dep.startswith("@"): + return dep + if dep.startswith("//"): + label = dep[2:] + if ":" in label: + return label + pkg_name = os.path.basename(label) + return f"{label}:{pkg_name}" + if dep.startswith(":"): + return f"c-toxcore/{current_pkg}{dep}" + + return dep def main() -> None: - packages = ["toxcore", "testing/support"] + packages = [] + for root, dirs, files in os.walk("."): + if "BUILD.bazel" in files: + pkg = os.path.relpath(root, ".") + if pkg == ".": + pkg = "" + packages.append(pkg) for pkg in packages: ctx = BuildContext(pkg) @@ -214,39 +224,53 @@ def main() -> None: if not os.path.exists(build_file): continue + # Use a defaultdict to handle any unknown functions as no-ops + from collections import defaultdict + + env: dict[str, Any] = defaultdict(lambda: lambda *args, **kwargs: None) + env.update({ + "load": ctx.bzl_load, + "exports_files": ctx.bzl_exports_files, + "cc_library": ctx.bzl_cc_library, + "no_undefined_cc_library": ctx.bzl_cc_library, + "cc_binary": ctx.bzl_cc_binary, + "cc_test": ctx.bzl_cc_test, + "cc_fuzz_test": ctx.bzl_cc_fuzz_test, + "select": ctx.bzl_select, + "glob": ctx.bzl_glob, + "alias": ctx.bzl_alias, + "sh_library": ctx.bzl_sh_library, + "project": ctx.bzl_project, + }) + with open(build_file, "r") as f: - exec( - f.read(), - { - "load": ctx.bzl_load, - "exports_files": ctx.bzl_exports_files, - "cc_library": ctx.bzl_cc_library, - "cc_binary": ctx.bzl_cc_binary, - "cc_test": ctx.bzl_cc_test, - "cc_fuzz_test": ctx.bzl_cc_fuzz_test, - "select": ctx.bzl_select, - "glob": ctx.bzl_glob, - "alias": ctx.bzl_alias, - "sh_library": ctx.bzl_sh_library, - }, - ) + exec(f.read(), env) with open("module.modulemap", "w") as f: - f.write(STD_MODULE) + f.write(EXTRA_MODULES) for t in TARGETS: f.write(f'module "{t.label}" {{\n') for hdr in t.hdrs: + # Proper modular header f.write(f' header "{os.path.join(t.package, hdr)}"\n') - f.write(" use std\n") + + # Use all dependencies for dep in t.deps: mod_name = resolve_module_name(dep, t.package) - # Export all dependencies to ensure visibility of types used in headers - f.write(f" use {mod_name}\n") - f.write(f" export {mod_name}\n") + f.write(f' use "{mod_name}"\n') + + # Re-export everything we use to match standard C++ transitive include behavior. + f.write(" export *\n") + + # Basic system modules used everywhere + f.write(' use "_system"\n') + f.write("}\n") - # with open("module.modulemap", "r") as f: - # print(f.read(), file=sys.stderr) + if "--print-modulemap" in sys.argv: + with open("module.modulemap", "r") as f: + print(f.read()) + return src_to_module = {} for t in TARGETS: @@ -254,29 +278,44 @@ def main() -> None: full_src = os.path.join(t.package, src) src_to_module[full_src] = t.label - # Sort for deterministic output all_srcs = sorted(src_to_module.keys()) + all_srcs = [src for src in all_srcs if src.endswith(".c")] + os.makedirs("/tmp/clang-modules", exist_ok=True) for src in all_srcs: print(f"Validating {src}", file=sys.stderr) module_name = src_to_module[src] + lang = "-xc" if src.endswith(".c") else "-xc++" + std = "-std=c11" if src.endswith(".c") else "-std=c++23" + subprocess.run( [ "clang", "-fsyntax-only", - "-xc++", + lang, + "-stdlib=libc++" if lang == "-xc++" else "-nostdinc++", "-Wall", "-Werror", "-Wno-missing-braces", "-DTCP_SERVER_USE_EPOLL", - "-std=c++23", + "-D_XOPEN_SOURCE=600", + "-D_GNU_SOURCE", + std, "-fdiagnostics-color=always", "-fmodules", - "-fmodules-strict-decluse", + "-Xclang", + "-fmodules-local-submodule-visibility", + "-fmodules-decluse", + "-Xclang", + "-fno-modules-error-recovery", + "-fno-implicit-module-maps", + "-fno-builtin-module-map", + "-fmodules-cache-path=/tmp/clang-modules", "-fmodule-map-file=module.modulemap", f"-fmodule-name={module_name}", "-I.", + "-I/usr/include/opus", src, ], check=True, diff --git a/other/docker/modules/modules.Dockerfile b/other/docker/modules/modules.Dockerfile index 8bc8af5c..a056dd9b 100644 --- a/other/docker/modules/modules.Dockerfile +++ b/other/docker/modules/modules.Dockerfile @@ -1,10 +1,13 @@ -FROM alpine:3.21.0 +FROM alpine:3.23.0 RUN ["apk", "add", "--no-cache", \ "bash", \ "benchmark-dev", \ "clang", \ + "gmock", \ "gtest-dev", \ + "libc++-dev", \ + "libc++-static", \ "libconfig-dev", \ "libsodium-dev", \ "libvpx-dev", \ diff --git a/other/docker/windows/build_dependencies.sh b/other/docker/windows/build_dependencies.sh index fa2353c9..e6996eae 100755 --- a/other/docker/windows/build_dependencies.sh +++ b/other/docker/windows/build_dependencies.sh @@ -114,6 +114,21 @@ build() { make install cd .. + echo + echo "=== Building GTest $VERSION_GTEST $ARCH ===" + curl "${CURL_OPTIONS[@]}" "https://github.com/google/googletest/archive/refs/tags/v$VERSION_GTEST.tar.gz" -o "googletest-$VERSION_GTEST.tar.gz" + check_sha256 "65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c" "googletest-$VERSION_GTEST.tar.gz" + tar -xf "googletest-$VERSION_GTEST.tar.gz" + cd "googletest-$VERSION_GTEST" + cmake \ + -DCMAKE_TOOLCHAIN_FILE=../windows_toolchain.cmake \ + -DCMAKE_INSTALL_PREFIX="$PREFIX_DIR" \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -S . -B build + cmake --build build --target install --parallel "$(nproc)" + cd .. + rm -rf /tmp/* } diff --git a/other/docker/windows/dockerignore b/other/docker/windows/dockerignore new file mode 100644 index 00000000..013db440 --- /dev/null +++ b/other/docker/windows/dockerignore @@ -0,0 +1 @@ +!other/docker/windows/*.sh diff --git a/other/docker/windows/run b/other/docker/windows/run new file mode 100755 index 00000000..9c258cf7 --- /dev/null +++ b/other/docker/windows/run @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +DOCKERFLAGS=(--build-arg SUPPORT_TEST=true) + +. "$(cd "$(dirname "${BASH_SOURCE[0]}")/../sources" && pwd)/run.sh" + +# Create a temporary directory for the source to workaround potential bind mount issues +# (e.g. FUSE/SSHFS filesystems or Docker daemon visibility issues). +TEMP_SRC=$(mktemp -d -t toxcore-src-XXXXXX) +cleanup() { + rm -rf "$TEMP_SRC" +} +trap cleanup EXIT + +echo "Copying source to temporary directory $TEMP_SRC..." +# Exclude .git and build artifacts to speed up copy +rsync -a --exclude .git --exclude _build . "$TEMP_SRC/" + +docker run \ + -e ENABLE_TEST=true \ + -e EXTRA_CMAKE_FLAGS=-DUNITTEST=ON \ + -v "$TEMP_SRC:/toxcore" \ + -v /tmp/c-toxcore-build:/prefix \ + -t \ + --rm \ + toxchat/c-toxcore:windows diff --git a/other/docker/windows/Dockerfile b/other/docker/windows/windows.Dockerfile similarity index 80% rename from other/docker/windows/Dockerfile rename to other/docker/windows/windows.Dockerfile index 64a5b209..218e1f7a 100644 --- a/other/docker/windows/Dockerfile +++ b/other/docker/windows/windows.Dockerfile @@ -7,6 +7,7 @@ FROM debian:trixie-slim ARG VERSION_OPUS=1.4 \ VERSION_SODIUM=1.0.19 \ VERSION_VPX=1.14.0 \ + VERSION_GTEST=1.17.0 \ ENABLE_HASH_VERIFICATION=true \ \ SUPPORT_TEST=false \ @@ -21,14 +22,14 @@ ENV SUPPORT_TEST=${SUPPORT_TEST} \ CROSS_COMPILE=${CROSS_COMPILE} WORKDIR /work -COPY check_sha256.sh . -COPY get_packages.sh . +COPY other/docker/windows/check_sha256.sh . +COPY other/docker/windows/get_packages.sh . RUN ./get_packages.sh -COPY build_dependencies.sh . +COPY other/docker/windows/build_dependencies.sh . RUN ./build_dependencies.sh -COPY build_toxcore.sh . +COPY other/docker/windows/build_toxcore.sh . ENV ENABLE_TEST=false \ ALLOW_TEST_FAILURE=false \ diff --git a/other/docker/windows/windows.Dockerfile.dockerignore b/other/docker/windows/windows.Dockerfile.dockerignore new file mode 100644 index 00000000..8a8be10b --- /dev/null +++ b/other/docker/windows/windows.Dockerfile.dockerignore @@ -0,0 +1,24 @@ +# ===== common ===== +# Ignore everything ... +**/* +# ... except sources +!**/*.[ch] +!**/*.cc +!**/*.hh +!CHANGELOG.md +!LICENSE +!README.md +!auto_tests/data/* +!other/bootstrap_daemon/bash-completion/** +!other/bootstrap_daemon/tox-bootstrapd.* +!other/proxy/*.mod +!other/proxy/*.sum +!other/proxy/*.go +# ... and CMake build files (used by most builds). +!**/CMakeLists.txt +!.github/scripts/flags*.sh +!cmake/*.cmake +!other/pkgconfig/* +!other/rpm/* +!so.version +!other/docker/windows/*.sh diff --git a/other/event_tooling/generate_event_c.cpp b/other/event_tooling/generate_event_c.cpp index 476b4e6c..17e47943 100644 --- a/other/event_tooling/generate_event_c.cpp +++ b/other/event_tooling/generate_event_c.cpp @@ -4,13 +4,14 @@ // this file can be used to generate event.c files // requires c++17 +#include +#include #include #include -#include -#include -#include -#include #include +#include +#include +#include std::string str_tolower(std::string s) { std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::tolower(c); }); @@ -168,6 +169,7 @@ std::string zero_initializer_for_type(const std::string& type) { void generate_event_impl(const std::string& event_name, const std::vector& event_types) { const std::string event_name_l = str_tolower(event_name); + const std::string event_name_u = str_toupper(event_name); std::string file_name = output_folder + "/" + event_name_l + ".c"; std::ofstream f(file_name); @@ -218,11 +220,20 @@ void generate_event_impl(const std::string& event_name, const std::vector" << t.name << " = " << t.name << ";\n"; }, [&](const EventTypeByteRange& t) { - f << " if (" << event_name_l << "->" << t.name_data << " != nullptr) {\n"; - f << " mem_delete(mem, " << event_name_l << "->" << t.name_data << ");\n"; - f << " " << event_name_l << "->" << t.name_data << " = nullptr;\n"; - f << " " << event_name_l << "->" << t.name_length << " = 0;\n"; - f << " }\n\n"; - f << " if (" << t.name_data << " == nullptr) {\n"; - f << " assert(" << t.name_length << " == 0);\n"; - f << " return true;\n }\n\n"; + f << " if (" << event_name_l << "->" << t.name_data << " != nullptr) {\n" + " mem_delete(mem, " << event_name_l << "->" << t.name_data << ");\n" + " " << event_name_l << "->" << t.name_data << " = nullptr;\n" + " " << event_name_l << "->" << t.name_length << " = 0;\n" + " }\n\n" + " if (" << t.name_data << " == nullptr) {\n" + " assert(" << t.name_length << " == 0);\n" + " return true;\n" + " }\n\n"; + if (t.null_terminated) { - f << " uint8_t *" << t.name_data << "_copy = (uint8_t *)mem_balloc(mem, " << t.name_length << " + 1);\n\n"; + f << " if (" << t.name_length << " == UINT32_MAX) {\n" + " return false;\n" + " }\n\n"; } else { - f << " uint8_t *" << t.name_data << "_copy = (uint8_t *)mem_balloc(mem, " << t.name_length << ");\n\n"; + f << " if (" << t.name_length << " == 0) {\n" + " " << event_name_l << "->" << t.name_data << " = nullptr;\n" + " " << event_name_l << "->" << t.name_length << " = 0;\n" + " return true;\n" + " }\n\n"; } - f << " if (" << t.name_data << "_copy == nullptr) {\n"; - f << " return false;\n }\n\n"; - f << " memcpy(" << t.name_data << "_copy, " << t.name_data << ", " << t.name_length << ");\n"; + + f << " " << t.type_c_arg << " *" << t.name_data << "_copy = (" << t.type_c_arg << " *)mem_balloc(mem, " << t.name_length << (t.null_terminated ? " + 1" : "") << ");\n\n" + " if (" << t.name_data << "_copy == nullptr) {\n" + " return false;\n" + " }\n\n" + " memcpy(" << t.name_data << "_copy, " << t.name_data << ", " << t.name_length << ");\n"; + if (t.null_terminated) { f << " " << t.name_data << "_copy[" << t.name_length << "] = 0;\n"; } - f << " " << event_name_l << "->" << t.name_data << " = " << t.name_data << "_copy;\n"; - f << " " << event_name_l << "->" << t.name_length << " = " << t.name_length << ";\n"; - f << " return true;\n"; + + f << " " << event_name_l << "->" << t.name_data << " = " << t.name_data << "_copy;\n" + " " << event_name_l << "->" << t.name_length << " = " << t.name_length << ";\n" + " return true;\n"; }, [&](const EventTypeByteArray& t) { f << " memcpy(" << event_name_l << "->" << t.name << ", " << t.name << ", " << t.length_constant << ");\n"; @@ -342,7 +366,7 @@ void generate_event_impl(const std::string& event_name, const std::vector" << t.name_length << ";\n}\n"; - f << "const uint8_t *tox_event_" << event_name_l << "_get_" << t.name_data; + f << "const " << t.type_c_arg << " *tox_event_" << event_name_l << "_get_" << t.name_data; f << "(const Tox_Event_" << event_name << " *" << event_name_l << ")\n"; f << "{\n assert(" << event_name_l << " != nullptr);\n"; f << " return " << event_name_l << "->" << t.name_data << ";\n}\n\n"; @@ -435,7 +459,11 @@ void generate_event_impl(const std::string& event_name, const std::vector" << t.name_data << ", event->" << t.name_length << ")"; + if (t.type_c_arg == "char") { + f << "bin_pack_str(bp, event->" << t.name_data << ", event->" << t.name_length << ")"; + } else { + f << "bin_pack_bin(bp, event->" << t.name_data << ", event->" << t.name_length << ")"; + } }, [&](const EventTypeByteArray& t) { f << "bin_pack_bin(bp, event->" << t.name << ", " << t.length_constant << ")"; @@ -471,7 +499,11 @@ void generate_event_impl(const std::string& event_name, const std::vector" << t.name_data << ", &event->" << t.name_length << ")"; + if (t.type_c_arg == "char") { + f << "bin_unpack_str(bu, &event->" << t.name_data << ", &event->" << t.name_length << ")"; + } else { + f << "bin_unpack_bin(bu, &event->" << t.name_data << ", &event->" << t.name_length << ")"; + } }, [&](const EventTypeByteArray& t) { f << "bin_unpack_bin_fixed(bu, event->" << t.name << ", " << t.length_constant << ")"; @@ -493,7 +525,7 @@ void generate_event_impl(const std::string& event_name, const std::vectortype == TOX_EVENT_" << str_toupper(event_name) << " ? event->data." << event_name_l << " : nullptr;\n}\n\n"; + f << " return event->type == TOX_EVENT_" << event_name_u << " ? event->data." << event_name_l << " : nullptr;\n}\n\n"; // new f << "Tox_Event_" << event_name << " *tox_event_" << event_name_l << "_new(const Memory *mem)\n{\n"; @@ -506,7 +538,7 @@ void generate_event_impl(const std::string& event_name, const std::vectormem, "; - if (t.type_c_arg != "uint8_t") { - f << "(const uint8_t *)"; - } - f << t.name_data << ", " << t.name_length_cb << ");\n"; + f << " if (!tox_event_" << event_name_l << "_set_" << t.name_data << "(" << event_name_l << ", state->mem, "; + f << t.name_data << ", " << t.name_length_cb << ")) {\n"; + f << " state->error = TOX_ERR_EVENTS_ITERATE_MALLOC;\n"; + f << " }\n"; }, [&](const EventTypeByteArray& t) { f << " tox_event_" << event_name_l << "_set_" << t.name << "(" << event_name_l << ", " << t.name << ");\n"; @@ -595,6 +627,45 @@ void generate_event_impl(const std::string& event_name, const std::vectordata_length == 0 || tox->" << event_name_l << "_callback_per_pktid[event->data[0]] == nullptr) {\n"; + f << " return;\n"; + f << " }\n\n"; + f << " tox_unlock(tox);\n"; + f << " tox->" << event_name_l << "_callback_per_pktid[event->data[0]](tox, event->friend_number, event->data, event->data_length, user_data);\n"; + f << " tox_lock(tox);\n"; + } else { + f << " if (tox->" << event_name_l << "_callback == nullptr) {\n return;\n }\n\n"; + f << " tox_unlock(tox);\n"; + f << " tox->" << event_name_l << "_callback(tox, "; + bool first_arg = true; + for (const auto& t : event_types) { + if (!first_arg) f << ", "; + std::visit( + overloaded{ + [&](const EventTypeTrivial& t) { + f << "event->" << t.name; + }, + [&](const EventTypeByteRange& t) { + if (t.type_c_arg != "uint8_t") { + f << "(const " << t.type_c_arg << " *)"; + } + f << "event->" << t.name_data << ", event->" << t.name_length; + }, + [&](const EventTypeByteArray& t) { + f << "event->" << t.name; + } + }, + t + ); + first_arg = false; + } + f << ", user_data);\n"; + f << " tox_lock(tox);\n"; + } + f << "}\n"; } // c++ generate_event_c.cpp -std=c++17 && ./a.out Friend_Lossy_Packet && diff --color ../../toxcore/events/friend_lossy_packet.c out/friend_lossy_packet.c diff --git a/other/windows_build_script_toxcore.sh b/other/windows_build_script_toxcore.sh index 1f006451..211c78b1 100644 --- a/other/windows_build_script_toxcore.sh +++ b/other/windows_build_script_toxcore.sh @@ -1,6 +1,6 @@ #!/bin/sh -# When editing, make sure to update /other/docker/windows/Dockerfile and +# When editing, make sure to update /other/docker/windows/windows.Dockerfile and # INSTALL.md to match. export VERSION_OPUS="1.4" diff --git a/testing/BUILD.bazel b/testing/BUILD.bazel index b247f45e..c1daebbc 100644 --- a/testing/BUILD.bazel +++ b/testing/BUILD.bazel @@ -1,4 +1,5 @@ load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") +load("//hs-tokstyle/tools:tokstyle.bzl", "tokstyle_c_test") CIMPLE_FILES = [ "//c-toxcore/toxav:cimple_files", @@ -9,13 +10,16 @@ CIMPLE_FILES = [ sh_test( name = "cimple_test", - size = "small", + size = "medium", srcs = ["//hs-tokstyle/tools:check-cimple"], args = ["$(locations %s)" % f for f in CIMPLE_FILES] + [ "-Wno-boolean-return", "-Wno-callback-names", "-Wno-enum-from-int", + "-Wno-nullability", + "-Wno-ownership-decls", "-Wno-tagged-union", + "-Wno-type-check", "+RTS", "-N4", "-RTS", @@ -27,37 +31,30 @@ sh_test( ], ) -sh_test( +tokstyle_c_test( name = "c_test", - size = "small", - srcs = ["//hs-tokstyle/tools:check-c"], + size = "medium", + srcs = CIMPLE_FILES, args = [ - "--cc=$(CC)", - "-Iexternal/libsodium/include", - "-Iexternal/libvpx", - "-Iexternal/opus/include", - "-Ihs-tokstyle/include", - ] + ["$(locations %s)" % f for f in CIMPLE_FILES] + [ - "+RTS", - "-N4", - "-RTS", - ], - data = CIMPLE_FILES + [ - "//hs-tokstyle:headers", - "@libsodium//:headers", - "@libvpx//:headers", - "@opus//:headers", + "-Wno-borrow-check", + "-Wno-callback-discipline", + "-Wno-strict-typedef", ], tags = [ "haskell", "no-cross", ], - toolchains = ["@rules_cc//cc:current_cc_toolchain"], + deps = [ + "//hs-tokstyle:headers", + "@libsodium", + "@libvpx", + "@opus", + ], ) sh_test( name = "cimplefmt_test", - size = "small", + size = "medium", srcs = ["//hs-cimple/tools:cimplefmt"], args = ["--reparse"] + ["$(locations %s)" % f for f in CIMPLE_FILES], data = CIMPLE_FILES, diff --git a/testing/CMakeLists.txt b/testing/CMakeLists.txt index 11aaea0c..25028113 100644 --- a/testing/CMakeLists.txt +++ b/testing/CMakeLists.txt @@ -9,11 +9,11 @@ else() target_link_libraries(misc_tools PRIVATE toxcore_shared) endif() if(TARGET pthreads4w::pthreads4w) - target_link_libraries(misc_tools PRIVATE pthreads4w::pthreads4w) + target_link_libraries(misc_tools PUBLIC pthreads4w::pthreads4w) elseif(TARGET PThreads4W::PThreads4W) - target_link_libraries(misc_tools PRIVATE PThreads4W::PThreads4W) + target_link_libraries(misc_tools PUBLIC PThreads4W::PThreads4W) elseif(TARGET Threads::Threads) - target_link_libraries(misc_tools PRIVATE Threads::Threads) + target_link_libraries(misc_tools PUBLIC Threads::Threads) endif() ################################################################################ @@ -30,13 +30,7 @@ if(BUILD_MISC_TESTS) else() target_link_libraries(Messenger_test PRIVATE toxcore_shared) endif() - if(TARGET pthreads4w::pthreads4w) - target_link_libraries(Messenger_test PRIVATE pthreads4w::pthreads4w) - elseif(TARGET PThreads4W::PThreads4W) - target_link_libraries(Messenger_test PRIVATE PThreads4W::PThreads4W) - elseif(TARGET Threads::Threads) - target_link_libraries(Messenger_test PRIVATE Threads::Threads) - endif() endif() +add_subdirectory(bench) add_subdirectory(support) diff --git a/testing/Messenger_test.c b/testing/Messenger_test.c index d0ba5690..58bccfa7 100644 --- a/testing/Messenger_test.c +++ b/testing/Messenger_test.c @@ -102,14 +102,27 @@ int main(int argc, char *argv[]) exit(0); } - Messenger_Options options = {0}; + Messenger_Options options = {nullptr}; options.ipv6enabled = ipv6enabled; + + Logger *logger = logger_new(mem); + + if (logger == nullptr) { + fputs("Failed to allocate logger datastructure\n", stderr); + mono_time_free(mem, mono_time); + exit(1); + } + + options.log = logger; + Messenger_Error err; m = new_messenger(mono_time, mem, os_random(), os_network(), &options, &err); if (!m) { fprintf(stderr, "Failed to allocate messenger datastructure: %u\n", err); - exit(0); + logger_kill(logger); + mono_time_free(mem, mono_time); + exit(1); } if (argc == argvoffset + 4) { @@ -117,6 +130,9 @@ int main(int argc, char *argv[]) if (port_conv <= 0 || port_conv > UINT16_MAX) { printf("Failed to convert \"%s\" into a valid port. Exiting...\n", argv[argvoffset + 2]); + kill_messenger(m); + logger_kill(logger); + mono_time_free(mem, mono_time); exit(1); } @@ -131,6 +147,9 @@ int main(int argc, char *argv[]) if (!res) { printf("Failed to convert \"%s\" into an IP address. Exiting...\n", argv[argvoffset + 1]); + kill_messenger(m); + logger_kill(logger); + mono_time_free(mem, mono_time); exit(1); } } @@ -157,6 +176,9 @@ int main(int argc, char *argv[]) printf("\nEnter the address of the friend you wish to add (38 bytes HEX format):\n"); if (!fgets(temp_hex_id, sizeof(temp_hex_id), stdin)) { + kill_messenger(m); + logger_kill(logger); + mono_time_free(mem, mono_time); exit(0); } @@ -186,6 +208,8 @@ int main(int argc, char *argv[]) if (file == nullptr) { printf("Failed to open file %s\n", filename); kill_messenger(m); + logger_kill(logger); + mono_time_free(mem, mono_time); return 1; } @@ -195,6 +219,8 @@ int main(int argc, char *argv[]) fputs("Failed to allocate memory\n", stderr); fclose(file); kill_messenger(m); + logger_kill(logger); + mono_time_free(mem, mono_time); return 1; } @@ -205,6 +231,8 @@ int main(int argc, char *argv[]) free(buffer); fclose(file); kill_messenger(m); + logger_kill(logger); + mono_time_free(mem, mono_time); return 1; } diff --git a/testing/bench/BUILD.bazel b/testing/bench/BUILD.bazel new file mode 100644 index 00000000..2fda2b7f --- /dev/null +++ b/testing/bench/BUILD.bazel @@ -0,0 +1,24 @@ +load("@rules_cc//cc:defs.bzl", "cc_binary") + +cc_binary( + name = "tox_messenger_bench", + testonly = True, + srcs = ["tox_messenger_bench.cc"], + deps = [ + "//c-toxcore/testing/support", + "//c-toxcore/toxcore:network", + "//c-toxcore/toxcore:tox", + "@benchmark", + ], +) + +cc_binary( + name = "tox_friends_scaling_bench", + testonly = True, + srcs = ["tox_friends_scaling_bench.cc"], + deps = [ + "//c-toxcore/testing/support", + "//c-toxcore/toxcore:tox", + "@benchmark", + ], +) diff --git a/testing/bench/CMakeLists.txt b/testing/bench/CMakeLists.txt new file mode 100644 index 00000000..caa2be9c --- /dev/null +++ b/testing/bench/CMakeLists.txt @@ -0,0 +1,21 @@ +if(NOT UNITTEST) + return() +endif() + +find_package(benchmark QUIET) + +if(benchmark_FOUND) + add_executable(tox_messenger_bench tox_messenger_bench.cc) + target_link_libraries(tox_messenger_bench PRIVATE + toxcore_static + support + benchmark::benchmark + ) + + add_executable(tox_friends_scaling_bench tox_friends_scaling_bench.cc) + target_link_libraries(tox_friends_scaling_bench PRIVATE + toxcore_static + support + benchmark::benchmark + ) +endif() diff --git a/testing/bench/tox_friends_scaling_bench.cc b/testing/bench/tox_friends_scaling_bench.cc new file mode 100644 index 00000000..f0033e0d --- /dev/null +++ b/testing/bench/tox_friends_scaling_bench.cc @@ -0,0 +1,479 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#include + +#include +#include +#include + +#include "../../testing/support/public/simulation.hh" +#include "../../testing/support/public/tox_network.hh" +#include "../../toxcore/tox.h" + +namespace { + +using tox::test::ConnectedFriend; +using tox::test::setup_connected_friends; +using tox::test::SimulatedNode; +using tox::test::Simulation; + +// --- Helper Contexts --- + +struct GroupContext { + uint32_t peer_count = 0; + uint32_t group_number = UINT32_MAX; +}; + +// --- Fixtures --- + +class ToxIterateScalingFixture : public benchmark::Fixture { +public: + void SetUp(benchmark::State &state) override + { + // Explicitly clear members to handle fixture reuse or non-empty initial state. + // Order matters: dependent objects first. + friend_toxes.clear(); + friend_nodes.clear(); + main_tox.reset(); + main_node.reset(); + sim.reset(); + + int num_friends = state.range(0); + sim = std::make_unique(); + main_node = sim->create_node(); + main_tox = main_node->create_tox(); + + for (int i = 0; i < num_friends; ++i) { + auto node = sim->create_node(); + auto tox = node->create_tox(); + + uint8_t friend_pk[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_public_key(tox.get(), friend_pk); + + Tox_Err_Friend_Add err; + tox_friend_add_norequest(main_tox.get(), friend_pk, &err); + + friend_nodes.push_back(std::move(node)); + friend_toxes.push_back(std::move(tox)); + } + } + +protected: + std::unique_ptr sim; + std::unique_ptr main_node; + SimulatedNode::ToxPtr main_tox; + std::vector> friend_nodes; + std::vector friend_toxes; +}; + +// --- Contexts for Shared State Benchmarks --- + +class ToxOnlineDisconnectedScalingFixture : public benchmark::Fixture { + static constexpr bool kVerbose = false; + +public: + void SetUp(benchmark::State &state) override + { + // Explicitly clear members to handle fixture reuse or non-empty initial state. + // Order matters: dependent objects first (Tox depends on Node). + main_tox.reset(); + bootstrap_tox.reset(); + main_node.reset(); + bootstrap_node.reset(); + sim.reset(); + + int num_friends = state.range(0); + sim = std::make_unique(); + sim->net().set_latency(1); // Low latency to encourage traffic + sim->net().set_verbose(kVerbose); + + // Create a bootstrap node to ensure we are "online" on the DHT + bootstrap_node = sim->create_node(); + + auto log_cb = [](Tox *tox, Tox_Log_Level level, const char *file, uint32_t line, + const char *func, const char *message, void *user_data) { + if (kVerbose) { + std::cerr << "Log: " << file << ":" << line << " (" << func << ") " << message + << std::endl; + } + }; + + auto opts_bs = std::unique_ptr( + tox_options_new(nullptr), tox_options_free); + assert(opts_bs); + tox_options_set_local_discovery_enabled(opts_bs.get(), false); + tox_options_set_ipv6_enabled(opts_bs.get(), false); + tox_options_set_log_callback(opts_bs.get(), log_cb); + + bootstrap_tox = bootstrap_node->create_tox(opts_bs.get()); + uint8_t bootstrap_pk[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_dht_id(bootstrap_tox.get(), bootstrap_pk); + uint16_t bootstrap_port = bootstrap_node->get_primary_socket()->local_port(); + + main_node = sim->create_node(); + auto opts = std::unique_ptr( + tox_options_new(nullptr), tox_options_free); + assert(opts); + + // Disable local discovery to force DHT usage + tox_options_set_local_discovery_enabled(opts.get(), false); + tox_options_set_ipv6_enabled(opts.get(), false); + tox_options_set_log_callback(opts.get(), log_cb); + main_tox = main_node->create_tox(opts.get()); + + // Bootstrap to the network (Mutual bootstrap to ensure connectivity) + Ip_Ntoa bs_ip_str_buf; + const char *bs_ip_str = net_ip_ntoa(&bootstrap_node->ip, &bs_ip_str_buf); + + Tox_Err_Bootstrap bs_err; + tox_bootstrap(main_tox.get(), bs_ip_str, bootstrap_port, bootstrap_pk, &bs_err); + if (bs_err != TOX_ERR_BOOTSTRAP_OK) { + std::cerr << "bootstrapping failed: " << bs_err << "\n"; + std::abort(); + } + + // Run until we are connected to the DHT + sim->run_until( + [&]() { + tox_iterate(main_tox.get(), nullptr); + tox_iterate(bootstrap_tox.get(), nullptr); + return tox_self_get_connection_status(main_tox.get()) != TOX_CONNECTION_NONE; + }, + 15000); + + if (tox_self_get_connection_status(main_tox.get()) == TOX_CONNECTION_NONE) { + std::cerr << "WARNING: Failed to connect to DHT in SetUp (timeout 30s)\n"; + std::abort(); + } + + for (int i = 0; i < num_friends; ++i) { + // Add friend but don't create a node for them -> they are offline + uint8_t friend_pk[TOX_PUBLIC_KEY_SIZE]; + // Just generate a random PK + main_node->fake_random().bytes(friend_pk, TOX_PUBLIC_KEY_SIZE); + + Tox_Err_Friend_Add err; + tox_friend_add_norequest(main_tox.get(), friend_pk, &err); + } + } + +protected: + std::unique_ptr sim; + std::unique_ptr main_node; + SimulatedNode::ToxPtr main_tox; + std::unique_ptr bootstrap_node; + SimulatedNode::ToxPtr bootstrap_tox; +}; + +BENCHMARK_DEFINE_F(ToxOnlineDisconnectedScalingFixture, Iterate)(benchmark::State &state) +{ + if (tox_self_get_connection_status(main_tox.get()) == TOX_CONNECTION_NONE) { + state.SkipWithError("not connected to DHT"); + } + + for (auto _ : state) { + tox_iterate(main_tox.get(), nullptr); + tox_iterate(bootstrap_tox.get(), nullptr); + + uint32_t interval = tox_iteration_interval(main_tox.get()); + uint32_t interval_bs = tox_iteration_interval(bootstrap_tox.get()); + sim->advance_time(std::min(interval, interval_bs)); + } + + state.counters["mem_current"] + = benchmark::Counter(static_cast(main_node->fake_memory().current_allocation()), + benchmark::Counter::kDefaults, benchmark::Counter::OneK::kIs1024); +} +BENCHMARK_REGISTER_F(ToxOnlineDisconnectedScalingFixture, Iterate) + ->Arg(0) + ->Arg(10) + ->Arg(100) + ->Arg(1000) + ->Arg(2000); + +struct ConnectedContext { + std::unique_ptr sim; + std::unique_ptr main_node; + SimulatedNode::ToxPtr main_tox; + std::vector friends; + int num_friends = -1; + + void Setup(int n) + { + if (num_friends == n) + return; + + // Destruction order is critical + friends.clear(); + main_tox.reset(); + main_node.reset(); + sim.reset(); + + sim = std::make_unique(); + sim->net().set_latency(5); + main_node = sim->create_node(); + main_tox = main_node->create_tox(); + + num_friends = n; + if (n > 0) { + friends = setup_connected_friends(*sim, main_tox.get(), *main_node, num_friends); + } + } + + ~ConnectedContext(); +}; + +ConnectedContext::~ConnectedContext() = default; + +struct GroupScalingContext { + static constexpr bool verbose = false; + + std::unique_ptr sim; + std::unique_ptr main_node; + SimulatedNode::ToxPtr main_tox; + GroupContext main_ctx; + std::vector friends; + int num_peers = -1; + + void Setup(int peers) + { + if (num_peers == peers) + return; + + // Destruction order is critical + friends.clear(); + main_ctx = GroupContext(); + main_tox.reset(); + main_node.reset(); + sim.reset(); + + sim = std::make_unique(); + sim->net().set_latency(5); + main_node = sim->create_node(); + + auto opts = std::unique_ptr( + tox_options_new(nullptr), tox_options_free); + tox_options_set_ipv6_enabled(opts.get(), false); + tox_options_set_local_discovery_enabled(opts.get(), false); + main_tox = main_node->create_tox(opts.get()); + + num_peers = peers; + + // Setup Group Callbacks + tox_callback_group_peer_join( + main_tox.get(), [](Tox *, uint32_t, uint32_t, void *user_data) { + static_cast(user_data)->peer_count++; + }); + tox_callback_group_peer_exit(main_tox.get(), + [](Tox *, uint32_t, uint32_t, Tox_Group_Exit_Type, const uint8_t *, size_t, + const uint8_t *, size_t, + void *user_data) { static_cast(user_data)->peer_count--; }); + + Tox_Err_Group_New err_new; + main_ctx.group_number = tox_group_new(main_tox.get(), TOX_GROUP_PRIVACY_STATE_PUBLIC, + reinterpret_cast("test"), 4, reinterpret_cast("main"), + 4, &err_new); + + if (num_peers > 0) { + // Setup Friends + auto opts_friends = std::unique_ptr( + tox_options_new(nullptr), tox_options_free); + tox_options_set_ipv6_enabled(opts_friends.get(), false); + tox_options_set_local_discovery_enabled(opts_friends.get(), false); + + friends = setup_connected_friends( + *sim, main_tox.get(), *main_node, num_peers, opts_friends.get()); + + // Invite Friends + for (const auto &f : friends) { + tox_group_invite_friend( + main_tox.get(), main_ctx.group_number, f.friend_number, nullptr); + } + + // Wait for Joins + std::vector peer_group_numbers(num_peers, UINT32_MAX); + sim->run_until( + [&]() { + tox_iterate(main_tox.get(), &main_ctx); + + // Poll events + for (size_t i = 0; i < friends.size(); ++i) { + auto batches = friends[i].runner->poll_events(); + for (const auto &batch : batches) { + size_t size = tox_events_get_size(batch.get()); + for (size_t k = 0; k < size; ++k) { + const Tox_Event *e = tox_events_get(batch.get(), k); + if (tox_event_get_type(e) == TOX_EVENT_GROUP_INVITE) { + auto *ev = tox_event_get_group_invite(e); + uint32_t friend_number + = tox_event_group_invite_get_friend_number(ev); + const uint8_t *data + = tox_event_group_invite_get_invite_data(ev); + size_t len = tox_event_group_invite_get_invite_data_length(ev); + std::vector invite_data(data, data + len); + friends[i].runner->execute([=](Tox *tox) { + tox_group_invite_accept(tox, friend_number, + invite_data.data(), invite_data.size(), + reinterpret_cast("peer"), 4, nullptr, + 0, nullptr); + }); + } else if (tox_event_get_type(e) == TOX_EVENT_GROUP_SELF_JOIN) { + auto *ev = tox_event_get_group_self_join(e); + peer_group_numbers[i] + = tox_event_group_self_join_get_group_number(ev); + } + } + } + } + + bool all_joined = true; + for (auto gn : peer_group_numbers) + if (gn == UINT32_MAX) + all_joined = false; + return all_joined; + }, + 60000); + + // Wait for Convergence + sim->run_until( + [&]() { + tox_iterate(main_tox.get(), &main_ctx); + if (main_ctx.peer_count >= static_cast(num_peers)) + return true; + + static uint64_t last_print = 0; + if (verbose && sim->clock().current_time_ms() - last_print > 1000) { + std::cerr << "Peers joined: " << main_ctx.peer_count << "/" << num_peers + << std::endl; + last_print = sim->clock().current_time_ms(); + } + return false; + }, + 120000); + } + } + + ~GroupScalingContext(); +}; + +GroupScalingContext::~GroupScalingContext() = default; + +// --- Benchmark Definitions --- + +BENCHMARK_DEFINE_F(ToxIterateScalingFixture, Iterate)(benchmark::State &state) +{ + for (auto _ : state) { + tox_iterate(main_tox.get(), nullptr); + } + + state.counters["mem_current"] + = benchmark::Counter(static_cast(main_node->fake_memory().current_allocation()), + benchmark::Counter::kDefaults, benchmark::Counter::OneK::kIs1024); + state.counters["mem_max"] + = benchmark::Counter(static_cast(main_node->fake_memory().max_allocation()), + benchmark::Counter::kDefaults, benchmark::Counter::OneK::kIs1024); +} +BENCHMARK_REGISTER_F(ToxIterateScalingFixture, Iterate) + ->Arg(0) + ->Arg(10) + ->Arg(100) + ->Arg(200) + ->Arg(300); + +void RunConnectedScaling(benchmark::State &state, ConnectedContext &ctx) +{ + ctx.Setup(state.range(0)); + + for (auto _ : state) { + tox_iterate(ctx.main_tox.get(), nullptr); + } + + state.counters["mem_current"] + = benchmark::Counter(static_cast(ctx.main_node->fake_memory().current_allocation()), + benchmark::Counter::kDefaults, benchmark::Counter::OneK::kIs1024); + state.counters["mem_max"] + = benchmark::Counter(static_cast(ctx.main_node->fake_memory().max_allocation()), + benchmark::Counter::kDefaults, benchmark::Counter::OneK::kIs1024); +} + +void RunGroupScaling(benchmark::State &state, GroupScalingContext &ctx) +{ + ctx.Setup(state.range(0)); + + for (auto _ : state) { + tox_iterate(ctx.main_tox.get(), &ctx.main_ctx); + } + + state.counters["mem_current"] + = benchmark::Counter(static_cast(ctx.main_node->fake_memory().current_allocation()), + benchmark::Counter::kDefaults, benchmark::Counter::OneK::kIs1024); + state.counters["mem_max"] + = benchmark::Counter(static_cast(ctx.main_node->fake_memory().max_allocation()), + benchmark::Counter::kDefaults, benchmark::Counter::OneK::kIs1024); + state.counters["peers"] = benchmark::Counter( + static_cast(ctx.main_ctx.peer_count + 1), benchmark::Counter::kDefaults); +} + +/** + * @brief Benchmark the time and CPU required to discover and connect to many friends. + * + * This stresses the Onion Client's discovery mechanism (shared key caching) + * and the DHT's shared key cache efficiency. + */ +static void BM_MassDiscovery(benchmark::State &state) +{ + const int num_friends = state.range(0); + + for (auto _ : state) { + Simulation sim; + // Set a realistic latency to ensure packets are in flight and DHT/Onion logic + // has to run multiple iterations. + sim.net().set_latency(10); + + auto alice_node = sim.create_node(); + auto alice_tox = alice_node->create_tox(); + + // setup_connected_friends runs the simulation until all friends are connected. + auto friends = setup_connected_friends(sim, alice_tox.get(), *alice_node, num_friends); + + benchmark::DoNotOptimize(friends); + } +} +BENCHMARK(BM_MassDiscovery) + ->Arg(50) + ->Arg(100) + ->Arg(200) + ->Unit(benchmark::kMillisecond) + ->Iterations(5); + +} // namespace + +int main(int argc, char **argv) +{ + ::benchmark::Initialize(&argc, argv); + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) { + return 1; + } + + ConnectedContext connected_ctx; + benchmark::RegisterBenchmark("ToxConnectedScalingFixture/IterateConnected", + [&](benchmark::State &st) { RunConnectedScaling(st, connected_ctx); }) + ->Arg(0) + ->Arg(10) + ->Arg(20) + ->Arg(50); + + GroupScalingContext group_ctx; + benchmark::RegisterBenchmark("ToxGroupScalingFixture/IterateGroup", + [&](benchmark::State &st) { RunGroupScaling(st, group_ctx); }) + ->Arg(0) + ->Arg(10) + ->Arg(20) + ->Arg(50); + + ::benchmark::RunSpecifiedBenchmarks(); + ::benchmark::Shutdown(); + return 0; +} diff --git a/testing/bench/tox_messenger_bench.cc b/testing/bench/tox_messenger_bench.cc new file mode 100644 index 00000000..11726af4 --- /dev/null +++ b/testing/bench/tox_messenger_bench.cc @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#include + +#include + +#include "../../testing/support/public/simulation.hh" +#include "../../toxcore/network.h" +#include "../../toxcore/tox.h" + +namespace { + +using tox::test::Simulation; + +struct Context { + size_t count = 0; +}; + +void BM_ToxMessengerThroughput(benchmark::State &state) +{ + Simulation sim; + sim.net().set_latency(5); + auto node1 = sim.create_node(); + auto node2 = sim.create_node(); + + auto opts1 = std::unique_ptr( + tox_options_new(nullptr), tox_options_free); + tox_options_set_log_user_data(opts1.get(), const_cast("Tox1")); + tox_options_set_ipv6_enabled(opts1.get(), false); + tox_options_set_local_discovery_enabled(opts1.get(), false); + + auto opts2 = std::unique_ptr( + tox_options_new(nullptr), tox_options_free); + tox_options_set_log_user_data(opts2.get(), const_cast("Tox2")); + tox_options_set_ipv6_enabled(opts2.get(), false); + tox_options_set_local_discovery_enabled(opts2.get(), false); + + auto tox1 = node1->create_tox(opts1.get()); + auto tox2 = node2->create_tox(opts2.get()); + + if (!tox1 || !tox2) { + state.SkipWithError("Failed to create Tox instances"); + return; + } + + uint8_t tox1_pk[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_public_key(tox1.get(), tox1_pk); + uint8_t tox2_pk[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_public_key(tox2.get(), tox2_pk); + + uint8_t tox1_dht_id[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_dht_id(tox1.get(), tox1_dht_id); + uint8_t tox2_dht_id[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_dht_id(tox2.get(), tox2_dht_id); + + Tox_Err_Friend_Add friend_add_err; + uint32_t f1 = tox_friend_add_norequest(tox1.get(), tox2_pk, &friend_add_err); + uint32_t f2 = tox_friend_add_norequest(tox2.get(), tox1_pk, &friend_add_err); + + uint16_t port1 = node1->get_primary_socket()->local_port(); + uint16_t port2 = node2->get_primary_socket()->local_port(); + + char ip1[TOX_INET6_ADDRSTRLEN]; + ip_parse_addr(&node1->ip, ip1, sizeof(ip1)); + char ip2[TOX_INET6_ADDRSTRLEN]; + ip_parse_addr(&node2->ip, ip2, sizeof(ip2)); + + tox_bootstrap(tox2.get(), ip1, port1, tox1_dht_id, nullptr); + tox_bootstrap(tox1.get(), ip2, port2, tox2_dht_id, nullptr); + + bool connected = false; + sim.run_until( + [&]() { + tox_iterate(tox1.get(), nullptr); + tox_iterate(tox2.get(), nullptr); + sim.advance_time(90); // +10ms from run_until = 100ms + connected + = (tox_friend_get_connection_status(tox1.get(), f1, nullptr) != TOX_CONNECTION_NONE + && tox_friend_get_connection_status(tox2.get(), f2, nullptr) + != TOX_CONNECTION_NONE); + return connected; + }, + 60000); + + if (!connected) { + state.SkipWithError("Failed to connect toxes within 60s"); + return; + } + + const uint8_t msg[] = "benchmark message"; + const size_t msg_len = sizeof(msg); + + Context ctx; + tox_callback_friend_message(tox2.get(), + [](Tox *, uint32_t, Tox_Message_Type, const uint8_t *, size_t, void *user_data) { + static_cast(user_data)->count++; + }); + + for (auto _ : state) { + tox_friend_send_message(tox1.get(), f1, TOX_MESSAGE_TYPE_NORMAL, msg, msg_len, nullptr); + + for (int i = 0; i < 5; ++i) { + sim.advance_time(1); + tox_iterate(tox1.get(), nullptr); + tox_iterate(tox2.get(), &ctx); + } + } + + state.counters["messages_received"] + = benchmark::Counter(static_cast(ctx.count), benchmark::Counter::kAvgThreads); +} + +BENCHMARK(BM_ToxMessengerThroughput); + +void BM_ToxMessengerBidirectional(benchmark::State &state) +{ + Simulation sim; + sim.net().set_latency(5); + auto node1 = sim.create_node(); + auto node2 = sim.create_node(); + + auto opts1 = std::unique_ptr( + tox_options_new(nullptr), tox_options_free); + tox_options_set_log_user_data(opts1.get(), const_cast("Tox1")); + tox_options_set_ipv6_enabled(opts1.get(), false); + tox_options_set_local_discovery_enabled(opts1.get(), false); + + auto opts2 = std::unique_ptr( + tox_options_new(nullptr), tox_options_free); + tox_options_set_log_user_data(opts2.get(), const_cast("Tox2")); + tox_options_set_ipv6_enabled(opts2.get(), false); + tox_options_set_local_discovery_enabled(opts2.get(), false); + + auto tox1 = node1->create_tox(opts1.get()); + auto tox2 = node2->create_tox(opts2.get()); + + if (!tox1 || !tox2) { + state.SkipWithError("Failed to create Tox instances"); + return; + } + + uint8_t tox1_pk[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_public_key(tox1.get(), tox1_pk); + uint8_t tox2_pk[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_public_key(tox2.get(), tox2_pk); + + uint8_t tox1_dht_id[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_dht_id(tox1.get(), tox1_dht_id); + uint8_t tox2_dht_id[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_dht_id(tox2.get(), tox2_dht_id); + + Tox_Err_Friend_Add friend_add_err; + uint32_t f1 = tox_friend_add_norequest(tox1.get(), tox2_pk, &friend_add_err); + uint32_t f2 = tox_friend_add_norequest(tox2.get(), tox1_pk, &friend_add_err); + + uint16_t port1 = node1->get_primary_socket()->local_port(); + uint16_t port2 = node2->get_primary_socket()->local_port(); + + char ip1[TOX_INET6_ADDRSTRLEN]; + ip_parse_addr(&node1->ip, ip1, sizeof(ip1)); + char ip2[TOX_INET6_ADDRSTRLEN]; + ip_parse_addr(&node2->ip, ip2, sizeof(ip2)); + + tox_bootstrap(tox2.get(), ip1, port1, tox1_dht_id, nullptr); + tox_bootstrap(tox1.get(), ip2, port2, tox2_dht_id, nullptr); + + bool connected = false; + sim.run_until( + [&]() { + tox_iterate(tox1.get(), nullptr); + tox_iterate(tox2.get(), nullptr); + sim.advance_time(90); // +10ms from run_until = 100ms + connected + = (tox_friend_get_connection_status(tox1.get(), f1, nullptr) != TOX_CONNECTION_NONE + && tox_friend_get_connection_status(tox2.get(), f2, nullptr) + != TOX_CONNECTION_NONE); + return connected; + }, + 60000); + + if (!connected) { + state.SkipWithError("Failed to connect toxes within 60s"); + return; + } + + const uint8_t msg[] = "benchmark message"; + const size_t msg_len = sizeof(msg); + + Context ctx1, ctx2; + tox_callback_friend_message(tox1.get(), + [](Tox *, uint32_t, Tox_Message_Type, const uint8_t *, size_t, void *user_data) { + static_cast(user_data)->count++; + }); + + tox_callback_friend_message(tox2.get(), + [](Tox *, uint32_t, Tox_Message_Type, const uint8_t *, size_t, void *user_data) { + static_cast(user_data)->count++; + }); + + for (auto _ : state) { + tox_friend_send_message(tox1.get(), f1, TOX_MESSAGE_TYPE_NORMAL, msg, msg_len, nullptr); + tox_friend_send_message(tox2.get(), f2, TOX_MESSAGE_TYPE_NORMAL, msg, msg_len, nullptr); + + for (int i = 0; i < 5; ++i) { + sim.advance_time(1); + tox_iterate(tox1.get(), &ctx1); + tox_iterate(tox2.get(), &ctx2); + } + } + + state.counters["messages_received"] = benchmark::Counter( + static_cast(ctx1.count + ctx2.count), benchmark::Counter::kAvgThreads); +} + +BENCHMARK(BM_ToxMessengerBidirectional); + +} // namespace + +BENCHMARK_MAIN(); diff --git a/testing/support/BUILD.bazel b/testing/support/BUILD.bazel index bc32df56..8d1e1c67 100644 --- a/testing/support/BUILD.bazel +++ b/testing/support/BUILD.bazel @@ -18,6 +18,7 @@ cc_library( "src/simulated_environment.cc", "src/simulation.cc", "src/tox_network.cc", + "src/tox_runner.cc", ], hdrs = [ "doubles/fake_clock.hh", @@ -31,11 +32,13 @@ cc_library( "public/fuzz_data.hh", "public/fuzz_helpers.hh", "public/memory.hh", + "public/mpsc_queue.hh", "public/network.hh", "public/random.hh", "public/simulated_environment.hh", "public/simulation.hh", "public/tox_network.hh", + "public/tox_runner.hh", ], copts = select({ "//tools/config:windows": ["/wd4200"], # Zero-sized array in struct/union @@ -43,12 +46,14 @@ cc_library( }), visibility = ["//visibility:public"], deps = [ + "//c-toxcore/toxcore:attributes", "//c-toxcore/toxcore:mem", + "//c-toxcore/toxcore:net", "//c-toxcore/toxcore:network", + "//c-toxcore/toxcore:rng", "//c-toxcore/toxcore:tox", - "//c-toxcore/toxcore:tox_memory", + "//c-toxcore/toxcore:tox_events", "//c-toxcore/toxcore:tox_options", - "//c-toxcore/toxcore:tox_random", "@psocket", ], ) @@ -64,6 +69,28 @@ cc_test( ], ) +cc_test( + name = "fake_network_udp_test", + srcs = ["doubles/fake_network_udp_test.cc"], + deps = [ + ":support", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + "@psocket", + ], +) + +cc_test( + name = "fake_network_tcp_test", + srcs = ["doubles/fake_network_tcp_test.cc"], + deps = [ + ":support", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + "@psocket", + ], +) + cc_test( name = "fake_network_stack_test", srcs = ["doubles/fake_network_stack_test.cc"], @@ -98,12 +125,24 @@ cc_test( ], ) +cc_test( + name = "simulation_test", + srcs = ["simulation_test.cc"], + deps = [ + ":support", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "tox_network_test", timeout = "long", srcs = ["tox_network_test.cc"], deps = [ ":support", + "//c-toxcore/toxcore:attributes", + "//c-toxcore/toxcore:network", "//c-toxcore/toxcore:tox", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", diff --git a/testing/support/CMakeLists.txt b/testing/support/CMakeLists.txt index 00da142e..71288813 100644 --- a/testing/support/CMakeLists.txt +++ b/testing/support/CMakeLists.txt @@ -18,6 +18,7 @@ set(support_SOURCES src/simulated_environment.cc src/simulation.cc src/tox_network.cc + src/tox_runner.cc doubles/fake_clock.hh doubles/fake_memory.hh doubles/fake_network_stack.hh @@ -29,11 +30,13 @@ set(support_SOURCES public/fuzz_data.hh public/fuzz_helpers.hh public/memory.hh + public/mpsc_queue.hh public/network.hh public/random.hh public/simulated_environment.hh public/simulation.hh public/tox_network.hh + public/tox_runner.hh ) add_library(support STATIC ${support_SOURCES}) @@ -66,7 +69,10 @@ if(TARGET GTest::gtest_main) support_test(fake_sockets_test doubles/fake_sockets_test.cc) support_test(fake_network_stack_test doubles/fake_network_stack_test.cc) + support_test(fake_network_udp_test doubles/fake_network_udp_test.cc) + support_test(fake_network_tcp_test doubles/fake_network_tcp_test.cc) support_test(network_universe_test doubles/network_universe_test.cc) support_test(bootstrap_scaling_test bootstrap_scaling_test.cc) - support_test(tox_network_test tox_network_test.cc) + # TODO(iphydf): Re-enable once we migrate TCP server to ev. + #support_test(tox_network_test tox_network_test.cc) endif() diff --git a/testing/support/doubles/fake_clock.hh b/testing/support/doubles/fake_clock.hh index 0bfadeb5..a7ae2c47 100644 --- a/testing/support/doubles/fake_clock.hh +++ b/testing/support/doubles/fake_clock.hh @@ -1,6 +1,9 @@ #ifndef C_TOXCORE_TESTING_SUPPORT_DOUBLES_FAKE_CLOCK_H #define C_TOXCORE_TESTING_SUPPORT_DOUBLES_FAKE_CLOCK_H +#include +#include + #include "../public/clock.hh" namespace tox::test { @@ -15,7 +18,7 @@ public: void advance(uint64_t ms); private: - uint64_t now_ms_; + std::atomic now_ms_; }; } // namespace tox::test diff --git a/testing/support/doubles/fake_memory.hh b/testing/support/doubles/fake_memory.hh index 4754efb2..4b169e48 100644 --- a/testing/support/doubles/fake_memory.hh +++ b/testing/support/doubles/fake_memory.hh @@ -1,12 +1,13 @@ #ifndef C_TOXCORE_TESTING_SUPPORT_DOUBLES_FAKE_MEMORY_H #define C_TOXCORE_TESTING_SUPPORT_DOUBLES_FAKE_MEMORY_H +#include #include #include "../public/memory.hh" // Forward declaration -struct Tox_Memory; +struct Memory; namespace tox::test { @@ -18,9 +19,9 @@ public: FakeMemory(); ~FakeMemory() override; - void *malloc(size_t size) override; - void *realloc(void *ptr, size_t size) override; - void free(void *ptr) override; + void *_Nullable malloc(size_t size) override; + void *_Nullable realloc(void *_Nullable ptr, size_t size) override; + void free(void *_Nullable ptr) override; // Configure failure injection void set_failure_injector(FailureInjector injector); @@ -28,10 +29,18 @@ public: // Configure observer void set_observer(Observer observer); - // Get the C-compatible struct - struct Tox_Memory get_c_memory(); + /** + * @brief Returns C-compatible Memory struct. + */ + struct Memory c_memory() override; + + size_t current_allocation() const; + size_t max_allocation() const; private: + void on_allocation(size_t size); + void on_deallocation(size_t size); + struct Header { size_t size; size_t magic; @@ -39,8 +48,8 @@ private: static constexpr size_t kMagic = 0xDEADC0DE; static constexpr size_t kFreeMagic = 0xBAADF00D; - size_t current_allocation_ = 0; - size_t max_allocation_ = 0; + std::atomic current_allocation_{0}; + std::atomic max_allocation_{0}; FailureInjector failure_injector_; Observer observer_; diff --git a/testing/support/doubles/fake_network_stack.cc b/testing/support/doubles/fake_network_stack.cc index 02aa7ded..2fe046ee 100644 --- a/testing/support/doubles/fake_network_stack.cc +++ b/testing/support/doubles/fake_network_stack.cc @@ -8,55 +8,60 @@ namespace tox::test { -static const Network_Funcs kVtable = { - .close - = [](void *obj, Socket sock) { return static_cast(obj)->close(sock); }, - .accept - = [](void *obj, Socket sock) { return static_cast(obj)->accept(sock); }, - .bind - = [](void *obj, Socket sock, - const IP_Port *addr) { return static_cast(obj)->bind(sock, addr); }, +static const Network_Funcs kNetworkVtable = { + .close = [](void *_Nonnull obj, + Socket sock) { return static_cast(obj)->close(sock); }, + .accept = [](void *_Nonnull obj, + Socket sock) { return static_cast(obj)->accept(sock); }, + .bind = + [](void *_Nonnull obj, Socket sock, const IP_Port *_Nonnull addr) { + return static_cast(obj)->bind(sock, addr); + }, .listen - = [](void *obj, Socket sock, + = [](void *_Nonnull obj, Socket sock, int backlog) { return static_cast(obj)->listen(sock, backlog); }, .connect = - [](void *obj, Socket sock, const IP_Port *addr) { + [](void *_Nonnull obj, Socket sock, const IP_Port *_Nonnull addr) { return static_cast(obj)->connect(sock, addr); }, - .recvbuf - = [](void *obj, Socket sock) { return static_cast(obj)->recvbuf(sock); }, - .recv = [](void *obj, Socket sock, uint8_t *buf, + .recvbuf = [](void *_Nonnull obj, + Socket sock) { return static_cast(obj)->recvbuf(sock); }, + .recv = [](void *_Nonnull obj, Socket sock, uint8_t *_Nonnull buf, size_t len) { return static_cast(obj)->recv(sock, buf, len); }, .recvfrom = - [](void *obj, Socket sock, uint8_t *buf, size_t len, IP_Port *addr) { + [](void *_Nonnull obj, Socket sock, uint8_t *_Nonnull buf, size_t len, + IP_Port *_Nonnull addr) { return static_cast(obj)->recvfrom(sock, buf, len, addr); }, - .send = [](void *obj, Socket sock, const uint8_t *buf, + .send = [](void *_Nonnull obj, Socket sock, const uint8_t *_Nonnull buf, size_t len) { return static_cast(obj)->send(sock, buf, len); }, .sendto = - [](void *obj, Socket sock, const uint8_t *buf, size_t len, const IP_Port *addr) { + [](void *_Nonnull obj, Socket sock, const uint8_t *_Nonnull buf, size_t len, + const IP_Port *_Nonnull addr) { return static_cast(obj)->sendto(sock, buf, len, addr); }, .socket - = [](void *obj, int domain, int type, + = [](void *_Nonnull obj, int domain, int type, int proto) { return static_cast(obj)->socket(domain, type, proto); }, .socket_nonblock = - [](void *obj, Socket sock, bool nonblock) { + [](void *_Nonnull obj, Socket sock, bool nonblock) { return static_cast(obj)->socket_nonblock(sock, nonblock); }, .getsockopt = - [](void *obj, Socket sock, int level, int optname, void *optval, size_t *optlen) { + [](void *_Nonnull obj, Socket sock, int level, int optname, void *_Nonnull optval, + size_t *_Nonnull optlen) { return static_cast(obj)->getsockopt( sock, level, optname, optval, optlen); }, .setsockopt = - [](void *obj, Socket sock, int level, int optname, const void *optval, size_t optlen) { + [](void *_Nonnull obj, Socket sock, int level, int optname, const void *_Nonnull optval, + size_t optlen) { return static_cast(obj)->setsockopt( sock, level, optname, optval, optlen); }, .getaddrinfo = - [](void *obj, const Memory *mem, const char *address, int family, int protocol, - IP_Port **addrs) { + [](void *_Nonnull obj, const Memory *_Nonnull mem, const char *_Nonnull address, int family, + int protocol, IP_Port *_Nullable *_Nonnull addrs) { FakeNetworkStack *self = static_cast(obj); if (self->universe().is_verbose()) { std::cerr << "[FakeNetworkStack] getaddrinfo for " << address << std::endl; @@ -83,7 +88,7 @@ static const Network_Funcs kVtable = { return 0; }, .freeaddrinfo = - [](void *obj, const Memory *mem, IP_Port *addrs) { + [](void *_Nonnull obj, const Memory *_Nonnull mem, IP_Port *_Nullable addrs) { mem_delete(mem, addrs); return 0; }, @@ -97,7 +102,7 @@ FakeNetworkStack::FakeNetworkStack(NetworkUniverse &universe, const IP &node_ip) FakeNetworkStack::~FakeNetworkStack() = default; -struct Network FakeNetworkStack::get_c_network() { return Network{&kVtable, this}; } +struct Network FakeNetworkStack::c_network() { return Network{&kNetworkVtable, this}; } Socket FakeNetworkStack::socket(int domain, int type, int protocol) { diff --git a/testing/support/doubles/fake_network_stack.hh b/testing/support/doubles/fake_network_stack.hh index a09dd282..6dcb9f8a 100644 --- a/testing/support/doubles/fake_network_stack.hh +++ b/testing/support/doubles/fake_network_stack.hh @@ -2,8 +2,11 @@ #define C_TOXCORE_TESTING_SUPPORT_DOUBLES_FAKE_NETWORK_STACK_H #include +#include #include +#include +#include "../../../toxcore/net.h" #include "../public/network.hh" #include "fake_sockets.hh" #include "network_universe.hh" @@ -17,33 +20,38 @@ public: // NetworkSystem Implementation Socket socket(int domain, int type, int protocol) override; - int bind(Socket sock, const IP_Port *addr) override; + int bind(Socket sock, const IP_Port *_Nonnull addr) override; int close(Socket sock) override; - int sendto(Socket sock, const uint8_t *buf, size_t len, const IP_Port *addr) override; - int recvfrom(Socket sock, uint8_t *buf, size_t len, IP_Port *addr) override; + int sendto(Socket sock, const uint8_t *_Nonnull buf, size_t len, + const IP_Port *_Nonnull addr) override; + int recvfrom(Socket sock, uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr) override; int listen(Socket sock, int backlog) override; Socket accept(Socket sock) override; - int connect(Socket sock, const IP_Port *addr) override; - int send(Socket sock, const uint8_t *buf, size_t len) override; - int recv(Socket sock, uint8_t *buf, size_t len) override; + int connect(Socket sock, const IP_Port *_Nonnull addr) override; + int send(Socket sock, const uint8_t *_Nonnull buf, size_t len) override; + int recv(Socket sock, uint8_t *_Nonnull buf, size_t len) override; int recvbuf(Socket sock) override; int socket_nonblock(Socket sock, bool nonblock) override; - int getsockopt(Socket sock, int level, int optname, void *optval, size_t *optlen) override; - int setsockopt(Socket sock, int level, int optname, const void *optval, size_t optlen) override; + int getsockopt(Socket sock, int level, int optname, void *_Nonnull optval, + size_t *_Nonnull optlen) override; + int setsockopt( + Socket sock, int level, int optname, const void *_Nonnull optval, size_t optlen) override; - struct Network get_c_network(); + /** + * @brief Returns C-compatible Network struct. + */ + struct Network c_network() override; // For testing/fuzzing introspection - FakeUdpSocket *get_udp_socket(Socket sock); + FakeSocket *_Nullable get_sock(Socket sock); + FakeUdpSocket *_Nullable get_udp_socket(Socket sock); std::vector get_bound_udp_sockets(); NetworkUniverse &universe() { return universe_; } private: - FakeSocket *get_sock(Socket sock); - NetworkUniverse &universe_; std::map> sockets_; int next_fd_ = 100; diff --git a/testing/support/doubles/fake_network_stack_test.cc b/testing/support/doubles/fake_network_stack_test.cc index c171363c..a6042a8d 100644 --- a/testing/support/doubles/fake_network_stack_test.cc +++ b/testing/support/doubles/fake_network_stack_test.cc @@ -87,5 +87,62 @@ namespace { ASSERT_NE(net_socket_to_native(accepted), -1); } + TEST_F(FakeNetworkStackTest, LoopbackRedirection) + { + // 1. Create a stack with a specific IP (20.0.0.1) + FakeNetworkStack my_stack{universe, make_ip(0x14000001)}; + Socket sock = my_stack.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + IP_Port bind_addr; + ip_init(&bind_addr.ip, false); + bind_addr.ip.ip.v4.uint32 = net_htonl(0x14000001); + bind_addr.port = net_htons(12345); + ASSERT_EQ(my_stack.bind(sock, &bind_addr), 0); + ASSERT_EQ(my_stack.listen(sock, 5), 0); + + // 2. Connect to 127.0.0.1:12345 from the same stack + Socket client = my_stack.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + IP_Port connect_addr; + ip_init(&connect_addr.ip, false); + connect_addr.ip.ip.v4.uint32 = net_htonl(0x7F000001); + connect_addr.port = net_htons(12345); + + // Should redirect to 20.0.0.1:12345 because 127.0.0.1 is not bound + ASSERT_EQ(my_stack.connect(client, &connect_addr), -1); + ASSERT_EQ(errno, EINPROGRESS); + + universe.process_events(0); // SYN + + Socket accepted = my_stack.accept(sock); + ASSERT_NE(net_socket_to_native(accepted), -1); + } + + TEST_F(FakeNetworkStackTest, ImplicitBindAvoidsCollision) + { + // Bind server to 33445 (default start of find_free_port) + Socket server = stack.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + IP_Port addr; + ip_init(&addr.ip, false); + addr.ip.ip.v4.uint32 = 0; + addr.port = net_htons(33445); + ASSERT_EQ(stack.bind(server, &addr), 0); + + // Create client and connect (implicit bind) + Socket client = stack.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + IP_Port server_addr; + ip_init(&server_addr.ip, false); + server_addr.ip.ip.v4.uint32 = net_htonl(0x7F000001); + server_addr.port = net_htons(33445); + + // Should find a free port (not 33445) + ASSERT_EQ(stack.connect(client, &server_addr), -1); + ASSERT_EQ(errno, EINPROGRESS); + + auto *client_obj = stack.get_sock(client); + ASSERT_NE(client_obj, nullptr); + ASSERT_NE(client_obj->local_port(), 33445); + } + } // namespace } // namespace tox::test diff --git a/testing/support/doubles/fake_network_tcp_test.cc b/testing/support/doubles/fake_network_tcp_test.cc new file mode 100644 index 00000000..4cfa3e91 --- /dev/null +++ b/testing/support/doubles/fake_network_tcp_test.cc @@ -0,0 +1,459 @@ +#include + +#include "fake_sockets.hh" +#include "network_universe.hh" + +namespace tox::test { +namespace { + + class FakeNetworkTcpTest : public ::testing::Test { + protected: + NetworkUniverse universe; + + IP make_ip(uint8_t a, uint8_t b, uint8_t c, uint8_t d) + { + IP ip; + ip_init(&ip, false); + ip.ip.v4.uint8[0] = a; + ip.ip.v4.uint8[1] = b; + ip.ip.v4.uint8[2] = c; + ip.ip.v4.uint8[3] = d; + return ip; + } + }; + + TEST_F(FakeNetworkTcpTest, MultipleConnectionsToSamePort) + { + universe.set_verbose(true); + IP server_ip = make_ip(10, 0, 0, 1); + uint16_t server_port = 12345; + + FakeTcpSocket server(universe); + server.set_ip(server_ip); + IP_Port server_addr{server_ip, net_htons(server_port)}; + ASSERT_EQ(server.bind(&server_addr), 0); + ASSERT_EQ(server.listen(5), 0); + + // Client 1 + IP client1_ip = make_ip(10, 0, 0, 2); + FakeTcpSocket client1(universe); + client1.set_ip(client1_ip); + client1.connect(&server_addr); + + // Client 2 (same IP as client 1, different port) + FakeTcpSocket client2(universe); + client2.set_ip(client1_ip); + client2.connect(&server_addr); + + // Handshake for both + // 1. SYNs + universe.process_events(0); + universe.process_events(0); + + // 2. SYN-ACKs + universe.process_events(0); + universe.process_events(0); + + // 3. ACKs + universe.process_events(0); + universe.process_events(0); + + auto accepted1 = server.accept(nullptr); + auto accepted2 = server.accept(nullptr); + + ASSERT_NE(accepted1, nullptr); + ASSERT_NE(accepted2, nullptr); + + EXPECT_EQ( + static_cast(accepted1.get())->state(), FakeTcpSocket::ESTABLISHED); + EXPECT_EQ( + static_cast(accepted2.get())->state(), FakeTcpSocket::ESTABLISHED); + + // Verify data isolation + const char *msg1 = "Message 1"; + const char *msg2 = "Message 2"; + + client1.send(reinterpret_cast(msg1), strlen(msg1)); + client2.send(reinterpret_cast(msg2), strlen(msg2)); + + universe.process_events(0); + universe.process_events(0); + + uint8_t buf[100]; + int len1 = accepted1->recv(buf, sizeof(buf)); + EXPECT_EQ(len1, strlen(msg1)); + int len2 = accepted2->recv(buf, sizeof(buf)); + EXPECT_EQ(len2, strlen(msg2)); + } + + TEST_F(FakeNetworkTcpTest, DuplicateSynCreatesDuplicateConnections) + { + universe.set_verbose(true); + IP server_ip = make_ip(10, 0, 0, 1); + uint16_t server_port = 12345; + + FakeTcpSocket server(universe); + server.set_ip(server_ip); + IP_Port server_addr{server_ip, net_htons(server_port)}; + server.bind(&server_addr); + server.listen(5); + + IP client_ip = make_ip(10, 0, 0, 2); + IP_Port client_addr{client_ip, net_htons(33445)}; + + Packet p{}; + p.from = client_addr; + p.to = server_addr; + p.is_tcp = true; + p.tcp_flags = 0x02; // SYN + p.seq = 100; + + universe.send_packet(p); + universe.send_packet(p); // Duplicate SYN + + universe.process_events(0); + universe.process_events(0); + + // Now send ACK from client + Packet ack{}; + ack.from = client_addr; + ack.to = server_addr; + ack.is_tcp = true; + ack.tcp_flags = 0x10; // ACK + ack.ack = 101; + + universe.send_packet(ack); + universe.process_events(0); + + auto accepted1 = server.accept(nullptr); + auto accepted2 = server.accept(nullptr); + + ASSERT_NE(accepted1, nullptr); + EXPECT_EQ(accepted2, nullptr); // This should pass now + } + + TEST_F(FakeNetworkTcpTest, PeerCloseClearsConnection) + { + universe.set_verbose(true); + IP server_ip = make_ip(10, 0, 0, 1); + uint16_t server_port = 12345; + + FakeTcpSocket server(universe); + server.set_ip(server_ip); + IP_Port server_addr{server_ip, net_htons(server_port)}; + server.bind(&server_addr); + server.listen(5); + + IP client_ip = make_ip(10, 0, 0, 2); + FakeTcpSocket client(universe); + client.set_ip(client_ip); + client.connect(&server_addr); + + // Handshake + universe.process_events(0); // SYN + universe.process_events(0); // SYN-ACK + universe.process_events(0); // ACK + + auto accepted = server.accept(nullptr); + ASSERT_NE(accepted, nullptr); + EXPECT_EQ( + static_cast(accepted.get())->state(), FakeTcpSocket::ESTABLISHED); + + // Client closes + client.close(); + universe.process_events(0); // Deliver RST/FIN + + // Server should no longer be ESTABLISHED + EXPECT_EQ(static_cast(accepted.get())->state(), FakeTcpSocket::CLOSED); + + // Now if client reconnects with same port + FakeTcpSocket client2(universe); + client2.set_ip(client_ip); + client2.connect(&server_addr); + universe.process_events(0); // Deliver SYN + + // Node 2 port 20002 should have: 1 LISTEN, 0 ESTABLISHED (old one gone), 1 SYN_RECEIVED + // (new one) Total targets should be 2. + } + + TEST_F(FakeNetworkTcpTest, DataNotProcessedByMultipleSockets) + { + universe.set_verbose(true); + IP server_ip = make_ip(10, 0, 0, 1); + uint16_t server_port = 12345; + + FakeTcpSocket server(universe); + server.set_ip(server_ip); + IP_Port server_addr{server_ip, net_htons(server_port)}; + server.bind(&server_addr); + server.listen(5); + + IP client_ip = make_ip(10, 0, 0, 2); + IP_Port client_addr{client_ip, net_htons(33445)}; + + // Manually create two "established" sockets on the same port for the same peer + // This simulates a bug where duplicate connections were allowed. + auto sock1 = FakeTcpSocket::create_connected(universe, client_addr, server_port); + sock1->set_ip(server_ip); + auto sock2 = FakeTcpSocket::create_connected(universe, client_addr, server_port); + sock2->set_ip(server_ip); + + universe.bind_tcp(server_ip, server_port, sock1.get()); + universe.bind_tcp(server_ip, server_port, sock2.get()); + + // Send data from client to server + Packet p{}; + p.from = client_addr; + p.to = server_addr; + p.is_tcp = true; + p.tcp_flags = 0x10; // ACK (Data) + const char *data = "Unique"; + p.data.assign(data, data + strlen(data)); + + universe.send_packet(p); + universe.process_events(0); + + // Only ONE of them should have received it, or at least they shouldn't BOTH have it + // in a way that suggests duplicate delivery. + EXPECT_TRUE((sock1->recv_buffer_size() == strlen(data)) + ^ (sock2->recv_buffer_size() == strlen(data))); + } + + TEST_F(FakeNetworkTcpTest, ConnectionCollision) + { + universe.set_verbose(true); + IP server_ip = make_ip(10, 0, 0, 1); + uint16_t server_port = 12345; + + FakeTcpSocket server(universe); + server.set_ip(server_ip); + IP_Port server_addr{server_ip, net_htons(server_port)}; + server.bind(&server_addr); + server.listen(5); + + IP client_ip = make_ip(10, 0, 0, 2); + + FakeTcpSocket client1(universe); + client1.set_ip(client_ip); + // Bind to specific port to force collision later + IP_Port client_bind_addr{client_ip, net_htons(33445)}; + client1.bind(&client_bind_addr); + client1.connect(&server_addr); + + // Handshake 1 + universe.process_events(0); // SYN + universe.process_events(0); // SYN-ACK + universe.process_events(0); // ACK + + auto accepted1 = server.accept(nullptr); + ASSERT_NE(accepted1, nullptr); + EXPECT_EQ( + static_cast(accepted1.get())->state(), FakeTcpSocket::ESTABLISHED); + + // Now client 1 "reconnects" (e.g. after a crash or timeout, but using same port) + FakeTcpSocket client2(universe); + client2.set_ip(client_ip); + client2.bind(&client_bind_addr); // Forced collision + client2.connect(&server_addr); + + // Deliver new SYN + universe.process_events(0); + + // server_addr port 12345 now has: + // 1. LISTEN socket + // 2. accepted1 (ESTABLISHED with 10.0.0.2:33445) + + // In our simplified simulation, the ESTABLISHED socket now handles the SYN by returning + // true (ignoring it). So no new connection is created. + auto accepted2 = server.accept(nullptr); + EXPECT_EQ(accepted2, nullptr); + + const char *msg1 = "Data 1"; + client1.send(reinterpret_cast(msg1), strlen(msg1)); + universe.process_events(0); + + // Data should still go to accepted1 + EXPECT_EQ(accepted1->recv_buffer_size(), strlen(msg1)); + } + + TEST_F(FakeNetworkTcpTest, LoopbackConnection) + { + universe.set_verbose(true); + IP node_ip = make_ip(10, 0, 0, 1); + uint16_t port = 12345; + + FakeTcpSocket server(universe); + server.set_ip(node_ip); + IP_Port listen_addr{node_ip, net_htons(port)}; + server.bind(&listen_addr); + server.listen(5); + + FakeTcpSocket client(universe); + client.set_ip(node_ip); + IP loopback_ip; + ip_init(&loopback_ip, false); + loopback_ip.ip.v4.uint32 = net_htonl(0x7F000001); + IP_Port server_loopback_addr{loopback_ip, net_htons(port)}; + + client.connect(&server_loopback_addr); + + // SYN (Client -> 127.0.0.1:12345) + universe.process_events(0); + + // SYN-ACK (Server -> Client) + universe.process_events(0); + + // ACK (Client -> Server) + universe.process_events(0); + + EXPECT_EQ(client.state(), FakeTcpSocket::ESTABLISHED); + auto accepted = server.accept(nullptr); + ASSERT_NE(accepted, nullptr); + EXPECT_EQ( + static_cast(accepted.get())->state(), FakeTcpSocket::ESTABLISHED); + + // Data Transfer + const char *msg = "Loopback"; + client.send(reinterpret_cast(msg), strlen(msg)); + universe.process_events(0); + + uint8_t buf[100]; + int len = accepted->recv(buf, sizeof(buf)); + ASSERT_EQ(len, strlen(msg)); + EXPECT_EQ(std::string(reinterpret_cast(buf), len), msg); + } + + TEST_F(FakeNetworkTcpTest, SimultaneousConnect) + { + universe.set_verbose(true); + IP ipA = make_ip(10, 0, 0, 1); + IP ipB = make_ip(10, 0, 0, 2); + uint16_t portA = 10001; + uint16_t portB = 10002; + + FakeTcpSocket sockA(universe); + sockA.set_ip(ipA); + IP_Port addrA{ipA, net_htons(portA)}; + sockA.bind(&addrA); + sockA.listen(5); + + FakeTcpSocket sockB(universe); + sockB.set_ip(ipB); + IP_Port addrB{ipB, net_htons(portB)}; + sockB.bind(&addrB); + sockB.listen(5); + + // A connects to B + sockA.connect(&addrB); + // B connects to A + sockB.connect(&addrA); + + // This is "simultaneous open" in TCP but here they are also LISTENing. + // Toxcore uses this pattern sometimes. + + universe.process_events(0); // SYN from A to B + universe.process_events(0); // SYN from B to A + + universe.process_events(0); // SYN-ACK from B to A (for A's SYN) + universe.process_events(0); // SYN-ACK from A to B (for B's SYN) + + universe.process_events(0); // ACK from A to B + universe.process_events(0); // ACK from B to A + + EXPECT_EQ(sockA.state(), FakeTcpSocket::ESTABLISHED); + EXPECT_EQ(sockB.state(), FakeTcpSocket::ESTABLISHED); + } + + TEST_F(FakeNetworkTcpTest, DataInHandshakeAck) + { + universe.set_verbose(true); + IP server_ip = make_ip(10, 0, 0, 1); + uint16_t server_port = 12345; + + FakeTcpSocket server(universe); + server.set_ip(server_ip); + IP_Port server_addr{server_ip, net_htons(server_port)}; + server.bind(&server_addr); + server.listen(5); + + IP client_ip = make_ip(10, 0, 0, 2); + IP_Port client_addr{client_ip, net_htons(33445)}; + + // 1. SYN + Packet syn{}; + syn.from = client_addr; + syn.to = server_addr; + syn.is_tcp = true; + syn.tcp_flags = 0x02; + universe.send_packet(syn); + universe.process_events(0); + + // 2. SYN-ACK (Server -> Client) + universe.process_events(0); + + // 3. ACK + Data (Client -> Server) + Packet ack{}; + ack.from = client_addr; + ack.to = server_addr; + ack.is_tcp = true; + ack.tcp_flags = 0x10; + const char *data = "HandshakeData"; + ack.data.assign(data, data + strlen(data)); + universe.send_packet(ack); + universe.process_events(0); + + auto accepted = server.accept(nullptr); + ASSERT_NE(accepted, nullptr); + EXPECT_EQ(accepted->recv_buffer_size(), strlen(data)); + } + + TEST_F(FakeNetworkTcpTest, LoopbackWithNodeIPMixed) + { + universe.set_verbose(true); + IP node_ip = make_ip(10, 0, 0, 1); + uint16_t port = 12345; + + FakeTcpSocket server(universe); + server.set_ip(node_ip); + IP_Port listen_addr{node_ip, net_htons(port)}; + server.bind(&listen_addr); + server.listen(5); + + FakeTcpSocket client(universe); + client.set_ip(node_ip); + IP loopback_ip; + ip_init(&loopback_ip, false); + loopback_ip.ip.v4.uint32 = net_htonl(0x7F000001); + IP_Port server_loopback_addr{loopback_ip, net_htons(port)}; + + // Client connects to 127.0.0.1 + client.connect(&server_loopback_addr); + + universe.process_events(0); // SYN (Client -> 127.0.0.1) + universe.process_events(0); // SYN-ACK (Server -> Client) + universe.process_events(0); // ACK (Client -> Server) + + EXPECT_EQ(client.state(), FakeTcpSocket::ESTABLISHED); + auto accepted = server.accept(nullptr); + ASSERT_NE(accepted, nullptr); + + // Now manually simulate a packet coming from the server's EXTERNAL IP to the client. + // This happens because the server socket is bound to node_ip, so its packets might + // be delivered as coming from node_ip even if the client connected to 127.0.0.1. + Packet p{}; + p.from = listen_addr; // node_ip:port + p.to.ip = node_ip; + p.to.port = net_htons(client.local_port()); + p.is_tcp = true; + p.tcp_flags = 0x10; // ACK + const char *msg = "MixedIP"; + p.data.assign(msg, msg + strlen(msg)); + + universe.send_packet(p); + universe.process_events(0); + + EXPECT_EQ(client.recv_buffer_size(), strlen(msg)); + } + +} +} diff --git a/testing/support/doubles/fake_network_udp_test.cc b/testing/support/doubles/fake_network_udp_test.cc new file mode 100644 index 00000000..e2e0a4ae --- /dev/null +++ b/testing/support/doubles/fake_network_udp_test.cc @@ -0,0 +1,75 @@ +#include + +#include "fake_network_stack.hh" +#include "network_universe.hh" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#else +#include +#include +#endif + +namespace tox::test { +namespace { + + class FakeNetworkUdpTest : public ::testing::Test { + public: + FakeNetworkUdpTest() + : ip1(make_ip(0x0A000001)) // 10.0.0.1 + , ip2(make_ip(0x0A000002)) // 10.0.0.2 + , stack1{universe, ip1} + , stack2{universe, ip2} + { + } + + protected: + NetworkUniverse universe; + IP ip1, ip2; + FakeNetworkStack stack1; + FakeNetworkStack stack2; + }; + + TEST_F(FakeNetworkUdpTest, UdpExchange) + { + Socket sock1 = stack1.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + Socket sock2 = stack2.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + IP_Port addr1; + addr1.ip = ip1; + addr1.port = net_htons(1234); + ASSERT_EQ(stack1.bind(sock1, &addr1), 0); + + IP_Port addr2; + addr2.ip = ip2; + addr2.port = net_htons(5678); + ASSERT_EQ(stack2.bind(sock2, &addr2), 0); + + const char *msg = "Hello UDP"; + size_t msg_len = strlen(msg) + 1; + + // Send from 1 to 2 + ASSERT_EQ(stack1.sendto(sock1, reinterpret_cast(msg), msg_len, &addr2), + static_cast(msg_len)); + + // Delivery + universe.process_events(10); // With some time offset + + // Receive at 2 + uint8_t buffer[1024]; + IP_Port from_addr; + int recv_len = stack2.recvfrom(sock2, buffer, sizeof(buffer), &from_addr); + + ASSERT_EQ(recv_len, static_cast(msg_len)); + EXPECT_STREQ(reinterpret_cast(buffer), msg); + EXPECT_EQ(net_ntohl(from_addr.ip.ip.v4.uint32), net_ntohl(ip1.ip.v4.uint32)); + EXPECT_EQ(net_ntohs(from_addr.port), 1234); + + stack1.close(sock1); + stack2.close(sock2); + } + +} // namespace +} // namespace tox::test diff --git a/testing/support/doubles/fake_random.hh b/testing/support/doubles/fake_random.hh index df2de861..9ce2b0da 100644 --- a/testing/support/doubles/fake_random.hh +++ b/testing/support/doubles/fake_random.hh @@ -7,19 +7,19 @@ #include "../public/random.hh" // Forward declaration -struct Tox_Random; +struct Random; namespace tox::test { class FakeRandom : public RandomSystem { public: - using EntropySource = std::function; - using Observer = std::function; + using EntropySource = std::function; + using Observer = std::function; explicit FakeRandom(uint64_t seed); uint32_t uniform(uint32_t upper_bound) override; - void bytes(uint8_t *out, size_t count) override; + void bytes(uint8_t *_Nonnull out, size_t count) override; /** * @brief Set a custom entropy source. @@ -32,7 +32,10 @@ public: */ void set_observer(Observer observer); - struct Tox_Random get_c_random(); + /** + * @brief Returns C-compatible Random struct. + */ + struct Random c_random() override; private: std::minstd_rand rng_; diff --git a/testing/support/doubles/fake_sockets.cc b/testing/support/doubles/fake_sockets.cc index 10dff41d..628d044e 100644 --- a/testing/support/doubles/fake_sockets.cc +++ b/testing/support/doubles/fake_sockets.cc @@ -3,7 +3,11 @@ #include #include #include +#include +#include #include +#include +#include #include "network_universe.hh" @@ -27,8 +31,14 @@ int FakeSocket::close() return 0; } -int FakeSocket::getsockopt(int level, int optname, void *optval, size_t *optlen) { return 0; } -int FakeSocket::setsockopt(int level, int optname, const void *optval, size_t optlen) { return 0; } +int FakeSocket::getsockopt(int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen) +{ + return 0; +} +int FakeSocket::setsockopt(int level, int optname, const void *_Nonnull optval, size_t optlen) +{ + return 0; +} int FakeSocket::socket_nonblock(bool nonblock) { nonblocking_ = nonblock; @@ -59,7 +69,7 @@ void FakeUdpSocket::close_impl() } } -int FakeUdpSocket::bind(const IP_Port *addr) +int FakeUdpSocket::bind(const IP_Port *_Nonnull addr) { std::lock_guard lock(mutex_); if (local_port_ != 0) @@ -80,7 +90,7 @@ int FakeUdpSocket::bind(const IP_Port *addr) return -1; } -int FakeUdpSocket::connect(const IP_Port *addr) +int FakeUdpSocket::connect(const IP_Port *_Nonnull addr) { // UDP connect just sets default dest. // Not strictly needed for toxcore UDP but good for completeness. @@ -92,23 +102,29 @@ int FakeUdpSocket::listen(int backlog) errno = EOPNOTSUPP; return -1; } -std::unique_ptr FakeUdpSocket::accept(IP_Port *addr) +std::unique_ptr FakeUdpSocket::accept(IP_Port *_Nullable addr) { errno = EOPNOTSUPP; return nullptr; } -int FakeUdpSocket::send(const uint8_t *buf, size_t len) +int FakeUdpSocket::send(const uint8_t *_Nonnull buf, size_t len) { errno = EDESTADDRREQ; return -1; } -int FakeUdpSocket::recv(uint8_t *buf, size_t len) +int FakeUdpSocket::recv(uint8_t *_Nonnull buf, size_t len) { errno = EOPNOTSUPP; return -1; } -int FakeUdpSocket::sendto(const uint8_t *buf, size_t len, const IP_Port *addr) +size_t FakeUdpSocket::recv_buffer_size() +{ + std::lock_guard lock(mutex_); + return recv_queue_.size(); +} + +int FakeUdpSocket::sendto(const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr) { std::lock_guard lock(mutex_); if (local_port_ == 0) { @@ -132,16 +148,15 @@ int FakeUdpSocket::sendto(const uint8_t *buf, size_t len, const IP_Port *addr) universe_.send_packet(p); if (universe_.is_verbose()) { - uint32_t tip4 = net_ntohl(addr->ip.ip.v4.uint32); + Ip_Ntoa ip_str; + net_ip_ntoa(&addr->ip, &ip_str); std::cerr << "[FakeUdpSocket] sent " << len << " bytes from port " << local_port_ << " to " - << ((tip4 >> 24) & 0xFF) << "." << ((tip4 >> 16) & 0xFF) << "." - << ((tip4 >> 8) & 0xFF) << "." << (tip4 & 0xFF) << ":" << net_ntohs(addr->port) - << std::endl; + << ip_str.buf << ":" << net_ntohs(addr->port) << std::endl; } return len; } -int FakeUdpSocket::recvfrom(uint8_t *buf, size_t len, IP_Port *addr) +int FakeUdpSocket::recvfrom(uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr) { RecvObserver observer_copy; std::vector data_copy; @@ -196,15 +211,13 @@ void FakeUdpSocket::push_packet(std::vector data, IP_Port from) { std::lock_guard lock(mutex_); if (universe_.is_verbose()) { - uint32_t fip4 = net_ntohl(from.ip.ip.v4.uint32); + Ip_Ntoa local_ip_str, from_ip_str; + net_ip_ntoa(&ip_, &local_ip_str); + net_ip_ntoa(&from.ip, &from_ip_str); + std::cerr << "[FakeUdpSocket] push " << data.size() << " bytes into queue for " - << ((ip_.ip.v4.uint32 >> 24) & 0xFF) - << "." // ip_ is in network order from net_htonl - << ((ip_.ip.v4.uint32 >> 16) & 0xFF) << "." << ((ip_.ip.v4.uint32 >> 8) & 0xFF) - << "." << (ip_.ip.v4.uint32 & 0xFF) << ":" << local_port_ << " from " - << ((fip4 >> 24) & 0xFF) << "." << ((fip4 >> 16) & 0xFF) << "." - << ((fip4 >> 8) & 0xFF) << "." << (fip4 & 0xFF) << ":" << net_ntohs(from.port) - << std::endl; + << local_ip_str.buf << ":" << local_port_ << " from " << from_ip_str.buf << ":" + << net_ntohs(from.port) << std::endl; } recv_queue_.push_back({std::move(data), from}); } @@ -225,8 +238,8 @@ void FakeUdpSocket::set_recv_observer(RecvObserver observer) FakeTcpSocket::FakeTcpSocket(NetworkUniverse &universe) : FakeSocket(universe, SOCK_STREAM) - , remote_addr_{} { + ipport_reset(&remote_addr_); } FakeTcpSocket::~FakeTcpSocket() { close_impl(); } @@ -234,6 +247,17 @@ FakeTcpSocket::~FakeTcpSocket() { close_impl(); } int FakeTcpSocket::close() { std::lock_guard lock(mutex_); + if (state_ == ESTABLISHED || state_ == SYN_SENT || state_ == SYN_RECEIVED + || state_ == CLOSE_WAIT) { + // Send RST to peer + Packet p{}; + p.from.ip = ip_; + p.from.port = net_htons(local_port_); + p.to = remote_addr_; + p.is_tcp = true; + p.tcp_flags = 0x04; // RST + universe_.send_packet(p); + } close_impl(); return 0; } @@ -247,7 +271,7 @@ void FakeTcpSocket::close_impl() state_ = CLOSED; } -int FakeTcpSocket::bind(const IP_Port *addr) +int FakeTcpSocket::bind(const IP_Port *_Nonnull addr) { std::lock_guard lock(mutex_); if (local_port_ != 0) @@ -276,14 +300,25 @@ int FakeTcpSocket::listen(int backlog) return 0; } -int FakeTcpSocket::connect(const IP_Port *addr) +int FakeTcpSocket::connect(const IP_Port *_Nonnull addr) { std::lock_guard lock(mutex_); + if (universe_.is_verbose()) { + Ip_Ntoa ip_str, dest_str; + net_ip_ntoa(&ip_, &ip_str); + net_ip_ntoa(&addr->ip, &dest_str); + std::cerr << "[FakeTcpSocket] connect from " << ip_str.buf << " to " << dest_str.buf << ":" + << net_ntohs(addr->port) << std::endl; + } + if (local_port_ == 0) { // Implicit bind uint16_t p = universe_.find_free_port(ip_); if (universe_.bind_tcp(ip_, p, this)) { local_port_ = p; + if (universe_.is_verbose()) { + std::cerr << "[FakeTcpSocket] implicit bind to port " << local_port_ << std::endl; + } } else { errno = EADDRINUSE; return -1; @@ -310,7 +345,7 @@ int FakeTcpSocket::connect(const IP_Port *addr) return -1; } -std::unique_ptr FakeTcpSocket::accept(IP_Port *addr) +std::unique_ptr FakeTcpSocket::accept(IP_Port *_Nullable addr) { std::lock_guard lock(mutex_); if (state_ != LISTEN) { @@ -318,13 +353,16 @@ std::unique_ptr FakeTcpSocket::accept(IP_Port *addr) return nullptr; } - if (pending_connections_.empty()) { + auto it = std::find_if(pending_connections_.begin(), pending_connections_.end(), + [](const std::unique_ptr &s) { return s->state() == ESTABLISHED; }); + + if (it == pending_connections_.end()) { errno = EWOULDBLOCK; return nullptr; } - auto client = std::move(pending_connections_.front()); - pending_connections_.pop_front(); + auto client = std::move(*it); + pending_connections_.erase(it); if (addr) { *addr = client->remote_addr(); @@ -332,11 +370,19 @@ std::unique_ptr FakeTcpSocket::accept(IP_Port *addr) return client; } -int FakeTcpSocket::send(const uint8_t *buf, size_t len) +int FakeTcpSocket::send(const uint8_t *_Nonnull buf, size_t len) { std::lock_guard lock(mutex_); if (state_ != ESTABLISHED) { - errno = ENOTCONN; + if (universe_.is_verbose()) { + std::cerr << "[FakeTcpSocket] send failed: state " << state_ << " port " << local_port_ + << std::endl; + } + if (state_ == SYN_SENT || state_ == SYN_RECEIVED) { + errno = EWOULDBLOCK; + } else { + errno = ENOTCONN; + } return -1; } @@ -357,7 +403,7 @@ int FakeTcpSocket::send(const uint8_t *buf, size_t len) return len; } -int FakeTcpSocket::recv(uint8_t *buf, size_t len) +int FakeTcpSocket::recv(uint8_t *_Nonnull buf, size_t len) { std::lock_guard lock(mutex_); if (recv_buffer_.empty()) { @@ -368,6 +414,13 @@ int FakeTcpSocket::recv(uint8_t *buf, size_t len) } size_t actual = std::min(len, recv_buffer_.size()); + if (universe_.is_verbose() && actual > 0) { + char remote_ip_str[TOX_INET_ADDRSTRLEN]; + ip_parse_addr(&remote_addr_.ip, remote_ip_str, sizeof(remote_ip_str)); + std::cerr << "[FakeTcpSocket] Port " << local_port_ << " (Peer: " << remote_ip_str << ":" + << net_ntohs(remote_addr_.port) << ") recv requested " << len << " got " << actual + << " (remaining " << recv_buffer_.size() - actual << ")" << std::endl; + } for (size_t i = 0; i < actual; ++i) { buf[i] = recv_buffer_.front(); recv_buffer_.pop_front(); @@ -381,37 +434,109 @@ size_t FakeTcpSocket::recv_buffer_size() return recv_buffer_.size(); } -int FakeTcpSocket::sendto(const uint8_t *buf, size_t len, const IP_Port *addr) +bool FakeTcpSocket::is_readable() +{ + std::lock_guard lock(mutex_); + if (state_ == LISTEN) { + return std::any_of(pending_connections_.begin(), pending_connections_.end(), + [](const std::unique_ptr &s) { return s->state() == ESTABLISHED; }); + } + return !recv_buffer_.empty() || state_ == CLOSED || state_ == CLOSE_WAIT; +} + +bool FakeTcpSocket::is_writable() +{ + std::lock_guard lock(mutex_); + return state_ == ESTABLISHED; +} + +int FakeTcpSocket::sendto(const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr) { errno = EOPNOTSUPP; return -1; } -int FakeTcpSocket::recvfrom(uint8_t *buf, size_t len, IP_Port *addr) +int FakeTcpSocket::recvfrom(uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr) { errno = EOPNOTSUPP; return -1; } -void FakeTcpSocket::handle_packet(const Packet &p) +int FakeTcpSocket::getsockopt( + int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen) +{ + if (universe_.is_verbose()) { + std::cerr << "[FakeTcpSocket] getsockopt level=" << level << " optname=" << optname + << " state=" << state_ << std::endl; + } + if (level == SOL_SOCKET && optname == SO_ERROR) { + int error = 0; + if (state_ == SYN_SENT || state_ == SYN_RECEIVED) { + error = EINPROGRESS; + } else if (state_ == CLOSED) { + error = ECONNREFUSED; + } + + if (*optlen >= sizeof(int)) { + *static_cast(optval) = error; + *optlen = sizeof(int); + } + if (universe_.is_verbose()) { + std::cerr << "[FakeTcpSocket] getsockopt SO_ERROR returning error=" << error + << std::endl; + } + return 0; + } + return 0; +} + +bool FakeTcpSocket::handle_packet(const Packet &p) { std::lock_guard lock(mutex_); if (universe_.is_verbose()) { - std::cerr << "Handle Packet: Port " << local_port_ << " Flags " - << static_cast(p.tcp_flags) << " State " << state_ << std::endl; + char remote_ip_str[TOX_INET_ADDRSTRLEN]; + ip_parse_addr(&remote_addr_.ip, remote_ip_str, sizeof(remote_ip_str)); + std::cerr << "Handle Packet: Port " << local_port_ << " (Peer: " << remote_ip_str << ":" + << net_ntohs(remote_addr_.port) << ") Flags " << TcpFlags{p.tcp_flags} + << " State " << state_ << " From " << net_ntohs(p.from.port) << std::endl; + } + + if (state_ != LISTEN) { + // Filter packets not from our peer + bool port_match = net_ntohs(p.from.port) == net_ntohs(remote_addr_.port); + bool ip_match = ip_equal(&p.from.ip, &remote_addr_.ip) + || (is_loopback(p.from.ip) && ip_equal(&remote_addr_.ip, &ip_)) + || (is_loopback(remote_addr_.ip) && ip_equal(&p.from.ip, &ip_)); + + if (!port_match || !ip_match) { + return false; + } + + if (p.tcp_flags & 0x04) { // RST + state_ = CLOSED; + if (local_port_ != 0) { + universe_.unbind_tcp(ip_, local_port_, this); + local_port_ = 0; + } + return true; + } } if (state_ == LISTEN) { if (p.tcp_flags & 0x02) { // SYN + // Check for duplicate SYN from same peer + for (const auto &pending : pending_connections_) { + if (ipport_equal(&p.from, &pending->remote_addr_)) { + return true; + } + } + // Create new socket for connection auto new_sock = std::make_unique(universe_); - // Bind to ephemeral? No, it's accepted on the same port but distinct 4-tuple. - // In our simplified model, the new socket is not bound to the global map - // until accepted? Or effectively bound to the 4-tuple. - // For now, let's just create it and queue it. new_sock->state_ = SYN_RECEIVED; new_sock->remote_addr_ = p.from; new_sock->local_port_ = local_port_; + new_sock->set_ip(ip_); // Inherit IP from listening socket new_sock->last_ack_ = p.seq + 1; new_sock->next_seq_ = 1000; // Random ISN @@ -428,13 +553,9 @@ void FakeTcpSocket::handle_packet(const Packet &p) universe_.send_packet(resp); - // In real TCP, we wait for ACK to move to ESTABLISHED and accept queue. - // Here we cheat and move to ESTABLISHED immediately or wait for ACK? - // Let's wait for ACK. - // But where do we store this half-open socket? - // For simplicity: auto-transition to ESTABLISHED and queue it. - new_sock->state_ = ESTABLISHED; + // Add to pending, but it's still SYN_RECEIVED pending_connections_.push_back(std::move(new_sock)); + return true; } } else if (state_ == SYN_SENT) { if ((p.tcp_flags & 0x12) == 0x12) { // SYN | ACK @@ -451,8 +572,31 @@ void FakeTcpSocket::handle_packet(const Packet &p) ack.seq = next_seq_; ack.ack = last_ack_; universe_.send_packet(ack); + return true; + } else if (p.tcp_flags & 0x02) { // SYN (Simultaneous Open) + state_ = SYN_RECEIVED; + last_ack_ = p.seq + 1; + + // Send SYN-ACK + Packet resp{}; + resp.from = p.to; + resp.to = p.from; + resp.is_tcp = true; + resp.tcp_flags = 0x12; // SYN | ACK + resp.seq = next_seq_++; + resp.ack = last_ack_; + universe_.send_packet(resp); + return true; } - } else if (state_ == ESTABLISHED) { + } else if (state_ == SYN_RECEIVED) { + if (p.tcp_flags & 0x10) { // ACK + state_ = ESTABLISHED; + } else { + return false; + } + } + + if (state_ == ESTABLISHED) { if (p.tcp_flags & 0x01) { // FIN state_ = CLOSE_WAIT; // Send ACK @@ -464,12 +608,23 @@ void FakeTcpSocket::handle_packet(const Packet &p) ack.seq = next_seq_; ack.ack = p.seq + 1; // Consume FIN universe_.send_packet(ack); - } else if (!p.data.empty()) { - recv_buffer_.insert(recv_buffer_.end(), p.data.begin(), p.data.end()); - last_ack_ += p.data.size(); - // Should send ACK? + return true; + } else { + if (!p.data.empty()) { + if (universe_.is_verbose()) { + char remote_ip_str[TOX_INET_ADDRSTRLEN]; + ip_parse_addr(&remote_addr_.ip, remote_ip_str, sizeof(remote_ip_str)); + std::cerr << "[FakeTcpSocket] Port " << local_port_ + << " (Peer: " << remote_ip_str << ":" << net_ntohs(remote_addr_.port) + << ") adding " << p.data.size() << " bytes to buffer (currently " + << recv_buffer_.size() << ")" << std::endl; + } + recv_buffer_.insert(recv_buffer_.end(), p.data.begin(), p.data.end()); + } + return true; } } + return false; } std::unique_ptr FakeTcpSocket::create_connected( @@ -482,4 +637,23 @@ std::unique_ptr FakeTcpSocket::create_connected( return s; } +std::ostream &operator<<(std::ostream &os, FakeTcpSocket::State state) +{ + switch (state) { + case FakeTcpSocket::CLOSED: + return os << "CLOSED"; + case FakeTcpSocket::LISTEN: + return os << "LISTEN"; + case FakeTcpSocket::SYN_SENT: + return os << "SYN_SENT"; + case FakeTcpSocket::SYN_RECEIVED: + return os << "SYN_RECEIVED"; + case FakeTcpSocket::ESTABLISHED: + return os << "ESTABLISHED"; + case FakeTcpSocket::CLOSE_WAIT: + return os << "CLOSE_WAIT"; + } + return os << "UNKNOWN(" << static_cast(state) << ")"; +} + } // namespace tox::test diff --git a/testing/support/doubles/fake_sockets.hh b/testing/support/doubles/fake_sockets.hh index d42f0b2e..727bdccc 100644 --- a/testing/support/doubles/fake_sockets.hh +++ b/testing/support/doubles/fake_sockets.hh @@ -18,6 +18,7 @@ #include #endif +#include "../../../toxcore/attributes.h" #include "../../../toxcore/network.h" namespace tox::test { @@ -33,21 +34,23 @@ public: FakeSocket(NetworkUniverse &universe, int type); virtual ~FakeSocket(); - virtual int bind(const IP_Port *addr) = 0; - virtual int connect(const IP_Port *addr) = 0; + virtual int bind(const IP_Port *_Nonnull addr) = 0; + virtual int connect(const IP_Port *_Nonnull addr) = 0; virtual int listen(int backlog) = 0; - virtual std::unique_ptr accept(IP_Port *addr) = 0; + virtual std::unique_ptr accept(IP_Port *_Nullable addr) = 0; - virtual int send(const uint8_t *buf, size_t len) = 0; - virtual int recv(uint8_t *buf, size_t len) = 0; + virtual int send(const uint8_t *_Nonnull buf, size_t len) = 0; + virtual int recv(uint8_t *_Nonnull buf, size_t len) = 0; virtual size_t recv_buffer_size() { return 0; } + virtual bool is_readable() { return recv_buffer_size() > 0; } + virtual bool is_writable() { return true; } - virtual int sendto(const uint8_t *buf, size_t len, const IP_Port *addr) = 0; - virtual int recvfrom(uint8_t *buf, size_t len, IP_Port *addr) = 0; + virtual int sendto(const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr) = 0; + virtual int recvfrom(uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr) = 0; - virtual int getsockopt(int level, int optname, void *optval, size_t *optlen); - virtual int setsockopt(int level, int optname, const void *optval, size_t optlen); + virtual int getsockopt(int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen); + virtual int setsockopt(int level, int optname, const void *_Nonnull optval, size_t optlen); virtual int socket_nonblock(bool nonblock); virtual int close(); @@ -76,17 +79,18 @@ public: explicit FakeUdpSocket(NetworkUniverse &universe); ~FakeUdpSocket() override; - int bind(const IP_Port *addr) override; - int connect(const IP_Port *addr) override; + int bind(const IP_Port *_Nonnull addr) override; + int connect(const IP_Port *_Nonnull addr) override; int listen(int backlog) override; - std::unique_ptr accept(IP_Port *addr) override; + std::unique_ptr accept(IP_Port *_Nullable addr) override; int close() override; - int send(const uint8_t *buf, size_t len) override; - int recv(uint8_t *buf, size_t len) override; + int send(const uint8_t *_Nonnull buf, size_t len) override; + int recv(uint8_t *_Nonnull buf, size_t len) override; + size_t recv_buffer_size() override; - int sendto(const uint8_t *buf, size_t len, const IP_Port *addr) override; - int recvfrom(uint8_t *buf, size_t len, IP_Port *addr) override; + int sendto(const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr) override; + int recvfrom(uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr) override; // Called by Universe to deliver a packet void push_packet(std::vector data, IP_Port from); @@ -124,21 +128,25 @@ public: explicit FakeTcpSocket(NetworkUniverse &universe); ~FakeTcpSocket() override; - int bind(const IP_Port *addr) override; - int connect(const IP_Port *addr) override; + int bind(const IP_Port *_Nonnull addr) override; + int connect(const IP_Port *_Nonnull addr) override; int listen(int backlog) override; - std::unique_ptr accept(IP_Port *addr) override; + std::unique_ptr accept(IP_Port *_Nullable addr) override; int close() override; - int send(const uint8_t *buf, size_t len) override; - int recv(uint8_t *buf, size_t len) override; + int send(const uint8_t *_Nonnull buf, size_t len) override; + int recv(uint8_t *_Nonnull buf, size_t len) override; size_t recv_buffer_size() override; + bool is_readable() override; + bool is_writable() override; - int sendto(const uint8_t *buf, size_t len, const IP_Port *addr) override; - int recvfrom(uint8_t *buf, size_t len, IP_Port *addr) override; + int sendto(const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr) override; + int recvfrom(uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr) override; + + int getsockopt(int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen) override; // Internal events - void handle_packet(const Packet &p); + bool handle_packet(const Packet &p); State state() const { return state_; } const IP_Port &remote_addr() const { return remote_addr_; } @@ -160,6 +168,8 @@ private: uint32_t last_ack_ = 0; }; +std::ostream &operator<<(std::ostream &os, FakeTcpSocket::State state); + } // namespace tox::test #endif // C_TOXCORE_TESTING_SUPPORT_DOUBLES_FAKE_SOCKETS_H diff --git a/testing/support/doubles/fake_sockets_test.cc b/testing/support/doubles/fake_sockets_test.cc index a5814968..93c289a9 100644 --- a/testing/support/doubles/fake_sockets_test.cc +++ b/testing/support/doubles/fake_sockets_test.cc @@ -82,6 +82,46 @@ namespace { EXPECT_EQ(std::string(reinterpret_cast(recv_buf), 5), "Hello"); } + TEST_F(FakeTcpSocketTest, RecvBuffering) + { + IP_Port server_addr; + ip_init(&server_addr.ip, false); + server_addr.ip.ip.v4.uint32 = net_htonl(0x7F000001); + server_addr.port = net_htons(8082); + + server.bind(&server_addr); + server.listen(5); + client.connect(&server_addr); + + universe.process_events(0); // SYN + universe.process_events(0); // SYN-ACK + universe.process_events(0); // ACK + + auto accepted = server.accept(nullptr); + ASSERT_NE(accepted, nullptr); + + uint8_t msg1[] = "Part1"; + uint8_t msg2[] = "Part2"; + client.send(msg1, 5); + client.send(msg2, 5); + + universe.process_events(0); // Deliver Part1 + universe.process_events(0); // Deliver Part2 + + EXPECT_EQ(accepted->recv_buffer_size(), 10); + + uint8_t recv_buf[20]; + // Read partial + ASSERT_EQ(accepted->recv(recv_buf, 3), 3); + EXPECT_EQ(std::string(reinterpret_cast(recv_buf), 3), "Par"); + EXPECT_EQ(accepted->recv_buffer_size(), 7); + + // Read rest + ASSERT_EQ(accepted->recv(recv_buf, 7), 7); + EXPECT_EQ(std::string(reinterpret_cast(recv_buf), 7), "t1Part2"); + EXPECT_EQ(accepted->recv_buffer_size(), 0); + } + class FakeUdpSocketTest : public ::testing::Test { public: ~FakeUdpSocketTest() override; @@ -119,6 +159,40 @@ namespace { EXPECT_EQ(sender_addr.port, net_htons(client.local_port())); } + TEST_F(FakeUdpSocketTest, RecvBuffering) + { + IP_Port server_addr; + ip_init(&server_addr.ip, false); + server_addr.ip.ip.v4.uint32 = net_htonl(0x7F000001); + server_addr.port = net_htons(9001); + + server.bind(&server_addr); + + const char *msg1 = "Msg1"; + const char *msg2 = "Msg2"; + + client.sendto(reinterpret_cast(msg1), strlen(msg1), &server_addr); + client.sendto(reinterpret_cast(msg2), strlen(msg2), &server_addr); + + universe.process_events(0); // Deliver msg1 + universe.process_events(0); // Deliver msg2 + + EXPECT_EQ(server.recv_buffer_size(), 2); + + IP_Port sender; + uint8_t buf[10]; + + int len = server.recvfrom(buf, sizeof(buf), &sender); + ASSERT_EQ(len, 4); + EXPECT_EQ(std::string(reinterpret_cast(buf), len), "Msg1"); + EXPECT_EQ(server.recv_buffer_size(), 1); + + len = server.recvfrom(buf, sizeof(buf), &sender); + ASSERT_EQ(len, 4); + EXPECT_EQ(std::string(reinterpret_cast(buf), len), "Msg2"); + EXPECT_EQ(server.recv_buffer_size(), 0); + } + } // namespace } // namespace tox::test diff --git a/testing/support/doubles/network_universe.cc b/testing/support/doubles/network_universe.cc index 5750c804..63e8752a 100644 --- a/testing/support/doubles/network_universe.cc +++ b/testing/support/doubles/network_universe.cc @@ -7,6 +7,30 @@ namespace tox::test { +std::ostream &operator<<(std::ostream &os, TcpFlags flags) +{ + bool first = true; + if (flags.value & 0x02) { + os << (first ? "" : "|") << "SYN"; + first = false; + } + if (flags.value & 0x10) { + os << (first ? "" : "|") << "ACK"; + first = false; + } + if (flags.value & 0x01) { + os << (first ? "" : "|") << "FIN"; + first = false; + } + if (flags.value & 0x04) { + os << (first ? "" : "|") << "RST"; + first = false; + } + if (first) + os << "NONE"; + return os << "(" << static_cast(flags.value) << ")"; +} + bool NetworkUniverse::IP_Port_Key::operator<(const IP_Port_Key &other) const { if (port != other.port) @@ -24,7 +48,7 @@ bool NetworkUniverse::IP_Port_Key::operator<(const IP_Port_Key &other) const NetworkUniverse::NetworkUniverse() { } NetworkUniverse::~NetworkUniverse() { } -bool NetworkUniverse::bind_udp(IP ip, uint16_t port, FakeUdpSocket *socket) +bool NetworkUniverse::bind_udp(IP ip, uint16_t port, FakeUdpSocket *_Nonnull socket) { std::lock_guard lock(mutex_); IP_Port_Key key{ip, port}; @@ -40,14 +64,14 @@ void NetworkUniverse::unbind_udp(IP ip, uint16_t port) udp_bindings_.erase({ip, port}); } -bool NetworkUniverse::bind_tcp(IP ip, uint16_t port, FakeTcpSocket *socket) +bool NetworkUniverse::bind_tcp(IP ip, uint16_t port, FakeTcpSocket *_Nonnull socket) { std::lock_guard lock(mutex_); tcp_bindings_.insert({{ip, port}, socket}); return true; } -void NetworkUniverse::unbind_tcp(IP ip, uint16_t port, FakeTcpSocket *socket) +void NetworkUniverse::unbind_tcp(IP ip, uint16_t port, FakeTcpSocket *_Nonnull socket) { std::lock_guard lock(mutex_); auto range = tcp_bindings_.equal_range({ip, port}); @@ -75,9 +99,64 @@ void NetworkUniverse::send_packet(Packet p) p.delivery_time += global_latency_ms_; std::lock_guard lock(mutex_); + p.sequence_number = next_packet_id_++; + + if (verbose_) { + Ip_Ntoa from_str, to_str; + net_ip_ntoa(&p.from.ip, &from_str); + net_ip_ntoa(&p.to.ip, &to_str); + std::cerr << "[NetworkUniverse] Enqueued packet #" << p.sequence_number << " from " + << from_str.buf << ":" << net_ntohs(p.from.port) << " to " << to_str.buf << ":" + << net_ntohs(p.to.port); + if (p.is_tcp) { + std::cerr << " (TCP Flags=" << TcpFlags{p.tcp_flags} << " Seq=" << p.seq + << " Ack=" << p.ack << ")"; + } + std::cerr << " with size " << p.data.size() << std::endl; + } + event_queue_.push(std::move(p)); } +static bool is_ipv4_mapped(const IP &ip) +{ + if (!net_family_is_ipv6(ip.family)) + return false; + const uint8_t *b = ip.ip.v6.uint8; + for (int i = 0; i < 10; ++i) + if (b[i] != 0) + return false; + if (b[10] != 0xFF || b[11] != 0xFF) + return false; + return true; +} + +static IP extract_ipv4(const IP &ip) +{ + IP ip4; + ip_init(&ip4, false); + const uint8_t *b = ip.ip.v6.uint8; + std::memcpy(ip4.ip.v4.uint8, b + 12, 4); + return ip4; +} + +bool is_loopback(const IP &ip) +{ + if (net_family_is_ipv4(ip.family)) { + return ip.ip.v4.uint32 == net_htonl(0x7F000001); + } + if (net_family_is_ipv6(ip.family)) { + const uint8_t *b = ip.ip.v6.uint8; + for (int i = 0; i < 15; ++i) { + if (b[i] != 0) { + return false; + } + } + return b[15] == 1; + } + return false; +} + void NetworkUniverse::process_events(uint64_t current_time_ms) { while (true) { @@ -88,19 +167,101 @@ void NetworkUniverse::process_events(uint64_t current_time_ms) { std::lock_guard lock(mutex_); + if (!event_queue_.empty()) { + const Packet &top = event_queue_.top(); + if (verbose_) { + std::cerr << "[NetworkUniverse] Peek packet: time=" << top.delivery_time + << " current=" << current_time_ms << " tcp=" << top.is_tcp + << std::endl; + } + } + if (!event_queue_.empty() && event_queue_.top().delivery_time <= current_time_ms) { p = event_queue_.top(); event_queue_.pop(); has_packet = true; + if (verbose_) { + Ip_Ntoa from_str, to_str; + net_ip_ntoa(&p.from.ip, &from_str); + net_ip_ntoa(&p.to.ip, &to_str); + std::cerr << "[NetworkUniverse] Processing packet #" << p.sequence_number + << " from " << from_str.buf << ":" << net_ntohs(p.from.port) << " to " + << to_str.buf << ":" << net_ntohs(p.to.port) + << " (TCP=" << (p.is_tcp ? "true" : "false"); + if (p.is_tcp) { + std::cerr << " Flags=" << TcpFlags{p.tcp_flags} << " Seq=" << p.seq + << " Ack=" << p.ack; + } + std::cerr << " Size=" << p.data.size() << ")" << std::endl; + } + + IP target_ip = p.to.ip; + if (p.is_tcp) { - auto range = tcp_bindings_.equal_range({p.to.ip, net_ntohs(p.to.port)}); + if (is_loopback(target_ip) + && tcp_bindings_.count({target_ip, net_ntohs(p.to.port)}) == 0) { + if (verbose_) { + std::cerr << "[NetworkUniverse] Loopback packet to " + << static_cast(target_ip.ip.v4.uint8[3]) + << " redirected to " + << static_cast(p.from.ip.ip.v4.uint8[3]) << std::endl; + } + target_ip = p.from.ip; + } + + auto range = tcp_bindings_.equal_range({target_ip, net_ntohs(p.to.port)}); + FakeTcpSocket *listen_match = nullptr; + for (auto it = range.first; it != range.second; ++it) { - tcp_targets.push_back(it->second); + FakeTcpSocket *s = it->second; + if (s->state() == FakeTcpSocket::LISTEN) { + listen_match = s; + } else { + const IP_Port &remote = s->remote_addr(); + if (net_ntohs(p.from.port) == net_ntohs(remote.port)) { + if (ip_equal(&p.from.ip, &remote.ip) + || (is_loopback(p.from.ip) && ip_equal(&remote.ip, &target_ip)) + || (is_loopback(remote.ip) + && ip_equal(&p.from.ip, &target_ip))) { + tcp_targets.push_back(s); + } + } + } + } + + if (listen_match && (p.tcp_flags & 0x02)) { + tcp_targets.push_back(listen_match); + } + + if (verbose_) { + std::cerr << "[NetworkUniverse] Routing TCP to " + << static_cast(target_ip.ip.v4.uint8[0]) << "." + << static_cast(target_ip.ip.v4.uint8[3]) << ":" + << net_ntohs(p.to.port) + << ". Targets found: " << tcp_targets.size() << std::endl; + } + if (tcp_targets.empty()) { + if (verbose_) { + std::cerr << "[NetworkUniverse] WARNING: No TCP targets for " + << static_cast(target_ip.ip.v4.uint8[0]) << "." + << static_cast(target_ip.ip.v4.uint8[3]) << ":" + << net_ntohs(p.to.port) << std::endl; + } } } else { - if (udp_bindings_.count({p.to.ip, net_ntohs(p.to.port)})) { - udp_target = udp_bindings_[{p.to.ip, net_ntohs(p.to.port)}]; + if (is_loopback(target_ip) + && udp_bindings_.count({target_ip, net_ntohs(p.to.port)}) == 0) { + target_ip = p.from.ip; + } + + if (udp_bindings_.count({target_ip, net_ntohs(p.to.port)})) { + udp_target = udp_bindings_[{target_ip, net_ntohs(p.to.port)}]; + } else if (is_ipv4_mapped(target_ip)) { + IP ip4 = extract_ipv4(target_ip); + if (udp_bindings_.count({ip4, net_ntohs(p.to.port)})) { + udp_target = udp_bindings_[{ip4, net_ntohs(p.to.port)}]; + } } } } @@ -112,7 +273,9 @@ void NetworkUniverse::process_events(uint64_t current_time_ms) if (p.is_tcp) { for (auto *it : tcp_targets) { - it->handle_packet(p); + if (it->handle_packet(p)) { + break; + } } } else { if (udp_target) { @@ -136,7 +299,7 @@ uint16_t NetworkUniverse::find_free_port(IP ip, uint16_t start) { std::lock_guard lock(mutex_); for (uint16_t port = start; port < 65535; ++port) { - if (!udp_bindings_.count({ip, port})) + if (!udp_bindings_.count({ip, port}) && !tcp_bindings_.count({ip, port})) return port; } return 0; diff --git a/testing/support/doubles/network_universe.hh b/testing/support/doubles/network_universe.hh index cf1d6f3c..73c85466 100644 --- a/testing/support/doubles/network_universe.hh +++ b/testing/support/doubles/network_universe.hh @@ -9,6 +9,7 @@ #include #include +#include "../../../toxcore/attributes.h" #include "../../../toxcore/network.h" namespace tox::test { @@ -16,11 +17,17 @@ namespace tox::test { class FakeUdpSocket; class FakeTcpSocket; +struct TcpFlags { + uint8_t value; +}; +std::ostream &operator<<(std::ostream &os, TcpFlags flags); + struct Packet { IP_Port from; IP_Port to; std::vector data; uint64_t delivery_time; + uint64_t sequence_number = 0; bool is_tcp = false; // TCP Simulation Fields @@ -28,9 +35,17 @@ struct Packet { uint32_t seq = 0; uint32_t ack = 0; - bool operator>(const Packet &other) const { return delivery_time > other.delivery_time; } + bool operator>(const Packet &other) const + { + if (delivery_time != other.delivery_time) { + return delivery_time > other.delivery_time; + } + return sequence_number > other.sequence_number; + } }; +bool is_loopback(const IP &ip); + /** * @brief The God Object for the network simulation. * Manages routing, latency, and connectivity. @@ -45,11 +60,11 @@ public: // Registration // Returns true if binding succeeded - bool bind_udp(IP ip, uint16_t port, FakeUdpSocket *socket); + bool bind_udp(IP ip, uint16_t port, FakeUdpSocket *_Nonnull socket); void unbind_udp(IP ip, uint16_t port); - bool bind_tcp(IP ip, uint16_t port, FakeTcpSocket *socket); - void unbind_tcp(IP ip, uint16_t port, FakeTcpSocket *socket); + bool bind_tcp(IP ip, uint16_t port, FakeTcpSocket *_Nonnull socket); + void unbind_tcp(IP ip, uint16_t port, FakeTcpSocket *_Nonnull socket); // Routing void send_packet(Packet p); @@ -81,6 +96,7 @@ private: std::vector observers_; uint64_t global_latency_ms_ = 0; + uint64_t next_packet_id_ = 0; bool verbose_ = false; std::recursive_mutex mutex_; }; diff --git a/testing/support/doubles/network_universe_test.cc b/testing/support/doubles/network_universe_test.cc index 494f938b..e29ebca6 100644 --- a/testing/support/doubles/network_universe_test.cc +++ b/testing/support/doubles/network_universe_test.cc @@ -236,5 +236,86 @@ namespace { std::string(reinterpret_cast(buf), static_cast(len)), "Padding test"); } + TEST_F(NetworkUniverseTest, TcpRoutingSpecificity) + { + IP ip1{}; + ip_init(&ip1, false); + ip1.ip.v4.uint32 = net_htonl(0x0A000001); // 10.0.0.1 + + uint16_t port = 12345; + IP_Port local_addr{ip1, net_htons(port)}; + + FakeTcpSocket listen_sock(universe); + listen_sock.set_ip(ip1); + listen_sock.bind(&local_addr); + listen_sock.listen(5); + + IP remote_ip{}; + ip_init(&remote_ip, false); + remote_ip.ip.v4.uint32 = net_htonl(0x0A000002); // 10.0.0.2 + IP_Port remote_addr{remote_ip, net_htons(33445)}; + + auto established_sock = FakeTcpSocket::create_connected(universe, remote_addr, port); + established_sock->set_ip(ip1); + universe.bind_tcp(ip1, port, established_sock.get()); + + // Send a data packet from remote to local + Packet p{}; + p.from = remote_addr; + p.to = local_addr; + p.is_tcp = true; + p.tcp_flags = 0x10; // ACK (Data) + const char *data = "Specific"; + p.data.assign(data, data + strlen(data)); + + universe.send_packet(p); + universe.process_events(0); + + // established_sock should have received it + EXPECT_EQ(established_sock->recv_buffer_size(), strlen(data)); + + // listen_sock should NOT have received it (it doesn't have a buffer, but it shouldn't have + // been called) We can't easily check listen_sock wasn't called without mocks or checking + // logs, but we can check that it didn't create a new pending connection. + EXPECT_FALSE(listen_sock.is_readable()); + } + + TEST_F(NetworkUniverseTest, PacketOrdering) + { + IP ip1{}, ip2{}; + ip_init(&ip1, false); + ip1.ip.v4.uint32 = net_htonl(0x0A000001); + ip_init(&ip2, false); + ip2.ip.v4.uint32 = net_htonl(0x0A000002); + + uint16_t port = 33445; + IP_Port addr1{ip1, net_htons(port)}; + IP_Port addr2{ip2, net_htons(port)}; + + FakeUdpSocket sock1{universe}; + sock1.set_ip(ip1); + sock1.bind(&addr1); + + FakeUdpSocket sock2{universe}; + sock2.set_ip(ip2); + sock2.bind(&addr2); + + // Send 10 packets with the same delivery time (global latency = 0) + for (int i = 0; i < 10; ++i) { + uint8_t data = static_cast(i); + sock1.sendto(&data, 1, &addr2); + } + + universe.process_events(0); + + // They should be received in the exact order they were sent + for (int i = 0; i < 10; ++i) { + uint8_t buf[1]; + IP_Port from; + ASSERT_EQ(sock2.recvfrom(buf, 1, &from), 1); + EXPECT_EQ(buf[0], i) << "Packet " << i << " was delivered out of order"; + } + } + } // namespace } // namespace tox::test diff --git a/testing/support/public/fuzz_data.hh b/testing/support/public/fuzz_data.hh index f3983fae..3b9cad78 100644 --- a/testing/support/public/fuzz_data.hh +++ b/testing/support/public/fuzz_data.hh @@ -7,6 +7,8 @@ #include #include +#include "../../../toxcore/attributes.h" + namespace tox::test { struct Fuzz_Data { @@ -14,12 +16,12 @@ struct Fuzz_Data { static constexpr std::size_t TRACE_TRAP = -1; private: - const uint8_t *data_; - const uint8_t *base_; + const uint8_t *_Nonnull data_; + const uint8_t *_Nonnull base_; std::size_t size_; public: - Fuzz_Data(const uint8_t *input_data, std::size_t input_size) + Fuzz_Data(const uint8_t *_Nonnull input_data, std::size_t input_size) : data_(input_data) , base_(input_data) , size_(input_size) @@ -30,7 +32,7 @@ public: Fuzz_Data(const Fuzz_Data &rhs) = delete; struct Consumer { - const char *func; + const char *_Nonnull func; Fuzz_Data &fd; operator bool() @@ -51,14 +53,14 @@ public: { if (sizeof(T) > fd.size()) return T{}; - const uint8_t *bytes = fd.consume(func, sizeof(T)); + const uint8_t *_Nonnull bytes = fd.consume(func, sizeof(T)); T val; std::memcpy(&val, bytes, sizeof(T)); return val; } }; - Consumer consume1(const char *func) { return Consumer{func, *this}; } + Consumer consume1(const char *_Nonnull func) { return Consumer{func, *this}; } template T consume_integral() @@ -81,7 +83,7 @@ public: { if (count == 0 || count > size_) return {}; - const uint8_t *start = consume("consume_bytes", count); + const uint8_t *_Nullable start = consume("consume_bytes", count); if (!start) return {}; return std::vector(start, start + count); @@ -92,20 +94,20 @@ public: if (empty()) return {}; std::size_t count = size(); - const uint8_t *start = consume("consume_remaining_bytes", count); + const uint8_t *_Nonnull start = consume("consume_remaining_bytes", count); return std::vector(start, start + count); } std::size_t size() const { return size_; } std::size_t pos() const { return data_ - base_; } - const uint8_t *data() const { return data_; } + const uint8_t *_Nonnull data() const { return data_; } bool empty() const { return size_ == 0; } - const uint8_t *consume(const char *func, std::size_t count) + const uint8_t *_Nullable consume(const char *_Nonnull func, std::size_t count) { if (count > size_) return nullptr; - const uint8_t *val = data_; + const uint8_t *_Nonnull val = data_; if (FUZZ_DEBUG) { if (count == 1) { std::printf("consume@%zu(%s): %d (0x%02x)\n", pos(), func, val[0], val[0]); @@ -170,7 +172,7 @@ struct Fuzz_Target_Selector<> { }; template -void fuzz_select_target(const uint8_t *data, std::size_t size) +void fuzz_select_target(const uint8_t *_Nonnull data, std::size_t size) { Fuzz_Data input{data, size}; diff --git a/testing/support/public/memory.hh b/testing/support/public/memory.hh index 4ffd0c46..f8f4e89f 100644 --- a/testing/support/public/memory.hh +++ b/testing/support/public/memory.hh @@ -4,6 +4,11 @@ #include #include +#include "../../../toxcore/attributes.h" + +// Forward declaration +struct Memory; + namespace tox::test { /** @@ -13,9 +18,14 @@ class MemorySystem { public: virtual ~MemorySystem(); - virtual void *malloc(size_t size) = 0; - virtual void *realloc(void *ptr, size_t size) = 0; - virtual void free(void *ptr) = 0; + virtual void *_Nullable malloc(size_t size) = 0; + virtual void *_Nullable realloc(void *_Nullable ptr, size_t size) = 0; + virtual void free(void *_Nullable ptr) = 0; + + /** + * @brief Returns C-compatible Memory struct. + */ + virtual struct Memory c_memory() = 0; }; } // namespace tox::test diff --git a/testing/support/public/mpsc_queue.hh b/testing/support/public/mpsc_queue.hh new file mode 100644 index 00000000..fb0130af --- /dev/null +++ b/testing/support/public/mpsc_queue.hh @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ +#ifndef C_TOXCORE_TESTING_SUPPORT_MPSC_QUEUE_H +#define C_TOXCORE_TESTING_SUPPORT_MPSC_QUEUE_H + +#include +#include +#include + +namespace tox::test { + +/** + * @brief Multiple Producer, Single Consumer Queue. + * + * This queue implementation provides thread-safe access for multiple producers + * pushing items and a single consumer popping items. It uses a `std::mutex` + * and `std::condition_variable` for synchronization. + * + * @tparam T The type of elements stored in the queue. + */ +template +class MpscQueue { +public: + MpscQueue() = default; + ~MpscQueue() = default; + + // Disable copy/move to prevent accidental sharing/slicing issues + MpscQueue(const MpscQueue &) = delete; + MpscQueue &operator=(const MpscQueue &) = delete; + + /** + * @brief Pushes a value onto the queue. + * Thread-safe (Multiple Producers). + */ + void push(T value) + { + { + std::lock_guard lock(mutex_); + queue_.push_back(std::move(value)); + } + cv_.notify_one(); + } + + /** + * @brief Pops a value from the queue, blocking if empty. + * Thread-safe (Single Consumer). + */ + T pop() + { + std::unique_lock lock(mutex_); + cv_.wait(lock, [this] { return !queue_.empty(); }); + T value = std::move(queue_.front()); + queue_.pop_front(); + return value; + } + + /** + * @brief Tries to pop a value from the queue without blocking. + * Thread-safe (Single Consumer). + * + * @param out Reference to store the popped value. + * @return true if a value was popped, false if the queue was empty. + */ + bool try_pop(T &out) + { + std::lock_guard lock(mutex_); + if (queue_.empty()) + return false; + out = std::move(queue_.front()); + queue_.pop_front(); + return true; + } + +private: + std::deque queue_; + std::mutex mutex_; + std::condition_variable cv_; +}; + +} // namespace tox::test + +#endif // C_TOXCORE_TESTING_SUPPORT_MPSC_QUEUE_H diff --git a/testing/support/public/network.hh b/testing/support/public/network.hh index 179b9341..84869126 100644 --- a/testing/support/public/network.hh +++ b/testing/support/public/network.hh @@ -4,6 +4,8 @@ #include #include +#include "../../../toxcore/attributes.h" +#include "../../../toxcore/net.h" #include "../../../toxcore/network.h" namespace tox::test { @@ -16,24 +18,35 @@ public: virtual ~NetworkSystem(); virtual Socket socket(int domain, int type, int protocol) = 0; - virtual int bind(Socket sock, const IP_Port *addr) = 0; + virtual int bind(Socket sock, const IP_Port *_Nonnull addr) = 0; virtual int close(Socket sock) = 0; - virtual int sendto(Socket sock, const uint8_t *buf, size_t len, const IP_Port *addr) = 0; - virtual int recvfrom(Socket sock, uint8_t *buf, size_t len, IP_Port *addr) = 0; + virtual int sendto( + Socket sock, const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr) + = 0; + virtual int recvfrom(Socket sock, uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr) + = 0; // TCP Support virtual int listen(Socket sock, int backlog) = 0; virtual Socket accept(Socket sock) = 0; - virtual int connect(Socket sock, const IP_Port *addr) = 0; - virtual int send(Socket sock, const uint8_t *buf, size_t len) = 0; - virtual int recv(Socket sock, uint8_t *buf, size_t len) = 0; + virtual int connect(Socket sock, const IP_Port *_Nonnull addr) = 0; + virtual int send(Socket sock, const uint8_t *_Nonnull buf, size_t len) = 0; + virtual int recv(Socket sock, uint8_t *_Nonnull buf, size_t len) = 0; virtual int recvbuf(Socket sock) = 0; // Auxiliary virtual int socket_nonblock(Socket sock, bool nonblock) = 0; - virtual int getsockopt(Socket sock, int level, int optname, void *optval, size_t *optlen) = 0; - virtual int setsockopt(Socket sock, int level, int optname, const void *optval, size_t optlen) + virtual int getsockopt( + Socket sock, int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen) = 0; + virtual int setsockopt( + Socket sock, int level, int optname, const void *_Nonnull optval, size_t optlen) + = 0; + + /** + * @brief Returns C-compatible Network struct. + */ + virtual struct Network c_network() = 0; }; /** diff --git a/testing/support/public/random.hh b/testing/support/public/random.hh index 72d0029c..ed3dcd28 100644 --- a/testing/support/public/random.hh +++ b/testing/support/public/random.hh @@ -4,6 +4,11 @@ #include #include +#include "../../../toxcore/attributes.h" + +// Forward declaration +struct Random; + namespace tox::test { /** @@ -14,7 +19,12 @@ public: virtual ~RandomSystem(); virtual uint32_t uniform(uint32_t upper_bound) = 0; - virtual void bytes(uint8_t *out, size_t count) = 0; + virtual void bytes(uint8_t *_Nonnull out, size_t count) = 0; + + /** + * @brief Returns C-compatible Random struct. + */ + virtual struct Random c_random() = 0; }; } // namespace tox::test diff --git a/testing/support/public/simulated_environment.hh b/testing/support/public/simulated_environment.hh index e430fe34..5e19e988 100644 --- a/testing/support/public/simulated_environment.hh +++ b/testing/support/public/simulated_environment.hh @@ -13,9 +13,10 @@ #include #endif -#include "../../../toxcore/tox_memory_impl.h" +#include "../../../toxcore/attributes.h" +#include "../../../toxcore/mem.h" +#include "../../../toxcore/rng.h" #include "../../../toxcore/tox_private.h" -#include "../../../toxcore/tox_random_impl.h" #include "../doubles/fake_clock.hh" #include "../doubles/fake_memory.hh" #include "../doubles/fake_random.hh" @@ -29,12 +30,12 @@ struct ScopedToxSystem { std::unique_ptr node; // Direct access to primary socket (for fuzzer injection) - FakeUdpSocket *endpoint; + FakeUdpSocket *_Nullable endpoint; // C structs struct Network c_network; - struct Tox_Random c_random; - struct Tox_Memory c_memory; + struct Random c_random; + struct Memory c_memory; // The main struct passed to tox_new Tox_System system; diff --git a/testing/support/public/simulation.hh b/testing/support/public/simulation.hh index 63cbdab4..4d0269b4 100644 --- a/testing/support/public/simulation.hh +++ b/testing/support/public/simulation.hh @@ -1,6 +1,8 @@ #ifndef C_TOXCORE_TESTING_SUPPORT_SIMULATION_H #define C_TOXCORE_TESTING_SUPPORT_SIMULATION_H +#include +#include #include #include #include @@ -14,10 +16,13 @@ #include #endif +#include + +#include "../../../toxcore/attributes.h" +#include "../../../toxcore/mem.h" +#include "../../../toxcore/rng.h" #include "../../../toxcore/tox.h" -#include "../../../toxcore/tox_memory_impl.h" #include "../../../toxcore/tox_private.h" -#include "../../../toxcore/tox_random_impl.h" #include "../doubles/fake_clock.hh" #include "../doubles/fake_memory.hh" #include "../doubles/fake_network_stack.hh" @@ -29,12 +34,61 @@ namespace tox::test { class SimulatedNode; +struct LogMetadata { + Tox_Log_Level level; + const char *_Nonnull file; + uint32_t line; + const char *_Nonnull func; + const char *_Nonnull message; + uint32_t node_id; +}; + +using LogPredicate = std::function; + +struct LogFilter { + LogPredicate pred; + + LogFilter() = default; + explicit LogFilter(LogPredicate p) + : pred(std::move(p)) + { + } + + bool operator()(const LogMetadata &md) const { return !pred || pred(md); } +}; + +LogFilter operator&&(const LogFilter &lhs, const LogFilter &rhs); +LogFilter operator||(const LogFilter &lhs, const LogFilter &rhs); +LogFilter operator!(const LogFilter &target); + +namespace log_filter { + LogFilter level(Tox_Log_Level min_level); + + struct LevelPlaceholder { + LogFilter operator>(Tox_Log_Level rhs) const; + LogFilter operator>=(Tox_Log_Level rhs) const; + LogFilter operator<(Tox_Log_Level rhs) const; + LogFilter operator<=(Tox_Log_Level rhs) const; + LogFilter operator==(Tox_Log_Level rhs) const; + LogFilter operator!=(Tox_Log_Level rhs) const; + }; + + LevelPlaceholder level(); + + LogFilter file(std::string pattern); + LogFilter func(std::string pattern); + LogFilter message(std::string pattern); + LogFilter node(uint32_t id); +} // namespace log_filter + /** * @brief The Simulation World. * Holds the Clock and the Universe. */ class Simulation { public: + static constexpr uint32_t kDefaultTickIntervalMs = 50; + Simulation(); ~Simulation(); @@ -42,9 +96,66 @@ public: void advance_time(uint64_t ms); void run_until(std::function condition, uint64_t timeout_ms = 5000); + // Logging + void set_log_filter(LogFilter filter); + const LogFilter &log_filter() const { return log_filter_; } + + // Synchronization Barrier + // These methods coordinate the lock-step execution of multiple Tox runners. + + /** + * @brief Registers a new runner with the simulation barrier. + * @return The current generation ID of the simulation. + */ + uint64_t register_runner(); + + /** + * @brief Unregisters a runner from the simulation barrier. + * + * This ensures the simulation does not block waiting for a terminated runner. + */ + void unregister_runner(); + + using TickListenerId = int; + + /** + * @brief Registers a callback to be invoked when a new simulation tick starts. + * + * @param listener The function to call with the new generation ID. + * @return An ID handle for unregistering the listener. + */ + TickListenerId register_tick_listener(std::function listener); + + /** + * @brief Unregisters a tick listener. + */ + void unregister_tick_listener(TickListenerId id); + + /** + * @brief Blocks until the simulation advances to the next tick. + * + * Called by runner threads to wait for the global clock to advance. + * + * @param last_gen The generation ID of the last processed tick. + * @param stop_token Atomic flag to signal termination while waiting. + * @param timeout_ms Maximum time to wait for the tick. + * @return The new generation ID, or `last_gen` on timeout/stop. + */ + uint64_t wait_for_tick( + uint64_t last_gen, const std::atomic &stop_token, uint64_t timeout_ms = 10); + + /** + * @brief Signals that a runner has completed its work for the current tick. + * + * @param next_delay_ms The requested delay until the next tick (from `tox_iteration_interval`). + */ + void tick_complete(uint32_t next_delay_ms = kDefaultTickIntervalMs); + // Global Access FakeClock &clock() { return *clock_; } + const FakeClock &clock() const { return *clock_; } NetworkUniverse &net() { return *net_; } + const NetworkUniverse &net() const { return *net_; } // Node Factory std::unique_ptr create_node(); @@ -52,7 +163,23 @@ public: private: std::unique_ptr clock_; std::unique_ptr net_; + LogFilter log_filter_; uint32_t node_count_ = 0; + + // Barrier State + std::mutex barrier_mutex_; + std::condition_variable barrier_cv_; + uint64_t current_generation_ = 0; + int registered_runners_ = 0; + std::atomic active_runners_{0}; + std::atomic next_step_min_{kDefaultTickIntervalMs}; + + struct TickListener { + TickListenerId id; + std::function callback; + }; + std::vector tick_listeners_; + TickListenerId next_listener_id_ = 0; }; /** @@ -79,19 +206,16 @@ public: // Returns a configured Tox instance bound to this node's environment. // The user owns the Tox instance. struct ToxDeleter { - void operator()(Tox *t) const { tox_kill(t); } + void operator()(Tox *_Nonnull t) const { tox_kill(t); } }; using ToxPtr = std::unique_ptr; - ToxPtr create_tox(const Tox_Options *options = nullptr); + ToxPtr create_tox(const Tox_Options *_Nullable options = nullptr); - // Helper to get C structs for manual injection - struct Network get_c_network() { return network_->get_c_network(); } - struct Tox_Random get_c_random() { return random_->get_c_random(); } - struct Tox_Memory get_c_memory() { return memory_->get_c_memory(); } + Simulation &simulation() { return sim_; } // For fuzzing compatibility (exposes first bound UDP socket as "endpoint") - FakeUdpSocket *get_primary_socket(); + FakeUdpSocket *_Nullable get_primary_socket(); private: Simulation &sim_; @@ -102,8 +226,8 @@ private: // C-compatible views (must stay valid for the lifetime of Tox) public: struct Network c_network; - struct Tox_Random c_random; - struct Tox_Memory c_memory; + struct Random c_random; + struct Memory c_memory; struct IP ip; }; diff --git a/testing/support/public/tox_network.hh b/testing/support/public/tox_network.hh index f799c09e..5e638cff 100644 --- a/testing/support/public/tox_network.hh +++ b/testing/support/public/tox_network.hh @@ -5,21 +5,25 @@ #ifndef C_TOXCORE_TESTING_SUPPORT_TOX_NETWORK_H #define C_TOXCORE_TESTING_SUPPORT_TOX_NETWORK_H +#include +#include #include +#include "../../../toxcore/attributes.h" #include "simulation.hh" +#include "tox_runner.hh" namespace tox::test { struct ConnectedFriend { std::unique_ptr node; - SimulatedNode::ToxPtr tox; + std::unique_ptr runner; uint32_t friend_number; - ConnectedFriend(std::unique_ptr node_in, SimulatedNode::ToxPtr tox_in, + ConnectedFriend(std::unique_ptr node_in, std::unique_ptr runner_in, uint32_t friend_number_in) : node(std::move(node_in)) - , tox(std::move(tox_in)) + , runner(std::move(runner_in)) , friend_number(friend_number_in) { } @@ -43,8 +47,9 @@ struct ConnectedFriend { * @param options Optional Tox_Options to use for the friend Tox instances. * @return A vector of ConnectedFriend structures, each representing a friend. */ -std::vector setup_connected_friends(Simulation &sim, Tox *main_tox, - SimulatedNode &main_node, int num_friends, const Tox_Options *options = nullptr); +std::vector setup_connected_friends(Simulation &sim, Tox *_Nonnull main_tox, + SimulatedNode &main_node, int num_friends, const Tox_Options *_Nullable options = nullptr, + bool verbose = false); /** * @brief Connects two existing Tox instances as friends. @@ -59,8 +64,8 @@ std::vector setup_connected_friends(Simulation &sim, Tox *main_ * @param tox2 The second Tox instance. * @return True if connected successfully, false otherwise. */ -bool connect_friends( - Simulation &sim, SimulatedNode &node1, Tox *tox1, SimulatedNode &node2, Tox *tox2); +bool connect_friends(Simulation &sim, SimulatedNode &node1, Tox *_Nonnull tox1, + SimulatedNode &node2, Tox *_Nonnull tox2); /** * @brief Sets up a group and has all friends join it. @@ -75,7 +80,7 @@ bool connect_friends( * @return The group number on the main Tox instance, or UINT32_MAX on failure. */ uint32_t setup_connected_group( - Simulation &sim, Tox *main_tox, const std::vector &friends); + Simulation &sim, Tox *_Nonnull main_tox, const std::vector &friends); } // namespace tox::test diff --git a/testing/support/public/tox_runner.hh b/testing/support/public/tox_runner.hh new file mode 100644 index 00000000..21dd90de --- /dev/null +++ b/testing/support/public/tox_runner.hh @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#ifndef C_TOXCORE_TESTING_SUPPORT_TOX_RUNNER_H +#define C_TOXCORE_TESTING_SUPPORT_TOX_RUNNER_H + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../toxcore/attributes.h" +#include "../../../toxcore/tox_events.h" +#include "mpsc_queue.hh" +#include "simulation.hh" + +namespace tox::test { + +class ToxRunner { +public: + explicit ToxRunner(SimulatedNode &node, const Tox_Options *_Nullable options = nullptr); + ~ToxRunner(); + + ToxRunner(const ToxRunner &) = delete; + ToxRunner &operator=(const ToxRunner &) = delete; + + struct ToxEventsDeleter { + void operator()(Tox_Events *_Nonnull e) const { tox_events_free(e); } + }; + using ToxEventsPtr = std::unique_ptr; + + /** + * @brief Schedules a task for execution on the runner's thread. + * + * This method is thread-safe and non-blocking. The task is queued and will + * be executed during the runner's event loop cycle. + * + * @param task The function to execute, taking a raw Tox pointer. + */ + void execute(std::function task); + + /** + * @brief Executes a task on the runner's thread and waits for the result. + * + * This method blocks the calling thread until the task has been executed + * by the runner. It automatically handles return value propagation and + * exception safety (though exceptions are not currently propagated). + * + * @tparam Func The type of the callable object. + * @param func The callable to execute, taking a raw Tox pointer. + * @return The result of the callable execution. + */ + template + auto invoke(Func &&func) -> std::invoke_result_t + { + using R = std::invoke_result_t; + auto promise = std::make_shared>(); + auto future = promise->get_future(); + + execute([p = promise, f = std::forward(func)](Tox *_Nonnull tox) { + if constexpr (std::is_void_v) { + f(tox); + p->set_value(); + } else { + p->set_value(f(tox)); + } + }); + + return future.get(); + } + + /** + * @brief Retrieves all accumulated Tox event batches. + * + * Returns a vector of unique pointers to Tox_Events structures that have + * been collected by the runner since the last call. Ownership is transferred + * to the caller. This method is thread-safe. + * + * @return A vector of Tox_Events pointers. + */ + std::vector poll_events(); + + /** + * @brief Accesses the underlying Tox instance directly. + * + * @warning Thread-Safety Violation: This method provides unsafe access to the + * Tox instance. It should ONLY be used when the runner thread is known to be + * idle (e.g., before the loop starts) or for accessing constant/read-only properties. + * For all other operations, use `execute` or `invoke`. + */ + Tox *_Nullable unsafe_tox() { return tox_.get(); } + + /** + * @brief Temporarily stops the runner from participating in the simulation. + * + * Unregisters the runner and its tick listener from the simulation. + * While paused, the runner will not call tox_iterate. + */ + void pause(); + + /** + * @brief Resumes the runner's participation in the simulation. + */ + void resume(); + + /** + * @brief Returns true if the runner is currently active. + */ + bool is_active() const { return active_; } + +private: + void loop(); + + SimulatedNode::ToxPtr tox_; + std::thread thread_; + std::atomic active_{true}; + + struct Message { + enum Type { Task, Tick, Stop } type; + std::function task; + uint64_t generation = 0; + }; + + MpscQueue queue_; + MpscQueue events_queue_; + + Simulation::TickListenerId tick_listener_id_ = -1; + SimulatedNode &node_; +}; + +} // namespace tox::test + +#endif // C_TOXCORE_TESTING_SUPPORT_TOX_RUNNER_H diff --git a/testing/support/simulation_test.cc b/testing/support/simulation_test.cc new file mode 100644 index 00000000..c70219bc --- /dev/null +++ b/testing/support/simulation_test.cc @@ -0,0 +1,76 @@ +#include "public/simulation.hh" + +#include + +namespace tox::test { + +TEST(LogFilterTest, Operators) +{ + LogMetadata md1{TOX_LOG_LEVEL_INFO, "file1.c", 10, "func1", "message1", 1}; + LogMetadata md2{TOX_LOG_LEVEL_DEBUG, "file2.c", 20, "func2", "message2", 2}; + + auto f1 = log_filter::file("file1"); + auto f2 = log_filter::level(TOX_LOG_LEVEL_INFO); + + EXPECT_TRUE(f1(md1)); + EXPECT_FALSE(f1(md2)); + + EXPECT_TRUE(f2(md1)); + EXPECT_FALSE(f2(md2)); + + auto f_and = f1 && f2; + EXPECT_TRUE(f_and(md1)); + EXPECT_FALSE(f_and(md2)); + + auto f_or = f1 || log_filter::file("file2"); + EXPECT_TRUE(f_or(md1)); + EXPECT_TRUE(f_or(md2)); + + auto f_not = !f1; + EXPECT_FALSE(f_not(md1)); + EXPECT_TRUE(f_not(md2)); +} + +TEST(LogFilterTest, LevelComparison) +{ + LogMetadata md_trace{TOX_LOG_LEVEL_TRACE, "file.c", 1, "func", "msg", 1}; + LogMetadata md_debug{TOX_LOG_LEVEL_DEBUG, "file.c", 1, "func", "msg", 1}; + LogMetadata md_info{TOX_LOG_LEVEL_INFO, "file.c", 1, "func", "msg", 1}; + LogMetadata md_warn{TOX_LOG_LEVEL_WARNING, "file.c", 1, "func", "msg", 1}; + LogMetadata md_error{TOX_LOG_LEVEL_ERROR, "file.c", 1, "func", "msg", 1}; + + // level() > DEBUG + auto f_gt = log_filter::level() > TOX_LOG_LEVEL_DEBUG; + EXPECT_FALSE(f_gt(md_trace)); + EXPECT_FALSE(f_gt(md_debug)); + EXPECT_TRUE(f_gt(md_info)); + EXPECT_TRUE(f_gt(md_error)); + + // level() < INFO + auto f_lt = log_filter::level() < TOX_LOG_LEVEL_INFO; + EXPECT_TRUE(f_lt(md_trace)); + EXPECT_TRUE(f_lt(md_debug)); + EXPECT_FALSE(f_lt(md_info)); + EXPECT_FALSE(f_lt(md_error)); + + // level() == WARNING + auto f_eq = log_filter::level() == TOX_LOG_LEVEL_WARNING; + EXPECT_FALSE(f_eq(md_info)); + EXPECT_TRUE(f_eq(md_warn)); + EXPECT_FALSE(f_eq(md_error)); +} + +TEST(LogFilterTest, SimulationIntegration) +{ + Simulation sim; + sim.net().set_verbose(true); + + sim.set_log_filter(log_filter::level(TOX_LOG_LEVEL_ERROR) && log_filter::node(1)); + + auto node = sim.create_node(); + auto tox = node->create_tox(); + + SUCCEED(); +} + +} // namespace tox::test diff --git a/testing/support/src/fake_memory.cc b/testing/support/src/fake_memory.cc index 4d67cf4c..908fa674 100644 --- a/testing/support/src/fake_memory.cc +++ b/testing/support/src/fake_memory.cc @@ -4,19 +4,20 @@ #include #include -#include "../../../toxcore/tox_memory_impl.h" +#include "../../../toxcore/mem.h" namespace tox::test { // --- Trampolines --- -static const Tox_Memory_Funcs kFakeMemoryVtable = { - .malloc_callback - = [](void *obj, uint32_t size) { return static_cast(obj)->malloc(size); }, +static const Memory_Funcs kFakeMemoryVtable = { + .malloc_callback = [](void *_Nonnull obj, + uint32_t size) { return static_cast(obj)->malloc(size); }, .realloc_callback - = [](void *obj, void *ptr, + = [](void *_Nonnull obj, void *_Nullable ptr, uint32_t size) { return static_cast(obj)->realloc(ptr, size); }, - .dealloc_callback = [](void *obj, void *ptr) { static_cast(obj)->free(ptr); }, + .dealloc_callback + = [](void *_Nonnull obj, void *_Nullable ptr) { static_cast(obj)->free(ptr); }, }; // --- Implementation --- @@ -24,7 +25,7 @@ static const Tox_Memory_Funcs kFakeMemoryVtable = { FakeMemory::FakeMemory() = default; FakeMemory::~FakeMemory() = default; -void *FakeMemory::malloc(size_t size) +void *_Nullable FakeMemory::malloc(size_t size) { bool fail = failure_injector_ && failure_injector_(size); @@ -45,18 +46,12 @@ void *FakeMemory::malloc(size_t size) header->size = size; header->magic = kMagic; - current_allocation_ += size; - if (current_allocation_ > max_allocation_) { - max_allocation_ = current_allocation_; - } + on_allocation(size); - void *res = header + 1; - // std::cerr << "[FakeMemory] malloc(" << size << ") -> " << res << " (header=" << header << ")" - // << std::endl; - return res; + return header + 1; } -void *FakeMemory::realloc(void *ptr, size_t size) +void *_Nullable FakeMemory::realloc(void *_Nullable ptr, size_t size) { if (!ptr) { return malloc(size); @@ -82,7 +77,6 @@ void *FakeMemory::realloc(void *ptr, size_t size) } if (fail) { - // If realloc fails, original block is left untouched. return nullptr; } @@ -92,22 +86,17 @@ void *FakeMemory::realloc(void *ptr, size_t size) return nullptr; } - Header *header = static_cast
(new_ptr); - current_allocation_ -= old_size; - current_allocation_ += size; - if (current_allocation_ > max_allocation_) { - max_allocation_ = current_allocation_; - } + on_deallocation(old_size); + on_allocation(size); + Header *header = static_cast
(new_ptr); header->size = size; header->magic = kMagic; - void *res = header + 1; - // std::cerr << "[FakeMemory] realloc(" << ptr << ", " << size << ") -> " << res << " (header=" - // << header << ")" << std::endl; - return res; + + return header + 1; } -void FakeMemory::free(void *ptr) +void FakeMemory::free(void *_Nullable ptr) { if (!ptr) { return; @@ -127,7 +116,7 @@ void FakeMemory::free(void *ptr) } size_t size = header->size; - current_allocation_ -= size; + on_deallocation(size); header->magic = kFreeMagic; // Mark as free std::free(header); } @@ -139,6 +128,19 @@ void FakeMemory::set_failure_injector(FailureInjector injector) void FakeMemory::set_observer(Observer observer) { observer_ = std::move(observer); } -struct Tox_Memory FakeMemory::get_c_memory() { return Tox_Memory{&kFakeMemoryVtable, this}; } +struct Memory FakeMemory::c_memory() { return Memory{&kFakeMemoryVtable, this}; } + +size_t FakeMemory::current_allocation() const { return current_allocation_.load(); } + +size_t FakeMemory::max_allocation() const { return max_allocation_.load(); } + +void FakeMemory::on_allocation(size_t size) +{ + size_t current = current_allocation_.fetch_add(size) + size; + size_t max = max_allocation_.load(std::memory_order_relaxed); + while (current > max && !max_allocation_.compare_exchange_weak(max, current)) { } +} + +void FakeMemory::on_deallocation(size_t size) { current_allocation_.fetch_sub(size); } } // namespace tox::test diff --git a/testing/support/src/fake_random.cc b/testing/support/src/fake_random.cc index 01940a92..74155595 100644 --- a/testing/support/src/fake_random.cc +++ b/testing/support/src/fake_random.cc @@ -2,18 +2,18 @@ #include -#include "../../../toxcore/tox_random_impl.h" +#include "../../../toxcore/rng.h" namespace tox::test { -// --- Trampolines for Tox_Random_Funcs --- +// --- Trampolines for Random_Funcs --- -static const Tox_Random_Funcs kFakeRandomVtable = { +static const Random_Funcs kFakeRandomVtable = { .bytes_callback - = [](void *obj, uint8_t *bytes, + = [](void *_Nonnull obj, uint8_t *_Nonnull bytes, uint32_t length) { static_cast(obj)->bytes(bytes, length); }, .uniform_callback - = [](void *obj, + = [](void *_Nonnull obj, uint32_t upper_bound) { return static_cast(obj)->uniform(upper_bound); }, }; @@ -44,7 +44,7 @@ uint32_t FakeRandom::uniform(uint32_t upper_bound) return dist(rng_); } -void FakeRandom::bytes(uint8_t *out, size_t count) +void FakeRandom::bytes(uint8_t *_Nonnull out, size_t count) { if (entropy_source_) { entropy_source_(out, count); @@ -58,6 +58,6 @@ void FakeRandom::bytes(uint8_t *out, size_t count) } } -struct Tox_Random FakeRandom::get_c_random() { return Tox_Random{&kFakeRandomVtable, this}; } +struct Random FakeRandom::c_random() { return Random{&kFakeRandomVtable, this}; } } // namespace tox::test diff --git a/testing/support/src/fuzz_helpers.cc b/testing/support/src/fuzz_helpers.cc index f756655b..51ec7d38 100644 --- a/testing/support/src/fuzz_helpers.cc +++ b/testing/support/src/fuzz_helpers.cc @@ -56,7 +56,7 @@ void configure_fuzz_memory_source(FakeMemory &memory, Fuzz_Data &input) void configure_fuzz_random_source(FakeRandom &random, Fuzz_Data &input) { - random.set_entropy_source([&input](uint8_t *out, size_t count) { + random.set_entropy_source([&input](uint8_t *_Nonnull out, size_t count) { // Initialize with zeros in case of underflow std::memset(out, 0, count); diff --git a/testing/support/src/network.cc b/testing/support/src/network.cc index d26b90e9..3683b41d 100644 --- a/testing/support/src/network.cc +++ b/testing/support/src/network.cc @@ -14,8 +14,8 @@ IP make_ip(uint32_t ipv4) IP make_node_ip(uint32_t node_id) { - // Use 10.x.y.z range: 10. (id >> 16) . (id >> 8) . (id & 0xFF) - return make_ip(0x0A000000 | (node_id & 0x00FFFFFF)); + // Use 20.x.y.z range: 20. (id >> 16) . (id >> 8) . (id & 0xFF) + return make_ip(0x14000000 | (node_id & 0x00FFFFFF)); } } // namespace tox::test diff --git a/testing/support/src/simulated_environment.cc b/testing/support/src/simulated_environment.cc index 2d416eef..40cd91e0 100644 --- a/testing/support/src/simulated_environment.cc +++ b/testing/support/src/simulated_environment.cc @@ -52,11 +52,11 @@ std::unique_ptr SimulatedEnvironment::create_node(uint16_t port scoped->endpoint = scoped->node->get_primary_socket(); // Use global Random and Memory for legacy compatibility. - scoped->c_random = global_random_->get_c_random(); - scoped->c_memory = global_memory_->get_c_memory(); + scoped->c_random = global_random_->c_random(); + scoped->c_memory = global_memory_->c_memory(); // Use Node's Network - scoped->c_network = scoped->node->get_c_network(); + scoped->c_network = scoped->node->c_network; // Setup System scoped->system.mem = &scoped->c_memory; @@ -64,7 +64,7 @@ std::unique_ptr SimulatedEnvironment::create_node(uint16_t port scoped->system.rng = &scoped->c_random; scoped->system.mono_time_user_data = &sim_->clock(); - scoped->system.mono_time_callback = [](void *user_data) -> uint64_t { + scoped->system.mono_time_callback = [](void *_Nullable user_data) -> uint64_t { return static_cast(user_data)->current_time_ms(); }; diff --git a/testing/support/src/simulation.cc b/testing/support/src/simulation.cc index 3a2d3364..1d1fdea2 100644 --- a/testing/support/src/simulation.cc +++ b/testing/support/src/simulation.cc @@ -1,10 +1,96 @@ #include "../public/simulation.hh" #include +#include #include +#include namespace tox::test { +// --- LogFilter --- + +LogFilter operator&&(const LogFilter &lhs, const LogFilter &rhs) +{ + return LogFilter([=](const LogMetadata &md) { return lhs(md) && rhs(md); }); +} + +LogFilter operator||(const LogFilter &lhs, const LogFilter &rhs) +{ + return LogFilter([=](const LogMetadata &md) { return lhs(md) || rhs(md); }); +} + +LogFilter operator!(const LogFilter &target) +{ + return LogFilter([=](const LogMetadata &md) { return !target(md); }); +} + +namespace log_filter { + + LogFilter level(Tox_Log_Level min_level) + { + return LogFilter([=](const LogMetadata &md) { return md.level >= min_level; }); + } + + LevelPlaceholder level() { return {}; } + + LogFilter LevelPlaceholder::operator>(Tox_Log_Level rhs) const + { + return LogFilter([=](const LogMetadata &md) { return md.level > rhs; }); + } + + LogFilter LevelPlaceholder::operator>=(Tox_Log_Level rhs) const + { + return LogFilter([=](const LogMetadata &md) { return md.level >= rhs; }); + } + + LogFilter LevelPlaceholder::operator<(Tox_Log_Level rhs) const + { + return LogFilter([=](const LogMetadata &md) { return md.level < rhs; }); + } + + LogFilter LevelPlaceholder::operator<=(Tox_Log_Level rhs) const + { + return LogFilter([=](const LogMetadata &md) { return md.level <= rhs; }); + } + + LogFilter LevelPlaceholder::operator==(Tox_Log_Level rhs) const + { + return LogFilter([=](const LogMetadata &md) { return md.level == rhs; }); + } + + LogFilter LevelPlaceholder::operator!=(Tox_Log_Level rhs) const + { + return LogFilter([=](const LogMetadata &md) { return md.level != rhs; }); + } + + LogFilter file(std::string pattern) + { + return LogFilter([p = std::move(pattern)](const LogMetadata &md) { + return std::string(md.file).find(p) != std::string::npos; + }); + } + + LogFilter func(std::string pattern) + { + return LogFilter([p = std::move(pattern)](const LogMetadata &md) { + return std::string(md.func).find(p) != std::string::npos; + }); + } + + LogFilter message(std::string pattern) + { + return LogFilter([p = std::move(pattern)](const LogMetadata &md) { + return std::string(md.message).find(p) != std::string::npos; + }); + } + + LogFilter node(uint32_t id) + { + return LogFilter([=](const LogMetadata &md) { return md.node_id == id; }); + } + +} // namespace log_filter + // --- Simulation --- Simulation::Simulation() @@ -24,11 +110,125 @@ void Simulation::advance_time(uint64_t ms) void Simulation::run_until(std::function condition, uint64_t timeout_ms) { uint64_t start_time = clock_->current_time_ms(); - while (!condition()) { - if (clock_->current_time_ms() - start_time > timeout_ms) { + + // Initial check + if (condition()) + return; + + while (true) { + if (clock_->current_time_ms() - start_time >= timeout_ms) { break; } - advance_time(10); // 10ms ticks + + // 1. Advance Global Time + // Determine the time step based on the minimum requested delay from all runners + // during the previous tick. We default to kDefaultTickIntervalMs if no specific request was + // made. The `exchange` operation resets the minimum accumulator for the current tick. + uint32_t step = next_step_min_.exchange(kDefaultTickIntervalMs); + advance_time(step); + + // 2. Start Barrier (Signal Runners) + // Notify all registered runners that time has advanced and they should proceed + // with their next iteration. + { + std::lock_guard lock(barrier_mutex_); + current_generation_++; + // Initialize the countdown of active runners for this tick. + active_runners_.store(registered_runners_); + + for (const auto &l : tick_listeners_) { + l.callback(current_generation_); + } + } + barrier_cv_.notify_all(); + + // 3. End Barrier (Wait for Completion) + // Block until all active runners have reported completion via `tick_complete()`. + { + std::unique_lock lock(barrier_mutex_); + // We use a lambda predicate to handle spurious wakeups. + // The wait finishes when `active_runners_` reaches zero. + barrier_cv_.wait(lock, [this] { return active_runners_.load() == 0; }); + } + + // 4. Check condition + if (condition()) + return; + } +} + +void Simulation::set_log_filter(LogFilter filter) { log_filter_ = std::move(filter); } + +Simulation::TickListenerId Simulation::register_tick_listener( + std::function listener) +{ + std::lock_guard lock(barrier_mutex_); + TickListenerId id = next_listener_id_++; + tick_listeners_.push_back({id, std::move(listener)}); + return id; +} + +void Simulation::unregister_tick_listener(TickListenerId id) +{ + std::lock_guard lock(barrier_mutex_); + for (auto it = tick_listeners_.begin(); it != tick_listeners_.end(); ++it) { + if (it->id == id) { + tick_listeners_.erase(it); + break; + } + } +} + +uint64_t Simulation::register_runner() +{ + std::lock_guard lock(barrier_mutex_); + registered_runners_++; + return current_generation_; +} + +void Simulation::unregister_runner() +{ + std::lock_guard lock(barrier_mutex_); + registered_runners_--; + + // If we are currently running a tick (active_runners > 0), we need to decrement it + // because this runner will not be calling tick_complete() + if (active_runners_.load() > 0) { + if (active_runners_.fetch_sub(1) == 1) { + barrier_cv_.notify_all(); + } + } +} + +uint64_t Simulation::wait_for_tick( + uint64_t last_gen, const std::atomic &stop_token, uint64_t timeout_ms) +{ + std::unique_lock lock(barrier_mutex_); + // Wait until generation increases (new tick started) OR we are stopped OR timeout + bool result = barrier_cv_.wait_for(lock, std::chrono::milliseconds(timeout_ms), + [&] { return current_generation_ > last_gen || stop_token; }); + + if (stop_token) + return last_gen; + if (!result) + return last_gen; // Timeout + return current_generation_; +} + +void Simulation::tick_complete(uint32_t next_delay_ms) +{ + // Atomic min reduction + uint32_t current = next_step_min_.load(std::memory_order_relaxed); + while ( + next_delay_ms < current && !next_step_min_.compare_exchange_weak(current, next_delay_ms)) { + // If exchange failed, current was updated to actual value, so loop checks again + } + + // We don't need the mutex to decrement the atomic + if (active_runners_.fetch_sub(1) == 1) { + // Last runner to finish: notify main thread + std::lock_guard lock(barrier_mutex_); + barrier_cv_.notify_all(); } } @@ -51,9 +251,9 @@ SimulatedNode::SimulatedNode(Simulation &sim, uint32_t node_id) , network_(std::make_unique(sim.net(), make_node_ip(node_id))) , random_(std::make_unique(12345 + node_id)) // Unique seed , memory_(std::make_unique()) - , c_network(network_->get_c_network()) - , c_random(random_->get_c_random()) - , c_memory(memory_->get_c_memory()) + , c_network(network_->c_network()) + , c_random(random_->c_random()) + , c_memory(memory_->c_memory()) , ip(make_node_ip(node_id)) { } @@ -65,26 +265,49 @@ ClockSystem &SimulatedNode::clock() { return sim_.clock(); } RandomSystem &SimulatedNode::random() { return *random_; } MemorySystem &SimulatedNode::memory() { return *memory_; } -SimulatedNode::ToxPtr SimulatedNode::create_tox(const Tox_Options *options) +SimulatedNode::ToxPtr SimulatedNode::create_tox(const Tox_Options *_Nullable options) { - std::unique_ptr default_options( - nullptr, tox_options_free); + std::unique_ptr local_opts( + tox_options_new(nullptr), tox_options_free); + assert(local_opts != nullptr); - if (options == nullptr) { - default_options.reset(tox_options_new(nullptr)); - assert(default_options != nullptr); - tox_options_set_ipv6_enabled(default_options.get(), false); - tox_options_set_start_port(default_options.get(), 33445); - tox_options_set_end_port(default_options.get(), 55555); - options = default_options.get(); + if (options != nullptr) { + tox_options_copy(local_opts.get(), options); + } else { + tox_options_set_ipv6_enabled(local_opts.get(), false); + tox_options_set_start_port(local_opts.get(), 33445); + tox_options_set_end_port(local_opts.get(), 55555); } + tox_options_set_log_callback(local_opts.get(), + [](Tox *tox, Tox_Log_Level level, const char *file, uint32_t line, const char *func, + const char *message, void *user_data) { + SimulatedNode *node = static_cast(user_data); + uint32_t ip4 = net_ntohl(node->ip.ip.v4.uint32); + + LogMetadata md{level, file, line, func, message, ip4 & 0xFF}; + const auto &filter = node->simulation().log_filter(); + + bool allow = false; + if (filter.pred) { + allow = filter(md); + } else { + allow = node->simulation().net().is_verbose() && level >= TOX_LOG_LEVEL_TRACE; + } + + if (allow) { + std::cerr << "[Tox Log] [Node " << (ip4 & 0xFF) << "] " << file << ":" << line + << " (" << func << "): " << message << std::endl; + } + }); + tox_options_set_log_user_data(local_opts.get(), this); + Tox_Options_Testing opts_testing; Tox_System system; system.ns = &c_network; system.rng = &c_random; system.mem = &c_memory; - system.mono_time_callback = [](void *user_data) -> uint64_t { + system.mono_time_callback = [](void *_Nullable user_data) -> uint64_t { return static_cast(user_data)->current_time_ms(); }; system.mono_time_user_data = &sim_.clock(); @@ -94,15 +317,17 @@ SimulatedNode::ToxPtr SimulatedNode::create_tox(const Tox_Options *options) Tox_Err_New err; Tox_Err_New_Testing err_testing; - Tox *t = tox_new_testing(options, &err, &opts_testing, &err_testing); + Tox *t = tox_new_testing(local_opts.get(), &err, &opts_testing, &err_testing); if (!t) { + std::cerr << "tox_new_testing failed: " << err << " (testing err: " << err_testing << ")" + << std::endl; return nullptr; } return ToxPtr(t); } -FakeUdpSocket *SimulatedNode::get_primary_socket() +FakeUdpSocket *_Nullable SimulatedNode::get_primary_socket() { auto sockets = network_->get_bound_udp_sockets(); if (sockets.empty()) diff --git a/testing/support/src/tox_network.cc b/testing/support/src/tox_network.cc index d306e625..72c6c80c 100644 --- a/testing/support/src/tox_network.cc +++ b/testing/support/src/tox_network.cc @@ -5,17 +5,21 @@ #include "../public/tox_network.hh" #include +#include #include +#include #include "../../../toxcore/network.h" #include "../../../toxcore/tox.h" +#include "../../../toxcore/tox_events.h" +#include "../public/tox_runner.hh" namespace tox::test { ConnectedFriend::~ConnectedFriend() = default; -std::vector setup_connected_friends(Simulation &sim, Tox *main_tox, - SimulatedNode &main_node, int num_friends, const Tox_Options *options) +std::vector setup_connected_friends(Simulation &sim, Tox *_Nonnull main_tox, + SimulatedNode &main_node, int num_friends, const Tox_Options *_Nullable options, bool verbose) { std::vector friends; friends.reserve(num_friends); @@ -42,108 +46,115 @@ std::vector setup_connected_friends(Simulation &sim, Tox *main_ for (int i = 0; i < num_friends; ++i) { auto node = sim.create_node(); - auto tox = node->create_tox(options); - if (!tox) { - return {}; - } + auto runner = std::make_unique(*node, options); uint8_t friend_pk[TOX_PUBLIC_KEY_SIZE]; - tox_self_get_public_key(tox.get(), friend_pk); + runner->invoke([&](Tox *_Nonnull tox) { tox_self_get_public_key(tox, friend_pk); }); Tox_Err_Friend_Add err; uint32_t fn = tox_friend_add_norequest(main_tox, friend_pk, &err); if (fn == UINT32_MAX || err != TOX_ERR_FRIEND_ADD_OK) { return {}; } - if (tox_friend_add_norequest(tox.get(), main_pk, &err) == UINT32_MAX - || err != TOX_ERR_FRIEND_ADD_OK) { - return {}; - } - // Bootstrap to the main node AND the PREVIOUS node in the chain - tox_bootstrap(tox.get(), main_ip_str, main_port, main_dht_id, nullptr); - if (i > 0) { - tox_bootstrap(tox.get(), prev_ip_str, prev_port, prev_dht_id, nullptr); - } + // Execute add friend and bootstrap on runner + runner->execute([=](Tox *_Nonnull tox) { + tox_friend_add_norequest(tox, main_pk, nullptr); + tox_bootstrap(tox, main_ip_str, main_port, main_dht_id, nullptr); + if (i > 0) { + tox_bootstrap(tox, prev_ip_str, prev_port, prev_dht_id, nullptr); + } + }); + + // Retrieve previous node's DHT ID and update IP for the next iteration. + // We use invoke to safely fetch data from the runner thread. + runner->invoke([&](Tox *_Nonnull tox) { tox_self_get_dht_id(tox, prev_dht_id); }); - // Update prev for next node - tox_self_get_dht_id(tox.get(), prev_dht_id); ip_parse_addr(&node->ip, prev_ip_str, sizeof(prev_ip_str)); - FakeUdpSocket *node_socket = node->get_primary_socket(); + FakeUdpSocket *_Nullable node_socket = node->get_primary_socket(); if (!node_socket) { return {}; } prev_port = node_socket->local_port(); - friends.push_back({std::move(node), std::move(tox), fn}); + friends.push_back({std::move(node), std::move(runner), fn}); - // Run simulation to let DHT stabilize - sim.run_until( - [&]() { - tox_iterate(main_tox, nullptr); - for (auto &f : friends) { - tox_iterate(f.tox.get(), nullptr); - } - return false; - }, - 200); + // Run the simulation periodically to allow the DHT to stabilize incrementally + // as we add nodes, rather than waiting until the end. + if (friends.size() % 10 == 0) { + sim.run_until([&]() { return false; }, 20); + } } - // Optional: Bootstrap main_tox to the last node to complete the circle if (!friends.empty()) { tox_bootstrap(main_tox, prev_ip_str, prev_port, prev_dht_id, nullptr); } - // Run simulation until all are connected + std::vector friends_connected(friends.size(), false); + sim.run_until( [&]() { - bool all_connected = true; - int connected_count = 0; tox_iterate(main_tox, nullptr); - for (auto &f : friends) { - tox_iterate(f.tox.get(), nullptr); - if (tox_friend_get_connection_status(main_tox, f.friend_number, nullptr) - != TOX_CONNECTION_NONE - && tox_friend_get_connection_status(f.tox.get(), 0, nullptr) - != TOX_CONNECTION_NONE) { - connected_count++; - } else { - all_connected = false; - } - } - static uint64_t last_print = 0; - if (sim.clock().current_time_ms() - last_print > 1000) { - std::cerr << "[setup_connected_friends] Friends connected: " << connected_count - << "/" << friends.size() << " (time: " << sim.clock().current_time_ms() - << "ms)" << std::endl; - if (connected_count < static_cast(friends.size()) - && sim.clock().current_time_ms() > 10000) { - for (size_t i = 0; i < friends.size(); ++i) { - auto s1 = tox_friend_get_connection_status( - main_tox, friends[i].friend_number, nullptr); - auto s2 - = tox_friend_get_connection_status(friends[i].tox.get(), 0, nullptr); - if (s1 == TOX_CONNECTION_NONE || s2 == TOX_CONNECTION_NONE) { - std::cerr << " Friend " << i << " not connected (Main->F: " << s1 - << ", F->Main: " << s2 << ")" << std::endl; + // Check connection status + int connected_count = 0; + + for (size_t i = 0; i < friends.size(); ++i) { + // Check if main sees friend + bool main_sees_friend + = tox_friend_get_connection_status(main_tox, friends[i].friend_number, nullptr) + != TOX_CONNECTION_NONE; + + // Check if friend sees main by polling events from the runner + auto batches = friends[i].runner->poll_events(); + for (const auto &batch : batches) { + size_t size = tox_events_get_size(batch.get()); + for (size_t k = 0; k < size; ++k) { + const Tox_Event *e = tox_events_get(batch.get(), k); + if (tox_event_get_type(e) == TOX_EVENT_FRIEND_CONNECTION_STATUS) { + auto *ev = tox_event_get_friend_connection_status(e); + if (tox_event_friend_connection_status_get_connection_status(ev) + != TOX_CONNECTION_NONE) { + friends_connected[i] = true; + } else { + friends_connected[i] = false; + } } } } + if (main_sees_friend && friends_connected[i]) { + connected_count++; + } + } + + if (connected_count == static_cast(friends.size())) { + return true; + } + + static uint64_t last_print = 0; + if (verbose && sim.clock().current_time_ms() - last_print > 1000) { + std::cerr << "[setup_connected_friends] Friends connected: " << connected_count + << "/" << friends.size() << " (time: " << sim.clock().current_time_ms() + << "ms)" << std::endl; last_print = sim.clock().current_time_ms(); } - return all_connected; + + return false; }, - 300000); // 5 minutes simulation time for 100 nodes to converge + 300000); return friends; } -bool connect_friends( - Simulation &sim, SimulatedNode &node1, Tox *tox1, SimulatedNode &node2, Tox *tox2) +bool connect_friends(Simulation &sim, SimulatedNode &node1, Tox *_Nonnull tox1, + SimulatedNode &node2, Tox *_Nonnull tox2) { + // This helper function assumes the Tox instances are running in the current thread + // (e.g., standard unit test) or that the caller is handling thread safety if they + // are part of a runner. It uses direct tox_iterate calls. + uint8_t pk1[TOX_PUBLIC_KEY_SIZE]; uint8_t pk2[TOX_PUBLIC_KEY_SIZE]; tox_self_get_public_key(tox1, pk1); @@ -199,7 +210,7 @@ bool connect_friends( } uint32_t setup_connected_group( - Simulation &sim, Tox *main_tox, const std::vector &friends) + Simulation &sim, Tox *_Nonnull main_tox, const std::vector &friends) { struct NodeGroupState { uint32_t peer_count = 0; @@ -207,9 +218,10 @@ uint32_t setup_connected_group( }; NodeGroupState main_state; - tox_callback_group_peer_join(main_tox, [](Tox *, uint32_t, uint32_t, void *user_data) { - static_cast(user_data)->peer_count++; - }); + tox_callback_group_peer_join( + main_tox, [](Tox *_Nonnull, uint32_t, uint32_t, void *_Nullable user_data) { + static_cast(user_data)->peer_count++; + }); Tox_Err_Group_New err_new; main_state.group_number = tox_group_new(main_tox, TOX_GROUP_PRIVACY_STATE_PUBLIC, @@ -217,52 +229,26 @@ uint32_t setup_connected_group( &err_new); if (main_state.group_number == UINT32_MAX || err_new != TOX_ERR_GROUP_NEW_OK) { - std::cerr << "tox_group_new failed with error: " << err_new << std::endl; return UINT32_MAX; } - std::vector> friend_states; - friend_states.reserve(friends.size()); + // Friend states tracked via events + std::vector friend_states(friends.size()); - for (size_t i = 0; i < friends.size(); ++i) { - auto state = std::make_unique(); - tox_callback_group_peer_join( - friends[i].tox.get(), [](Tox *, uint32_t, uint32_t, void *user_data) { - static_cast(user_data)->peer_count++; - }); + // Main tox sends invites; friends accept via events polled from their runners. - tox_callback_group_invite(friends[i].tox.get(), - [](Tox *tox, uint32_t friend_number, const uint8_t *invite_data, - size_t invite_data_length, const uint8_t *, size_t, void *user_data) { - NodeGroupState *ng_state = static_cast(user_data); - Tox_Err_Group_Invite_Accept err_accept; - ng_state->group_number - = tox_group_invite_accept(tox, friend_number, invite_data, invite_data_length, - reinterpret_cast("peer"), 4, nullptr, 0, &err_accept); - if (ng_state->group_number == UINT32_MAX - || err_accept != TOX_ERR_GROUP_INVITE_ACCEPT_OK) { - ng_state->group_number = UINT32_MAX; - } - }); - - friend_states.push_back(std::move(state)); - } - - // Run until all have joined and see everyone bool success = false; - uint64_t last_print = 0; size_t invites_sent = 0; sim.run_until( [&]() { tox_iterate(main_tox, &main_state); - // Throttle invites: keep max 5 pending + // Throttle invites size_t accepted_count = 0; - for (size_t k = 0; k < invites_sent; ++k) { - if (friend_states[k]->group_number != UINT32_MAX) { + for (const auto &fs : friend_states) { + if (fs.group_number != UINT32_MAX) accepted_count++; - } } while (invites_sent < friends.size() && (invites_sent - accepted_count) < 5) { @@ -271,58 +257,58 @@ uint32_t setup_connected_group( friends[invites_sent].friend_number, &err_invite)) { invites_sent++; } else { - if (err_invite != TOX_ERR_GROUP_INVITE_FRIEND_FAIL_SEND) { - std::cerr << "Invite failed for friend " << invites_sent << ": " - << err_invite << std::endl; - } - break; // Stop trying to send for this tick if we failed + break; } } - bool all_see_all = true; - - if (main_state.peer_count < friends.size()) { - all_see_all = false; - } - + // Process friend events for (size_t i = 0; i < friends.size(); ++i) { - tox_iterate(friends[i].tox.get(), friend_states[i].get()); - if (friend_states[i]->group_number == UINT32_MAX - || friend_states[i]->peer_count < friends.size()) { - all_see_all = false; - } - } + auto batches = friends[i].runner->poll_events(); + for (const auto &batch : batches) { + size_t size = tox_events_get_size(batch.get()); + for (size_t k = 0; k < size; ++k) { + const Tox_Event *e = tox_events_get(batch.get(), k); + Tox_Event_Type type = tox_event_get_type(e); - if ((sim.clock().current_time_ms() - last_print) % 5000 == 0) { - int joined = 0; - int fully_connected = 0; - if (main_state.group_number != UINT32_MAX) - joined++; - if (main_state.peer_count >= friends.size()) - fully_connected++; + if (type == TOX_EVENT_GROUP_INVITE) { + auto *ev = tox_event_get_group_invite(e); + uint32_t friend_number = tox_event_group_invite_get_friend_number(ev); + const uint8_t *data = tox_event_group_invite_get_invite_data(ev); + size_t len = tox_event_group_invite_get_invite_data_length(ev); - for (const auto &fs : friend_states) { - if (fs->group_number != UINT32_MAX) { - joined++; - if (fs->peer_count >= friends.size()) - fully_connected++; + // Accept invite on runner thread. + // We must copy data because the event structure will be freed. + std::vector invite_data(data, data + len); + + friends[i].runner->execute([=](Tox *_Nonnull tox) { + Tox_Err_Group_Invite_Accept err; + tox_group_invite_accept(tox, friend_number, invite_data.data(), + invite_data.size(), reinterpret_cast("peer"), + 4, nullptr, 0, &err); + }); + } else if (type == TOX_EVENT_GROUP_PEER_JOIN) { + friend_states[i].peer_count++; + } else if (type == TOX_EVENT_GROUP_SELF_JOIN) { + auto *ev = tox_event_get_group_self_join(e); + friend_states[i].group_number + = tox_event_group_self_join_get_group_number(ev); + } } } - std::cerr << "[setup_connected_group] Main peer count: " << main_state.peer_count - << "/" << friends.size() << ", Nodes joined: " << joined << "/" - << (friends.size() + 1) << ", fully connected: " << fully_connected << "/" - << (friends.size() + 1) << " (time: " << sim.clock().current_time_ms() - << "ms)" << std::endl; - last_print = sim.clock().current_time_ms(); } - if (all_see_all) { - success = true; - return true; + if (main_state.peer_count < friends.size()) + return false; + + for (const auto &fs : friend_states) { + if (fs.group_number == UINT32_MAX || fs.peer_count < friends.size()) + return false; } - return false; + + success = true; + return true; }, - 300000); // 5 minutes + 300000); return success ? main_state.group_number : UINT32_MAX; } diff --git a/testing/support/src/tox_runner.cc b/testing/support/src/tox_runner.cc new file mode 100644 index 00000000..f228d5b4 --- /dev/null +++ b/testing/support/src/tox_runner.cc @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#include "../public/tox_runner.hh" + +namespace tox::test { + +ToxRunner::ToxRunner(SimulatedNode &node, const Tox_Options *_Nullable options) + : tox_(node.create_tox(options)) + , node_(node) +{ + if (tox_) { + tox_events_init(tox_.get()); + } + node_.simulation().register_runner(); + + tick_listener_id_ = node_.simulation().register_tick_listener([this](uint64_t gen) { + Message msg; + msg.type = Message::Tick; + msg.generation = gen; + queue_.push(std::move(msg)); + }); + + thread_ = std::thread([this] { loop(); }); +} + +ToxRunner::~ToxRunner() +{ + // Unregister first to prevent new ticks and update simulation counters + node_.simulation().unregister_tick_listener(tick_listener_id_); + node_.simulation().unregister_runner(); + + Message msg; + msg.type = Message::Stop; + queue_.push(std::move(msg)); + + if (thread_.joinable()) { + thread_.join(); + } +} + +void ToxRunner::execute(std::function task) +{ + Message msg; + msg.type = Message::Task; + msg.task = std::move(task); + queue_.push(std::move(msg)); +} + +std::vector ToxRunner::poll_events() +{ + std::vector ret; + ToxEventsPtr ptr; + while (events_queue_.try_pop(ptr)) { + ret.push_back(std::move(ptr)); + ptr = nullptr; // Reset ptr to avoid use-after-move warning, although try_pop overwrites + // it. + } + return ret; +} + +void ToxRunner::pause() +{ + if (!active_.exchange(false)) { + return; + } + + node_.simulation().unregister_tick_listener(tick_listener_id_); + node_.simulation().unregister_runner(); + tick_listener_id_ = -1; +} + +void ToxRunner::resume() +{ + if (active_.exchange(true)) { + return; + } + + node_.simulation().register_runner(); + tick_listener_id_ = node_.simulation().register_tick_listener([this](uint64_t gen) { + Message msg; + msg.type = Message::Tick; + msg.generation = gen; + queue_.push(std::move(msg)); + }); +} + +void ToxRunner::loop() +{ + while (true) { + Message msg = queue_.pop(); // Blocking wait + + switch (msg.type) { + case Message::Stop: + return; + + case Message::Task: + if (msg.task && tox_) { + msg.task(tox_.get()); + } + break; + + case Message::Tick: { + if (!tox_) { + node_.simulation().tick_complete(); + break; + } + + // Run Tox Events + Tox_Err_Events_Iterate err; + Tox_Events *events = tox_events_iterate(tox_.get(), false, &err); + if (events) { + events_queue_.push(ToxEventsPtr(events)); + } + + uint32_t interval = tox_iteration_interval(tox_.get()); + node_.simulation().tick_complete(interval); + break; + } + } + } +} + +} // namespace tox::test diff --git a/testing/support/tox_network_test.cc b/testing/support/tox_network_test.cc index 1293fd5a..e1ffdc32 100644 --- a/testing/support/tox_network_test.cc +++ b/testing/support/tox_network_test.cc @@ -6,6 +6,13 @@ #include +#include +#include +#include + +#include "../../toxcore/attributes.h" +#include "../../toxcore/network.h" + namespace tox::test { namespace { @@ -22,6 +29,8 @@ namespace { ASSERT_EQ(friends.size(), num_friends); + // Verification of connection status is done inside setup_connected_friends now, + // but we can double check main_tox's view. for (const auto &f : friends) { EXPECT_NE(tox_friend_get_connection_status(main_tox.get(), f.friend_number, nullptr), TOX_CONNECTION_NONE); @@ -29,25 +38,22 @@ namespace { // Verify they can actually communicate struct Context { - int count = 0; + std::atomic count{0}; } ctx; tox_callback_friend_message(main_tox.get(), - [](Tox *, uint32_t, Tox_Message_Type, const uint8_t *, size_t, void *user_data) { - static_cast(user_data)->count++; - }); + [](Tox *_Nonnull, uint32_t, Tox_Message_Type, const uint8_t *_Nonnull, size_t, + void *_Nullable user_data) { static_cast(user_data)->count++; }); for (const auto &f : friends) { - const uint8_t msg[] = "hello"; - tox_friend_send_message( - f.tox.get(), 0, TOX_MESSAGE_TYPE_NORMAL, msg, sizeof(msg), nullptr); + f.runner->execute([](Tox *tox) { + const uint8_t msg[] = "hello"; + tox_friend_send_message(tox, 0, TOX_MESSAGE_TYPE_NORMAL, msg, sizeof(msg), nullptr); + }); } sim.run_until([&]() { tox_iterate(main_tox.get(), &ctx); - for (auto &f : friends) { - tox_iterate(f.tox.get(), nullptr); - } return ctx.count == num_friends; }); @@ -68,26 +74,23 @@ namespace { ASSERT_EQ(friends.size(), num_friends); struct Context { - int count = 0; + std::atomic count{0}; } ctx; tox_callback_friend_message(main_tox.get(), - [](Tox *, uint32_t, Tox_Message_Type, const uint8_t *, size_t, void *user_data) { - static_cast(user_data)->count++; - }); + [](Tox *_Nonnull, uint32_t, Tox_Message_Type, const uint8_t *_Nonnull, size_t, + void *_Nullable user_data) { static_cast(user_data)->count++; }); for (const auto &f : friends) { - const uint8_t msg[] = "hello"; - tox_friend_send_message( - f.tox.get(), 0, TOX_MESSAGE_TYPE_NORMAL, msg, sizeof(msg), nullptr); + f.runner->execute([](Tox *tox) { + const uint8_t msg[] = "hello"; + tox_friend_send_message(tox, 0, TOX_MESSAGE_TYPE_NORMAL, msg, sizeof(msg), nullptr); + }); } sim.run_until( [&]() { tox_iterate(main_tox.get(), &ctx); - for (auto &f : friends) { - tox_iterate(f.tox.get(), nullptr); - } return ctx.count == num_friends; }, 60000); @@ -113,10 +116,11 @@ namespace { EXPECT_NE(tox_friend_get_connection_status(tox2.get(), 0, nullptr), TOX_CONNECTION_NONE); // Verify communication - bool received = false; + std::atomic received{false}; tox_callback_friend_message(tox2.get(), - [](Tox *, uint32_t, Tox_Message_Type, const uint8_t *, size_t, void *user_data) { - *static_cast(user_data) = true; + [](Tox *_Nonnull, uint32_t, Tox_Message_Type, const uint8_t *_Nonnull, size_t, + void *_Nullable user_data) { + *static_cast *>(user_data) = true; }); const uint8_t msg[] = "hello"; @@ -125,7 +129,7 @@ namespace { sim.run_until([&]() { tox_iterate(tox1.get(), nullptr); tox_iterate(tox2.get(), &received); - return received; + return received.load(); }); EXPECT_TRUE(received); @@ -148,28 +152,27 @@ namespace { // Verify we can send a group message struct Context { - int count = 0; + std::atomic count{0}; } ctx; tox_callback_group_message(main_tox.get(), - [](Tox *, uint32_t, uint32_t, Tox_Message_Type, const uint8_t *, size_t, uint32_t, - void *user_data) { static_cast(user_data)->count++; }); + [](Tox *_Nonnull, uint32_t, uint32_t, Tox_Message_Type, const uint8_t *_Nonnull, size_t, + uint32_t, + void *_Nullable user_data) { static_cast(user_data)->count++; }); for (const auto &f : friends) { - const uint8_t msg[] = "hello"; - uint32_t f_gn = 0; // It should be 0 since it's the first group. - Tox_Err_Group_Send_Message err_send; - tox_group_send_message( - f.tox.get(), f_gn, TOX_MESSAGE_TYPE_NORMAL, msg, sizeof(msg), &err_send); - EXPECT_EQ(err_send, TOX_ERR_GROUP_SEND_MESSAGE_OK); + f.runner->execute([](Tox *tox) { + const uint8_t msg[] = "hello"; + uint32_t f_gn = 0; // First group + Tox_Err_Group_Send_Message err_send; + tox_group_send_message( + tox, f_gn, TOX_MESSAGE_TYPE_NORMAL, msg, sizeof(msg), &err_send); + }); } sim.run_until( [&]() { tox_iterate(main_tox.get(), &ctx); - for (auto &f : friends) { - tox_iterate(f.tox.get(), nullptr); - } return ctx.count == num_friends; }, 10000); @@ -193,28 +196,27 @@ namespace { EXPECT_NE(group_number, UINT32_MAX); struct Context { - int count = 0; + std::atomic count{0}; } ctx; tox_callback_group_message(main_tox.get(), - [](Tox *, uint32_t, uint32_t, Tox_Message_Type, const uint8_t *, size_t, uint32_t, - void *user_data) { static_cast(user_data)->count++; }); + [](Tox *_Nonnull, uint32_t, uint32_t, Tox_Message_Type, const uint8_t *_Nonnull, size_t, + uint32_t, + void *_Nullable user_data) { static_cast(user_data)->count++; }); for (const auto &f : friends) { - const uint8_t msg[] = "hello"; - uint32_t f_gn = 0; - Tox_Err_Group_Send_Message err_send; - tox_group_send_message( - f.tox.get(), f_gn, TOX_MESSAGE_TYPE_NORMAL, msg, sizeof(msg), &err_send); - EXPECT_EQ(err_send, TOX_ERR_GROUP_SEND_MESSAGE_OK); + f.runner->execute([](Tox *tox) { + const uint8_t msg[] = "hello"; + uint32_t f_gn = 0; + Tox_Err_Group_Send_Message err_send; + tox_group_send_message( + tox, f_gn, TOX_MESSAGE_TYPE_NORMAL, msg, sizeof(msg), &err_send); + }); } sim.run_until( [&]() { tox_iterate(main_tox.get(), &ctx); - for (auto &f : friends) { - tox_iterate(f.tox.get(), nullptr); - } return ctx.count == num_friends; }, 120000); @@ -222,5 +224,180 @@ namespace { EXPECT_EQ(ctx.count, num_friends); } + TEST(ToxNetworkTest, TcpRelayChaining) + { + constexpr bool kDebug = false; + + Simulation sim; + sim.net().set_verbose(false); + + if (kDebug) { + using namespace log_filter; + sim.set_log_filter(level(TOX_LOG_LEVEL_DEBUG) + || (level(TOX_LOG_LEVEL_TRACE) + && (file("TCP") || file("onion.c") || func("dht_isconnected")) + && !message("not sending repeated announce request"))); + } + + struct ToxOptionsDeleter { + void operator()(Tox_Options *opts) { tox_options_free(opts); } + }; + std::unique_ptr opts(tox_options_new(nullptr)); + tox_options_set_udp_enabled(opts.get(), false); + tox_options_set_ipv6_enabled(opts.get(), false); + tox_options_set_local_discovery_enabled(opts.get(), false); + + auto create = [&](const char *name, uint16_t port, bool udp_enabled = false) { + tox_options_set_tcp_port(opts.get(), port); + if (udp_enabled) { + tox_options_set_start_port(opts.get(), port); + tox_options_set_end_port(opts.get(), port); + } + tox_options_set_udp_enabled(opts.get(), udp_enabled); + auto node = sim.create_node(); + auto tox = node->create_tox(opts.get()); + if (!tox) { + std::cerr << "Failed to create node " << name << " on port " << port << std::endl; + std::abort(); + } + return std::make_pair(std::move(node), std::move(tox)); + }; + + // Servers (Enable UDP for relays so they can talk to each other) + auto [nodeA, toxA] = create("A", 20001, true); + auto [nodeB, toxB] = create("B", 20002, true); + + // Clients + auto [nodeC, toxC] = create("C", 0); + auto [nodeD, toxD] = create("D", 0); + auto [nodeE, toxE] = create("E", 0); + auto [nodeF, toxF] = create("F", 0); + + auto get_info = [](SimulatedNode &node, Tox *tox) { + uint8_t pk[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_public_key(tox, pk); + uint8_t dht_id[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_dht_id(tox, dht_id); + + char ip[TOX_INET_ADDRSTRLEN]; + ip_parse_addr(&node.ip, ip, sizeof(ip)); + uint16_t port = tox_self_get_tcp_port(tox, nullptr); + if (kDebug) { + std::cout << "Node Info: IP=" << ip << " Port=" << port << std::endl; + + auto to_hex = [](const uint8_t *data) { + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (int i = 0; i < TOX_PUBLIC_KEY_SIZE; ++i) + ss << std::setw(2) << static_cast(data[i]); + return ss.str(); + }; + + std::cout << "PK: " << to_hex(pk) << std::endl; + std::cout << "DHT ID: " << to_hex(dht_id) << std::endl; + } + + return std::make_tuple(std::vector(pk, pk + TOX_PUBLIC_KEY_SIZE), + std::vector(dht_id, dht_id + TOX_PUBLIC_KEY_SIZE), std::string(ip), port); + }; + + auto [pkA, dhtIdA, ipA, portA] = get_info(*nodeA, toxA.get()); + auto [pkB, dhtIdB, ipB, portB] = get_info(*nodeB, toxB.get()); + + // Helper to connect to a relay (bootstrap + add_tcp_relay) + auto connect_to_relay + = [](Tox *tox, const std::string &ip, uint16_t port, const std::vector &pk, + const std::vector &dht_id) { + Tox_Err_Bootstrap err_bs; + tox_bootstrap(tox, ip.c_str(), port, dht_id.data(), &err_bs); + if (err_bs != TOX_ERR_BOOTSTRAP_OK) { + std::cout << "tox_bootstrap failed with " << err_bs << " for " << ip << ":" + << port << std::endl; + } + Tox_Err_Bootstrap err_relay; + // Use dht_id for TCP relay as well, as server uses DHT key + tox_add_tcp_relay(tox, ip.c_str(), port, dht_id.data(), &err_relay); + if (err_relay != TOX_ERR_BOOTSTRAP_OK) { + std::cout << "tox_add_tcp_relay failed with " << err_relay << " for " << ip + << ":" << port << std::endl; + } + }; + + // Connect {C,D} -> A, {E,F} -> B + connect_to_relay(toxC.get(), ipA, portA, pkA, dhtIdA); + connect_to_relay(toxD.get(), ipA, portA, pkA, dhtIdA); + connect_to_relay(toxE.get(), ipB, portB, pkB, dhtIdB); + connect_to_relay(toxF.get(), ipB, portB, pkB, dhtIdB); + + // B -> A (Connect the two TCP relays, but only one initial link) + connect_to_relay(toxB.get(), ipA, portA, pkA, dhtIdA); + + // Connect C and F + uint8_t pkF[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_public_key(toxF.get(), pkF); + uint8_t pkC[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_public_key(toxC.get(), pkC); + + Tox_Err_Friend_Add err; + const uint32_t fC = tox_friend_add_norequest(toxC.get(), pkF, &err); + ASSERT_EQ(err, TOX_ERR_FRIEND_ADD_OK); + const uint32_t fF = tox_friend_add_norequest(toxF.get(), pkC, &err); + ASSERT_EQ(err, TOX_ERR_FRIEND_ADD_OK); + + struct Context { + bool received = false; + } ctx; + + tox_callback_friend_message(toxF.get(), + [](Tox *, uint32_t, Tox_Message_Type, const uint8_t *, size_t, void *user_data) { + static_cast(user_data)->received = true; + }); + + bool sent = false; + sim.run_until( + [&]() { + tox_iterate(toxA.get(), nullptr); + tox_iterate(toxB.get(), nullptr); + tox_iterate(toxC.get(), nullptr); + tox_iterate(toxD.get(), nullptr); + tox_iterate(toxE.get(), nullptr); + tox_iterate(toxF.get(), &ctx); + + Tox_Connection statusC = tox_friend_get_connection_status(toxC.get(), fC, nullptr); + Tox_Connection statusF = tox_friend_get_connection_status(toxF.get(), fF, nullptr); + + if (kDebug) { + static int loop_counter = 0; + if (loop_counter++ % 100 == 0) { + std::cout << "Conn Status: " + << "A=" << tox_self_get_connection_status(toxA.get()) << " " + << "B=" << tox_self_get_connection_status(toxB.get()) << " " + << "C=" << tox_self_get_connection_status(toxC.get()) << " " + << "D=" << tox_self_get_connection_status(toxC.get()) << " " + << "E=" << tox_self_get_connection_status(toxC.get()) << " " + << "F=" << tox_self_get_connection_status(toxF.get()) << " " + << " Friend Status C->F: " << statusC << ", F->C: " << statusF + << std::endl; + } + } + + if (!sent && statusC != TOX_CONNECTION_NONE) { + const uint8_t msg[] = "hello"; + Tox_Err_Friend_Send_Message send_err; + tox_friend_send_message( + toxC.get(), fC, TOX_MESSAGE_TYPE_NORMAL, msg, sizeof(msg), &send_err); + if (kDebug) { + std::cout << "Message sent from C to F, err=" << send_err << std::endl; + } + sent = true; + } + + return ctx.received; + }, + 120000); + + EXPECT_TRUE(ctx.received); + } + } // namespace } // namespace tox::test diff --git a/toxav/BUILD.bazel b/toxav/BUILD.bazel index 0460aa17..2e7a213b 100644 --- a/toxav/BUILD.bazel +++ b/toxav/BUILD.bazel @@ -17,7 +17,10 @@ cc_library( name = "ring_buffer", srcs = ["ring_buffer.c"], hdrs = ["ring_buffer.h"], - deps = ["//c-toxcore/toxcore:ccompat"], + deps = [ + "//c-toxcore/toxcore:attributes", + "//c-toxcore/toxcore:ccompat", + ], ) cc_test( @@ -26,6 +29,7 @@ cc_test( srcs = ["ring_buffer_test.cc"], deps = [ ":ring_buffer", + "//c-toxcore/toxcore:attributes", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", ], @@ -63,6 +67,7 @@ cc_test( srcs = ["rtp_test.cc"], deps = [ ":rtp", + "//c-toxcore/toxcore:attributes", "//c-toxcore/toxcore:logger", "//c-toxcore/toxcore:mono_time", "//c-toxcore/toxcore:net_crypto", @@ -80,6 +85,7 @@ cc_fuzz_test( deps = [ ":rtp", "//c-toxcore/testing/support", + "//c-toxcore/toxcore:attributes", "//c-toxcore/toxcore:logger", "//c-toxcore/toxcore:mono_time", "//c-toxcore/toxcore:os_memory", @@ -106,8 +112,10 @@ cc_test( srcs = ["bwcontroller_test.cc"], deps = [ ":bwcontroller", + "//c-toxcore/toxcore:attributes", "//c-toxcore/toxcore:logger", "//c-toxcore/toxcore:mono_time", + "//c-toxcore/toxcore:network", "//c-toxcore/toxcore:os_memory", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", @@ -139,6 +147,7 @@ cc_library( ":audio", ":rtp", ":video", + "//c-toxcore/toxcore:attributes", "//c-toxcore/toxcore:logger", "//c-toxcore/toxcore:mono_time", "//c-toxcore/toxcore:os_memory", @@ -156,6 +165,7 @@ cc_test( ":rtp", "//c-toxcore/toxcore:logger", "//c-toxcore/toxcore:mono_time", + "//c-toxcore/toxcore:network", "//c-toxcore/toxcore:os_memory", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", @@ -187,6 +197,7 @@ cc_test( ":video", "//c-toxcore/toxcore:logger", "//c-toxcore/toxcore:mono_time", + "//c-toxcore/toxcore:network", "//c-toxcore/toxcore:os_memory", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", @@ -201,6 +212,7 @@ cc_binary( ":av_test_support", ":rtp", ":video", + "//c-toxcore/toxcore:attributes", "//c-toxcore/toxcore:logger", "//c-toxcore/toxcore:mono_time", "//c-toxcore/toxcore:os_memory", @@ -216,8 +228,10 @@ cc_binary( ":audio", ":av_test_support", ":rtp", + "//c-toxcore/toxcore:attributes", "//c-toxcore/toxcore:logger", "//c-toxcore/toxcore:mono_time", + "//c-toxcore/toxcore:network", "//c-toxcore/toxcore:os_memory", "@benchmark", ], @@ -230,6 +244,7 @@ cc_binary( deps = [ ":av_test_support", ":rtp", + "//c-toxcore/toxcore:attributes", "//c-toxcore/toxcore:logger", "//c-toxcore/toxcore:mono_time", "//c-toxcore/toxcore:os_memory", @@ -255,6 +270,8 @@ cc_test( srcs = ["msi_test.cc"], deps = [ ":msi", + "//c-toxcore/toxcore:attributes", + "//c-toxcore/toxcore:logger", "//c-toxcore/toxcore:os_memory", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", diff --git a/toxav/audio.c b/toxav/audio.c index b4bf42e4..42c1eb4e 100644 --- a/toxav/audio.c +++ b/toxav/audio.c @@ -41,6 +41,8 @@ struct ACSession { pthread_mutex_t queue_mutex[1]; + int16_t *decode_buffer; + uint32_t friend_number; /* Audio frame receive callback */ ac_audio_receive_frame_cb *acb; @@ -96,6 +98,13 @@ ACSession *ac_new(Mono_Time *mono_time, const Logger *log, uint32_t friend_numbe ac->mono_time = mono_time; ac->log = log; + ac->decode_buffer = (int16_t *)malloc(AUDIO_MAX_BUFFER_SIZE_PCM16 * AUDIO_MAX_CHANNEL_COUNT * sizeof(int16_t)); + + if (ac->decode_buffer == nullptr) { + LOGGER_ERROR(log, "Failed to allocate memory for audio buffer"); + goto DECODER_CLEANUP; + } + /* Initialize encoders with default values */ ac->encoder = create_audio_encoder(log, AUDIO_START_BITRATE, AUDIO_START_SAMPLE_RATE, AUDIO_START_CHANNEL_COUNT); @@ -124,6 +133,7 @@ ACSession *ac_new(Mono_Time *mono_time, const Logger *log, uint32_t friend_numbe return ac; DECODER_CLEANUP: + free(ac->decode_buffer); opus_decoder_destroy(ac->decoder); jbuf_free((struct JitterBuffer *)ac->j_buf); BASE_CLEANUP: @@ -141,6 +151,7 @@ void ac_kill(ACSession *ac) opus_encoder_destroy(ac->encoder); opus_decoder_destroy(ac->decoder); jbuf_free((struct JitterBuffer *)ac->j_buf); + free(ac->decode_buffer); pthread_mutex_destroy(ac->queue_mutex); @@ -156,20 +167,12 @@ void ac_iterate(ACSession *ac) /* TODO: fix this and jitter buffering */ - /* Enough space for the maximum frame size (120 ms 48 KHz stereo audio) */ - int16_t *temp_audio_buffer = (int16_t *)malloc(AUDIO_MAX_BUFFER_SIZE_PCM16 * AUDIO_MAX_CHANNEL_COUNT * sizeof(int16_t)); - - if (temp_audio_buffer == nullptr) { - LOGGER_ERROR(ac->log, "Failed to allocate memory for audio buffer"); - return; - } - int rc = 0; pthread_mutex_lock(ac->queue_mutex); - struct JitterBuffer *const j_buf = (struct JitterBuffer *)ac->j_buf; while (true) { + struct JitterBuffer *const j_buf = (struct JitterBuffer *)ac->j_buf; struct RTPMessage *msg = jbuf_read(j_buf, &rc); if (msg == nullptr && rc != 2) { @@ -190,7 +193,7 @@ void ac_iterate(ACSession *ac) LOGGER_WARNING(ac->log, "Invalid PLC parameters: sr %u, dur %u", sampling_rate, frame_duration); } else { const int fs = (sampling_rate * frame_duration) / 1000; - rc = opus_decode(ac->decoder, nullptr, 0, temp_audio_buffer, fs, 1); + rc = opus_decode(ac->decoder, nullptr, 0, ac->decode_buffer, fs, 1); } } else { const uint8_t *msg_data = rtp_message_data(msg); @@ -240,7 +243,7 @@ void ac_iterate(ACSession *ac) * max_size is the max duration of the frame in samples (per channel) that can fit * into the decoded_frame array */ - rc = opus_decode(ac->decoder, msg_data + 4, msg_length - 4, temp_audio_buffer, AUDIO_MAX_BUFFER_SIZE_PCM16, 0); + rc = opus_decode(ac->decoder, msg_data + 4, msg_length - 4, ac->decode_buffer, AUDIO_MAX_BUFFER_SIZE_PCM16, 0); free(msg); } @@ -249,7 +252,7 @@ void ac_iterate(ACSession *ac) } else if (ac->acb != nullptr && ac->lp_sampling_rate != 0) { ac->lp_frame_duration = (rc * 1000) / ac->lp_sampling_rate; - ac->acb(ac->friend_number, temp_audio_buffer, (size_t)rc, ac->lp_channel_count, + ac->acb(ac->friend_number, ac->decode_buffer, (size_t)rc, ac->lp_channel_count, ac->lp_sampling_rate, ac->user_data); } @@ -257,8 +260,6 @@ void ac_iterate(ACSession *ac) } pthread_mutex_unlock(ac->queue_mutex); - - free(temp_audio_buffer); } int ac_queue_message(const Mono_Time *mono_time, void *cs, struct RTPMessage *msg) diff --git a/toxav/audio_bench.cc b/toxav/audio_bench.cc index c218ca58..2b3f7736 100644 --- a/toxav/audio_bench.cc +++ b/toxav/audio_bench.cc @@ -5,9 +5,12 @@ #include #include +#include +#include #include #include +#include "../toxcore/attributes.h" #include "../toxcore/logger.h" #include "../toxcore/mono_time.h" #include "../toxcore/network.h" @@ -22,15 +25,15 @@ class AudioBench : public benchmark::Fixture { public: void SetUp(const ::benchmark::State &state) override { - const Memory *mem = os_memory(); + const Memory *_Nonnull mem = os_memory(); log = logger_new(mem); tm.t = 1000; mono_time = mono_time_new(mem, mock_time_cb, &tm); ac = ac_new(mono_time, log, 123, nullptr, nullptr); - sampling_rate = static_cast(state.range(0)); - channels = static_cast(state.range(1)); - uint32_t bitrate = (channels == 1) ? 32000 : 64000; + sampling_rate = static_cast(state.range(0)); + channels = static_cast(state.range(1)); + std::uint32_t bitrate = (channels == 1) ? 32000 : 64000; ac_reconfigure_encoder(ac, bitrate, sampling_rate, channels); @@ -61,24 +64,24 @@ public: } } - Logger *log = nullptr; - Mono_Time *mono_time = nullptr; + Logger *_Nullable log = nullptr; + Mono_Time *_Nullable mono_time = nullptr; MockTime tm; - ACSession *ac = nullptr; + ACSession *_Nullable ac = nullptr; RtpMock rtp_mock; - uint32_t sampling_rate = 0; - uint8_t channels = 0; - size_t sample_count = 0; - std::vector pcm; + std::uint32_t sampling_rate = 0; + std::uint8_t channels = 0; + std::size_t sample_count = 0; + std::vector pcm; }; // Benchmark encoding a sequence of silent audio frames. BENCHMARK_DEFINE_F(AudioBench, EncodeSilentSequence)(benchmark::State &state) { - std::vector silent_pcm(sample_count * channels); + std::vector silent_pcm(sample_count * channels); fill_silent_frame(channels, sample_count, silent_pcm); - std::vector encoded(2000); + std::vector encoded(2000); for (auto _ : state) { int encoded_size @@ -97,13 +100,13 @@ BENCHMARK_DEFINE_F(AudioBench, EncodeSequence)(benchmark::State &state) { int frame_index = 0; const int num_prefilled = 50; - std::vector> pcms( - num_prefilled, std::vector(sample_count * channels)); + std::vector> pcms( + num_prefilled, std::vector(sample_count * channels)); for (int i = 0; i < num_prefilled; ++i) { fill_audio_frame(sampling_rate, channels, i, sample_count, pcms[i]); } - std::vector encoded(2000); + std::vector encoded(2000); for (auto _ : state) { int idx = frame_index % num_prefilled; @@ -125,16 +128,16 @@ BENCHMARK_REGISTER_F(AudioBench, EncodeSequence) BENCHMARK_DEFINE_F(AudioBench, DecodeSequence)(benchmark::State &state) { const int num_frames = 50; - std::vector> encoded_frames(num_frames); + std::vector> encoded_frames(num_frames); // Pre-encode - std::vector encoded_tmp(2000); + std::vector encoded_tmp(2000); for (int i = 0; i < num_frames; ++i) { fill_audio_frame(sampling_rate, channels, i, sample_count, pcm); int size = ac_encode(ac, pcm.data(), sample_count, encoded_tmp.data(), encoded_tmp.size()); encoded_frames[i].resize(4 + size); - uint32_t net_sr = net_htonl(sampling_rate); + std::uint32_t net_sr = net_htonl(sampling_rate); std::memcpy(encoded_frames[i].data(), &net_sr, 4); std::memcpy(encoded_frames[i].data() + 4, encoded_tmp.data(), size); } @@ -143,7 +146,7 @@ BENCHMARK_DEFINE_F(AudioBench, DecodeSequence)(benchmark::State &state) for (auto _ : state) { int idx = frame_index % num_frames; rtp_send_data(log, rtp_mock.recv_session, encoded_frames[idx].data(), - static_cast(encoded_frames[idx].size()), false); + static_cast(encoded_frames[idx].size()), false); ac_iterate(ac); frame_index++; } @@ -161,26 +164,26 @@ BENCHMARK_DEFINE_F(AudioBench, FullSequence)(benchmark::State &state) { int frame_index = 0; const int num_prefilled = 50; - std::vector> pcms( - num_prefilled, std::vector(sample_count * channels)); + std::vector> pcms( + num_prefilled, std::vector(sample_count * channels)); for (int i = 0; i < num_prefilled; ++i) { fill_audio_frame(sampling_rate, channels, i, sample_count, pcms[i]); } - std::vector encoded_tmp(2000); + std::vector encoded_tmp(2000); for (auto _ : state) { int idx = frame_index % num_prefilled; int size = ac_encode(ac, pcms[idx].data(), sample_count, encoded_tmp.data(), encoded_tmp.size()); - std::vector payload(4 + size); - uint32_t net_sr = net_htonl(sampling_rate); + std::vector payload(4 + size); + std::uint32_t net_sr = net_htonl(sampling_rate); std::memcpy(payload.data(), &net_sr, 4); std::memcpy(payload.data() + 4, encoded_tmp.data(), size); rtp_send_data(log, rtp_mock.recv_session, payload.data(), - static_cast(payload.size()), false); + static_cast(payload.size()), false); ac_iterate(ac); frame_index++; diff --git a/toxav/audio_test.cc b/toxav/audio_test.cc index 7bb65d6b..c3b2d54f 100644 --- a/toxav/audio_test.cc +++ b/toxav/audio_test.cc @@ -4,6 +4,11 @@ #include #include +#include +#include +#include +#include +#include #include #include "../toxcore/logger.h" @@ -38,31 +43,31 @@ TEST_F(AudioTest, EncodeDecodeLoop) nullptr, nullptr, nullptr, ac, RtpMock::audio_cb); rtp_mock.recv_session = recv_rtp; - uint32_t sampling_rate = 48000; - uint8_t channels = 1; - size_t sample_count = 960; // 20ms at 48kHz + std::uint32_t sampling_rate = 48000; + std::uint8_t channels = 1; + std::size_t sample_count = 960; // 20ms at 48kHz // Reconfigure to mono ASSERT_EQ(ac_reconfigure_encoder(ac, 48000, sampling_rate, channels), 0); - std::vector pcm(sample_count * channels); - for (size_t i = 0; i < pcm.size(); ++i) { - pcm[i] = static_cast(i * 10); + std::vector pcm(sample_count * channels); + for (std::size_t i = 0; i < pcm.size(); ++i) { + pcm[i] = static_cast(i * 10); } - std::vector encoded(2000); + std::vector encoded(2000); int encoded_size = ac_encode(ac, pcm.data(), sample_count, encoded.data(), encoded.size()); ASSERT_GT(encoded_size, 0); // Prepare payload: 4 bytes sampling rate + Opus data - std::vector payload(4 + static_cast(encoded_size)); - uint32_t net_sr = net_htonl(sampling_rate); - memcpy(payload.data(), &net_sr, 4); - memcpy(payload.data() + 4, encoded.data(), static_cast(encoded_size)); + std::vector payload(4 + static_cast(encoded_size)); + std::uint32_t net_sr = net_htonl(sampling_rate); + std::memcpy(payload.data(), &net_sr, 4); + std::memcpy(payload.data() + 4, encoded.data(), static_cast(encoded_size)); // Send via RTP int rc = rtp_send_data( - log, send_rtp, payload.data(), static_cast(payload.size()), false); + log, send_rtp, payload.data(), static_cast(payload.size()), false); ASSERT_EQ(rc, 0); // Decode @@ -92,10 +97,10 @@ TEST_F(AudioTest, EncodeDecodeRealistic) nullptr, nullptr, nullptr, ac, RtpMock::audio_cb); rtp_mock.recv_session = recv_rtp; - uint32_t sampling_rate = 48000; - uint8_t channels = 1; - size_t sample_count = 960; // 20ms at 48kHz - uint32_t bitrate = 48000; + std::uint32_t sampling_rate = 48000; + std::uint8_t channels = 1; + std::size_t sample_count = 960; // 20ms at 48kHz + std::uint32_t bitrate = 48000; ASSERT_EQ(ac_reconfigure_encoder(ac, bitrate, sampling_rate, channels), 0); @@ -103,27 +108,28 @@ TEST_F(AudioTest, EncodeDecodeRealistic) double amplitude = 10000.0; const double pi = std::acos(-1.0); - std::vector all_sent; - std::vector all_recv; + std::vector all_sent; + std::vector all_recv; for (int frame = 0; frame < 50; ++frame) { - std::vector pcm(sample_count * channels); - for (size_t i = 0; i < sample_count; ++i) { + std::vector pcm(sample_count * channels); + for (std::size_t i = 0; i < sample_count; ++i) { double t = static_cast(frame * sample_count + i) / sampling_rate; - pcm[i] = static_cast(std::sin(2.0 * pi * frequency * t) * amplitude); + pcm[i] = static_cast(std::sin(2.0 * pi * frequency * t) * amplitude); } all_sent.insert(all_sent.end(), pcm.begin(), pcm.end()); - std::vector encoded(2000); + std::vector encoded(2000); int encoded_size = ac_encode(ac, pcm.data(), sample_count, encoded.data(), encoded.size()); ASSERT_GT(encoded_size, 0); - std::vector payload(4 + static_cast(encoded_size)); - uint32_t net_sr = net_htonl(sampling_rate); - memcpy(payload.data(), &net_sr, 4); - memcpy(payload.data() + 4, encoded.data(), static_cast(encoded_size)); + std::vector payload(4 + static_cast(encoded_size)); + std::uint32_t net_sr = net_htonl(sampling_rate); + std::memcpy(payload.data(), &net_sr, 4); + std::memcpy(payload.data() + 4, encoded.data(), static_cast(encoded_size)); - rtp_send_data(log, send_rtp, payload.data(), static_cast(payload.size()), false); + rtp_send_data( + log, send_rtp, payload.data(), static_cast(payload.size()), false); ac_iterate(ac); @@ -143,7 +149,7 @@ TEST_F(AudioTest, EncodeDecodeRealistic) for (int delay = 3000; delay < 3500; ++delay) { double mse = 0; int count = 0; - for (size_t i = 0; i < 2000; ++i) { // Compare a decent chunk + for (std::size_t i = 0; i < 2000; ++i) { // Compare a decent chunk if (i + delay < all_sent.size() && i < all_recv.size()) { int diff = all_sent[i + delay] - all_recv[i]; mse += static_cast(diff) * diff; @@ -159,7 +165,7 @@ TEST_F(AudioTest, EncodeDecodeRealistic) } } - printf("Best audio delay found: %d samples, Min MSE: %f\n", best_delay, min_mse); + std::printf("Best audio delay found: %d samples, Min MSE: %f\n", best_delay, min_mse); // For 48kbps Opus, the MSE for a sine wave should be quite low once aligned. // 10M is about 20% of the signal power (50M), which is a safe threshold for verification. @@ -183,42 +189,43 @@ TEST_F(AudioTest, EncodeDecodeSiren) nullptr, nullptr, nullptr, ac, RtpMock::audio_cb); rtp_mock.recv_session = recv_rtp; - uint32_t sampling_rate = 48000; - uint8_t channels = 1; - size_t sample_count = 960; // 20ms at 48kHz - uint32_t bitrate = 64000; + std::uint32_t sampling_rate = 48000; + std::uint8_t channels = 1; + std::size_t sample_count = 960; // 20ms at 48kHz + std::uint32_t bitrate = 64000; ASSERT_EQ(ac_reconfigure_encoder(ac, bitrate, sampling_rate, channels), 0); double amplitude = 10000.0; const double pi = std::acos(-1.0); - std::vector all_sent; - std::vector all_recv; + std::vector all_sent; + std::vector all_recv; // 1 second of audio (50 frames) is enough for a siren test for (int frame = 0; frame < 50; ++frame) { - std::vector pcm(sample_count * channels); - for (size_t i = 0; i < sample_count; ++i) { + std::vector pcm(sample_count * channels); + for (std::size_t i = 0; i < sample_count; ++i) { double t = static_cast(frame * sample_count + i) / sampling_rate; // Linear frequency sweep from 50Hz to 440Hz over 1 second // f(t) = 50 + (440-50)/1 * t = 50 + 390t // phi(t) = 2*pi * integral(f(t)) = 2*pi * (50t + 195t^2) double phi = 2.0 * pi * (50.0 * t + 195.0 * t * t); - pcm[i] = static_cast(std::sin(phi) * amplitude); + pcm[i] = static_cast(std::sin(phi) * amplitude); } all_sent.insert(all_sent.end(), pcm.begin(), pcm.end()); - std::vector encoded(2000); + std::vector encoded(2000); int encoded_size = ac_encode(ac, pcm.data(), sample_count, encoded.data(), encoded.size()); ASSERT_GT(encoded_size, 0); - std::vector payload(4 + static_cast(encoded_size)); - uint32_t net_sr = net_htonl(sampling_rate); - memcpy(payload.data(), &net_sr, 4); - memcpy(payload.data() + 4, encoded.data(), static_cast(encoded_size)); + std::vector payload(4 + static_cast(encoded_size)); + std::uint32_t net_sr = net_htonl(sampling_rate); + std::memcpy(payload.data(), &net_sr, 4); + std::memcpy(payload.data() + 4, encoded.data(), static_cast(encoded_size)); - rtp_send_data(log, send_rtp, payload.data(), static_cast(payload.size()), false); + rtp_send_data( + log, send_rtp, payload.data(), static_cast(payload.size()), false); ac_iterate(ac); @@ -229,14 +236,14 @@ TEST_F(AudioTest, EncodeDecodeSiren) ASSERT_FALSE(all_recv.empty()); - auto calculate_mse_at = [&](int delay, size_t window) { + auto calculate_mse_at = [&](int delay, std::size_t window) { double mse = 0; int count = 0; - for (size_t i = 0; i < window; ++i) { + for (std::size_t i = 0; i < window; ++i) { int sent_idx = static_cast(i) + delay; - if (sent_idx >= 0 && static_cast(sent_idx) < all_sent.size() + if (sent_idx >= 0 && static_cast(sent_idx) < all_sent.size() && i < all_recv.size()) { - int diff = all_sent[static_cast(sent_idx)] - all_recv[i]; + int diff = all_sent[static_cast(sent_idx)] - all_recv[i]; mse += static_cast(diff) * diff; count++; } @@ -267,7 +274,7 @@ TEST_F(AudioTest, EncodeDecodeSiren) } } - printf("Best siren audio delay found: %d samples, Min MSE: %f\n", best_delay, min_mse); + std::printf("Best siren audio delay found: %d samples, Min MSE: %f\n", best_delay, min_mse); // For 64kbps Opus, the MSE for a siren wave should be reasonably low once aligned. EXPECT_LT(min_mse, 20000000.0); @@ -287,11 +294,11 @@ TEST_F(AudioTest, ReconfigureEncoder) int rc = ac_reconfigure_encoder(ac, 32000, 24000, 2); ASSERT_EQ(rc, 0); - size_t sample_count = 480; // 20ms at 24kHz - uint8_t channels = 2; - std::vector pcm(sample_count * channels, 0); + std::size_t sample_count = 480; // 20ms at 24kHz + std::uint8_t channels = 2; + std::vector pcm(sample_count * channels, 0); - std::vector encoded(1000); + std::vector encoded(1000); int encoded_size = ac_encode(ac, pcm.data(), sample_count, encoded.data(), encoded.size()); ASSERT_GT(encoded_size, 0); @@ -324,9 +331,9 @@ TEST_F(AudioTest, QueueInvalidMessage) &rtp_mock, nullptr, nullptr, nullptr, ac, RtpMock::audio_cb); rtp_mock.recv_session = audio_recv_rtp; - std::vector dummy_video(100, 0); + std::vector dummy_video(100, 0); int rc = rtp_send_data( - log, video_rtp, dummy_video.data(), static_cast(dummy_video.size()), true); + log, video_rtp, dummy_video.data(), static_cast(dummy_video.size()), true); ASSERT_EQ(rc, 0); // Iterate should NOT trigger callback because payload type was wrong @@ -352,9 +359,9 @@ TEST_F(AudioTest, JitterBufferDuplicate) nullptr, nullptr, nullptr, ac, RtpMock::audio_cb); rtp_mock.recv_session = recv_rtp; - uint8_t dummy_data[100] = {0}; - uint32_t net_sr = net_htonl(48000); - memcpy(dummy_data, &net_sr, 4); + std::uint8_t dummy_data[100] = {0}; + std::uint32_t net_sr = net_htonl(48000); + std::memcpy(dummy_data, &net_sr, 4); rtp_send_data(log, send_rtp, dummy_data, sizeof(dummy_data), false); ASSERT_EQ(rtp_mock.captured_packets.size(), 1u); @@ -393,9 +400,9 @@ TEST_F(AudioTest, JitterBufferOutOfOrder) nullptr, nullptr, nullptr, ac, RtpMock::audio_cb); rtp_mock.recv_session = recv_rtp; - uint8_t dummy_data[100] = {0}; - uint32_t net_sr = net_htonl(48000); - memcpy(dummy_data, &net_sr, 4); + std::uint8_t dummy_data[100] = {0}; + std::uint32_t net_sr = net_htonl(48000); + std::memcpy(dummy_data, &net_sr, 4); // Capture 3 packets rtp_send_data(log, send_rtp, dummy_data, sizeof(dummy_data), false); @@ -440,9 +447,9 @@ TEST_F(AudioTest, PacketLossConcealment) nullptr, nullptr, nullptr, ac, RtpMock::audio_cb); rtp_mock.recv_session = recv_rtp; - uint8_t dummy_data[100] = {0}; - uint32_t net_sr = net_htonl(48000); - memcpy(dummy_data, &net_sr, 4); + std::uint8_t dummy_data[100] = {0}; + std::uint32_t net_sr = net_htonl(48000); + std::memcpy(dummy_data, &net_sr, 4); // Send packet 0 and deliver it immediately. rtp_send_data(log, send_rtp, dummy_data, sizeof(dummy_data), false); @@ -486,9 +493,9 @@ TEST_F(AudioTest, JitterBufferReset) nullptr, nullptr, nullptr, ac, RtpMock::audio_cb); rtp_mock.recv_session = recv_rtp; - uint8_t dummy_data[100] = {0}; - uint32_t net_sr = net_htonl(48000); - memcpy(dummy_data, &net_sr, 4); + std::uint8_t dummy_data[100] = {0}; + std::uint32_t net_sr = net_htonl(48000); + std::memcpy(dummy_data, &net_sr, 4); rtp_send_data(log, send_rtp, dummy_data, sizeof(dummy_data), false); rtp_receive_packet( @@ -531,12 +538,12 @@ TEST_F(AudioTest, DecoderReconfigureCooldown) nullptr, nullptr, nullptr, ac, RtpMock::audio_cb); rtp_mock.recv_session = recv_rtp; - uint8_t dummy_data[100] = {0}; - uint32_t net_sr_48 = net_htonl(48000); - uint32_t net_sr_24 = net_htonl(24000); + std::uint8_t dummy_data[100] = {0}; + std::uint32_t net_sr_48 = net_htonl(48000); + std::uint32_t net_sr_24 = net_htonl(24000); // 1. Reconfigure to 24kHz. The initial sampling rate is 48kHz. - memcpy(dummy_data, &net_sr_24, 4); + std::memcpy(dummy_data, &net_sr_24, 4); rtp_send_data(log, send_rtp, dummy_data, sizeof(dummy_data), false); rtp_receive_packet( recv_rtp, rtp_mock.captured_packets.back().data(), rtp_mock.captured_packets.back().size()); @@ -550,7 +557,7 @@ TEST_F(AudioTest, DecoderReconfigureCooldown) mono_time_update(mono_time); // 3. Attempt to reconfigure back to 48kHz. - memcpy(dummy_data, &net_sr_48, 4); + std::memcpy(dummy_data, &net_sr_48, 4); rtp_send_data(log, send_rtp, dummy_data, sizeof(dummy_data), false); rtp_receive_packet( recv_rtp, rtp_mock.captured_packets.back().data(), rtp_mock.captured_packets.back().size()); @@ -592,9 +599,9 @@ TEST_F(AudioTest, QueueDummyMessage) &rtp_mock, nullptr, nullptr, nullptr, ac, RtpMock::audio_cb); rtp_mock.recv_session = audio_recv_rtp; - std::vector dummy_payload(100, 0); - int rc = rtp_send_data( - log, dummy_rtp, dummy_payload.data(), static_cast(dummy_payload.size()), false); + std::vector dummy_payload(100, 0); + int rc = rtp_send_data(log, dummy_rtp, dummy_payload.data(), + static_cast(dummy_payload.size()), false); ASSERT_EQ(rc, 0); // Iterate should NOT trigger callback because it was a dummy packet @@ -620,9 +627,9 @@ TEST_F(AudioTest, LatePacketReset) nullptr, nullptr, nullptr, ac, RtpMock::audio_cb); rtp_mock.recv_session = recv_rtp; - uint8_t dummy_data[100] = {0}; - uint32_t net_sr = net_htonl(48000); - memcpy(dummy_data, &net_sr, 4); + std::uint8_t dummy_data[100] = {0}; + std::uint32_t net_sr = net_htonl(48000); + std::memcpy(dummy_data, &net_sr, 4); // 1. Send and process the first packet. rtp_send_data(log, send_rtp, dummy_data, sizeof(dummy_data), false); // seq 0 @@ -633,14 +640,14 @@ TEST_F(AudioTest, LatePacketReset) data.sample_count = 0; // 2. Buffer another packet with a different sampling rate (24kHz) but don't process it yet. - uint32_t net_sr_24 = net_htonl(24000); - memcpy(dummy_data, &net_sr_24, 4); + std::uint32_t net_sr_24 = net_htonl(24000); + std::memcpy(dummy_data, &net_sr_24, 4); rtp_send_data(log, send_rtp, dummy_data, sizeof(dummy_data), false); // seq 1 rtp_receive_packet( recv_rtp, rtp_mock.captured_packets[1].data(), rtp_mock.captured_packets[1].size()); // 3. Receive the late packet (seq 0) again. - // This triggers the bug: (uint32_t)(0 - 1) > 16, causing a full jitter buffer reset. + // This triggers the bug: (std::uint32_t)(0 - 1) > 16, causing a full jitter buffer reset. rtp_receive_packet( recv_rtp, rtp_mock.captured_packets[0].data(), rtp_mock.captured_packets[0].size()); @@ -672,9 +679,9 @@ TEST_F(AudioTest, InvalidSamplingRate) rtp_mock.recv_session = recv_rtp; // 1. Send a packet with an absurdly large sampling rate. - uint8_t malicious_data[100] = {0}; - uint32_t net_sr = net_htonl(1000000000); // 1 GHz - memcpy(malicious_data, &net_sr, 4); + std::uint8_t malicious_data[100] = {0}; + std::uint32_t net_sr = net_htonl(1000000000); // 1 GHz + std::memcpy(malicious_data, &net_sr, 4); // Add some dummy Opus data so it's not too short malicious_data[4] = 0x08; @@ -720,7 +727,7 @@ TEST_F(AudioTest, ShortPacket) // 1. Send a packet that is too short (only sampling rate, no Opus data). // The protocol requires 4 bytes SR + at least 1 byte Opus data. - uint8_t short_data[4] = {0, 0, 0xBB, 0x80}; // 48000 + std::uint8_t short_data[4] = {0, 0, 0xBB, 0x80}; // 48000 // rtp_send_data might not like 4 bytes if it expects more, but let's see. rtp_send_data(log, send_rtp, short_data, sizeof(short_data), false); @@ -750,16 +757,16 @@ TEST_F(AudioTest, JitterBufferWrapAround) nullptr, nullptr, nullptr, ac, RtpMock::audio_cb); rtp_mock.recv_session = recv_rtp; - uint8_t dummy_data[100] = {0}; - uint32_t net_sr = net_htonl(48000); - memcpy(dummy_data, &net_sr, 4); + std::uint8_t dummy_data[100] = {0}; + std::uint32_t net_sr = net_htonl(48000); + std::memcpy(dummy_data, &net_sr, 4); // Send enough packets to reach the sequence number wrap-around point (0xFFFF -> 0x0000). // We detect the current sequence number to minimize the number of iterations. - uint16_t seq = 0; + std::uint16_t seq = 0; { rtp_send_data(log, send_rtp, dummy_data, sizeof(dummy_data), false); - const uint8_t *pkt = rtp_mock.captured_packets.back().data(); + const std::uint8_t *pkt = rtp_mock.captured_packets.back().data(); seq = (pkt[3] << 8) | pkt[4]; rtp_receive_packet(recv_rtp, pkt, rtp_mock.captured_packets.back().size()); rtp_mock.captured_packets.clear(); @@ -770,7 +777,7 @@ TEST_F(AudioTest, JitterBufferWrapAround) int to_send = (65532 - seq + 65536) % 65536; for (int i = 0; i < to_send; ++i) { rtp_send_data(log, send_rtp, dummy_data, sizeof(dummy_data), false); - const uint8_t *pkt = rtp_mock.captured_packets.back().data(); + const std::uint8_t *pkt = rtp_mock.captured_packets.back().data(); rtp_receive_packet(recv_rtp, pkt, rtp_mock.captured_packets.back().size()); rtp_mock.captured_packets.clear(); ac_iterate(ac); diff --git a/toxav/av_test_support.cc b/toxav/av_test_support.cc index d77ae2e6..094ef4a7 100644 --- a/toxav/av_test_support.cc +++ b/toxav/av_test_support.cc @@ -2,15 +2,18 @@ #include #include +#include +#include #include #include "../toxcore/os_memory.h" // Mock Time -uint64_t mock_time_cb(void *ud) { return static_cast(ud)->t; } +std::uint64_t mock_time_cb(void *_Nullable ud) { return static_cast(ud)->t; } // RTP Mock -int RtpMock::send_packet(void *user_data, const uint8_t *data, uint16_t length) +int RtpMock::send_packet( + void *_Nullable user_data, const std::uint8_t *_Nonnull data, std::uint16_t length) { auto *self = static_cast(user_data); if (self->capture_packets) { @@ -21,7 +24,7 @@ int RtpMock::send_packet(void *user_data, const uint8_t *data, uint16_t length) self->captured_packets[0].assign(data, data + length); } } else { - self->captured_packets.push_back(std::vector(data, data + length)); + self->captured_packets.push_back(std::vector(data, data + length)); } } if (self->auto_forward && self->recv_session) { @@ -30,45 +33,49 @@ int RtpMock::send_packet(void *user_data, const uint8_t *data, uint16_t length) return 0; } -int RtpMock::audio_cb(const Mono_Time *mono_time, void *cs, RTPMessage *msg) +int RtpMock::audio_cb( + const Mono_Time *_Nonnull mono_time, void *_Nullable cs, RTPMessage *_Nonnull msg) { return ac_queue_message(mono_time, cs, msg); } -int RtpMock::video_cb(const Mono_Time *mono_time, void *cs, RTPMessage *msg) +int RtpMock::video_cb( + const Mono_Time *_Nonnull mono_time, void *_Nullable cs, RTPMessage *_Nonnull msg) { return vc_queue_message(mono_time, cs, msg); } -int RtpMock::noop_cb(const Mono_Time * /*mono_time*/, void * /*cs*/, RTPMessage *msg) +int RtpMock::noop_cb( + const Mono_Time *_Nonnull /*mono_time*/, void *_Nullable /*cs*/, RTPMessage *_Nonnull msg) { std::free(msg); return 0; } // Audio Helpers -void fill_audio_frame(uint32_t sampling_rate, uint8_t channels, int frame_index, - size_t sample_count, std::vector &pcm) +void fill_audio_frame(std::uint32_t sampling_rate, std::uint8_t channels, int frame_index, + std::size_t sample_count, std::vector &pcm) { const double pi = std::acos(-1.0); double amplitude = 10000.0; - for (size_t i = 0; i < sample_count; ++i) { + for (std::size_t i = 0; i < sample_count; ++i) { double t = static_cast(frame_index * sample_count + i) / sampling_rate; // Linear frequency sweep from 50Hz to 440Hz over 1 second (50 frames) // f(t) = 50 + 390t // phi(t) = 2*pi * (50t + 195t^2) double phi = 2.0 * pi * (50.0 * t + 195.0 * t * t); - int16_t val = static_cast(std::sin(phi) * amplitude); - for (uint8_t c = 0; c < channels; ++c) { + std::int16_t val = static_cast(std::sin(phi) * amplitude); + for (std::uint8_t c = 0; c < channels; ++c) { pcm[i * channels + c] = val; } } } -void fill_silent_frame(uint8_t channels, size_t sample_count, std::vector &pcm) +void fill_silent_frame( + std::uint8_t channels, std::size_t sample_count, std::vector &pcm) { - for (size_t i = 0; i < sample_count * channels; ++i) { + for (std::size_t i = 0; i < sample_count * channels; ++i) { // Very low amplitude white noise (simulating silence with background hiss) pcm[i] = (std::rand() % 21) - 10; } @@ -77,8 +84,9 @@ void fill_silent_frame(uint8_t channels, size_t sample_count, std::vector(user_data); self->friend_number = friend_number; @@ -89,8 +97,8 @@ void AudioTestData::receive_frame(uint32_t friend_number, const int16_t *pcm, si } // Video Helpers -void fill_video_frame(uint16_t width, uint16_t height, int frame_index, std::vector &y, - std::vector &u, std::vector &v) +void fill_video_frame(std::uint16_t width, std::uint16_t height, int frame_index, + std::vector &y, std::vector &u, std::vector &v) { // Background (dark gray) std::fill(y.begin(), y.end(), 32); @@ -110,10 +118,10 @@ void fill_video_frame(uint16_t width, uint16_t height, int frame_index, std::vec } } -double calculate_video_mse(uint16_t width, uint16_t height, int32_t ystride, - const std::vector &y_recv, const std::vector &y_orig) +double calculate_video_mse(std::uint16_t width, std::uint16_t height, std::int32_t ystride, + const std::vector &y_recv, const std::vector &y_orig) { - if (y_recv.empty() || y_orig.size() != static_cast(width) * height) { + if (y_recv.empty() || y_orig.size() != static_cast(width) * height) { return 1e10; } @@ -124,16 +132,17 @@ double calculate_video_mse(uint16_t width, uint16_t height, int32_t ystride, mse += diff * diff; } } - return mse / (static_cast(width) * height); + return mse / (static_cast(width) * height); } // Video Test Data Helper VideoTestData::VideoTestData() = default; VideoTestData::~VideoTestData() = default; -void VideoTestData::receive_frame(uint32_t friend_number, uint16_t width, uint16_t height, - const uint8_t *y, const uint8_t *u, const uint8_t *v, int32_t ystride, int32_t ustride, - int32_t vstride, void *user_data) +void VideoTestData::receive_frame(std::uint32_t friend_number, std::uint16_t width, + std::uint16_t height, const std::uint8_t *_Nonnull y, const std::uint8_t *_Nonnull u, + const std::uint8_t *_Nonnull v, std::int32_t ystride, std::int32_t ustride, + std::int32_t vstride, void *_Nullable user_data) { auto *self = static_cast(user_data); self->friend_number = friend_number; @@ -143,12 +152,12 @@ void VideoTestData::receive_frame(uint32_t friend_number, uint16_t width, uint16 self->ustride = ustride; self->vstride = vstride; - self->y.assign(y, y + static_cast(std::abs(ystride)) * height); - self->u.assign(u, u + static_cast(std::abs(ustride)) * (height / 2)); - self->v.assign(v, v + static_cast(std::abs(vstride)) * (height / 2)); + self->y.assign(y, y + static_cast(std::abs(ystride)) * height); + self->u.assign(u, u + static_cast(std::abs(ustride)) * (height / 2)); + self->v.assign(v, v + static_cast(std::abs(vstride)) * (height / 2)); } -double VideoTestData::calculate_mse(const std::vector &y_orig) const +double VideoTestData::calculate_mse(const std::vector &y_orig) const { return calculate_video_mse(width, height, ystride, y, y_orig); } @@ -156,7 +165,7 @@ double VideoTestData::calculate_mse(const std::vector &y_orig) const // Common Test Fixture void AvTest::SetUp() { - const Memory *mem = os_memory(); + mem = os_memory(); log = logger_new(mem); tm.t = 1000; mono_time = mono_time_new(mem, mock_time_cb, &tm); @@ -165,7 +174,6 @@ void AvTest::SetUp() void AvTest::TearDown() { - const Memory *mem = os_memory(); mono_time_free(mem, mono_time); logger_kill(log); } diff --git a/toxav/av_test_support.hh b/toxav/av_test_support.hh index 4c0eb4ad..c42dbdbd 100644 --- a/toxav/av_test_support.hh +++ b/toxav/av_test_support.hh @@ -3,9 +3,11 @@ #include +#include #include #include +#include "../toxcore/attributes.h" #include "../toxcore/logger.h" #include "../toxcore/mono_time.h" #include "audio.h" @@ -14,65 +16,72 @@ // Mock Time struct MockTime { - uint64_t t = 1000; + std::uint64_t t = 1000; }; -uint64_t mock_time_cb(void *ud); +std::uint64_t mock_time_cb(void *_Nullable ud); // RTP Mock struct RtpMock { - RTPSession *recv_session = nullptr; - std::vector> captured_packets; + RTPSession *_Nullable recv_session = nullptr; + std::vector> captured_packets; bool auto_forward = true; bool capture_packets = true; bool store_last_packet_only = false; - static int send_packet(void *user_data, const uint8_t *data, uint16_t length); - static int audio_cb(const Mono_Time *mono_time, void *cs, RTPMessage *msg); - static int video_cb(const Mono_Time *mono_time, void *cs, RTPMessage *msg); - static int noop_cb(const Mono_Time *mono_time, void *cs, RTPMessage *msg); + static int send_packet( + void *_Nullable user_data, const std::uint8_t *_Nonnull data, std::uint16_t length); + static int audio_cb( + const Mono_Time *_Nonnull mono_time, void *_Nullable cs, RTPMessage *_Nonnull msg); + static int video_cb( + const Mono_Time *_Nonnull mono_time, void *_Nullable cs, RTPMessage *_Nonnull msg); + static int noop_cb( + const Mono_Time *_Nonnull mono_time, void *_Nullable cs, RTPMessage *_Nonnull msg); }; // Audio Helpers -void fill_audio_frame(uint32_t sampling_rate, uint8_t channels, int frame_index, - size_t sample_count, std::vector &pcm); -void fill_silent_frame(uint8_t channels, size_t sample_count, std::vector &pcm); +void fill_audio_frame(std::uint32_t sampling_rate, std::uint8_t channels, int frame_index, + std::size_t sample_count, std::vector &pcm); +void fill_silent_frame( + std::uint8_t channels, std::size_t sample_count, std::vector &pcm); struct AudioTestData { - uint32_t friend_number = 0; - std::vector last_pcm; - size_t sample_count = 0; - uint8_t channels = 0; - uint32_t sampling_rate = 0; + std::uint32_t friend_number = 0; + std::vector last_pcm; + std::size_t sample_count = 0; + std::uint8_t channels = 0; + std::uint32_t sampling_rate = 0; AudioTestData(); ~AudioTestData(); - static void receive_frame(uint32_t friend_number, const int16_t *pcm, size_t sample_count, - uint8_t channels, uint32_t sampling_rate, void *user_data); + static void receive_frame(std::uint32_t friend_number, const std::int16_t *_Nonnull pcm, + std::size_t sample_count, std::uint8_t channels, std::uint32_t sampling_rate, + void *_Nullable user_data); }; // Video Helpers -void fill_video_frame(uint16_t width, uint16_t height, int frame_index, std::vector &y, - std::vector &u, std::vector &v); -double calculate_video_mse(uint16_t width, uint16_t height, int32_t ystride, - const std::vector &y_recv, const std::vector &y_orig); +void fill_video_frame(std::uint16_t width, std::uint16_t height, int frame_index, + std::vector &y, std::vector &u, std::vector &v); +double calculate_video_mse(std::uint16_t width, std::uint16_t height, std::int32_t ystride, + const std::vector &y_recv, const std::vector &y_orig); // Video Test Data Helper struct VideoTestData { - uint32_t friend_number = 0; - uint16_t width = 0; - uint16_t height = 0; - std::vector y, u, v; - int32_t ystride = 0, ustride = 0, vstride = 0; + std::uint32_t friend_number = 0; + std::uint16_t width = 0; + std::uint16_t height = 0; + std::vector y, u, v; + std::int32_t ystride = 0, ustride = 0, vstride = 0; VideoTestData(); ~VideoTestData(); - static void receive_frame(uint32_t friend_number, uint16_t width, uint16_t height, - const uint8_t *y, const uint8_t *u, const uint8_t *v, int32_t ystride, int32_t ustride, - int32_t vstride, void *user_data); + static void receive_frame(std::uint32_t friend_number, std::uint16_t width, + std::uint16_t height, const std::uint8_t *_Nonnull y, const std::uint8_t *_Nonnull u, + const std::uint8_t *_Nonnull v, std::int32_t ystride, std::int32_t ustride, + std::int32_t vstride, void *_Nullable user_data); - double calculate_mse(const std::vector &y_orig) const; + double calculate_mse(const std::vector &y_orig) const; }; // Common Test Fixture @@ -81,8 +90,9 @@ protected: void SetUp() override; void TearDown() override; - Logger *log = nullptr; - Mono_Time *mono_time = nullptr; + Logger *_Nullable log = nullptr; + Mono_Time *_Nullable mono_time = nullptr; + const Memory *_Nullable mem = nullptr; MockTime tm; }; diff --git a/toxav/bwcontroller_test.cc b/toxav/bwcontroller_test.cc index 9aaef226..8200ab85 100644 --- a/toxav/bwcontroller_test.cc +++ b/toxav/bwcontroller_test.cc @@ -3,8 +3,11 @@ #include #include +#include +#include #include +#include "../toxcore/attributes.h" #include "../toxcore/logger.h" #include "../toxcore/mono_time.h" #include "../toxcore/network.h" @@ -13,17 +16,18 @@ namespace { struct BwcTimeMock { - uint64_t t; + std::uint64_t t; }; -uint64_t bwc_mock_time_cb(void *ud) { return static_cast(ud)->t; } +std::uint64_t bwc_mock_time_cb(void *ud) { return static_cast(ud)->t; } struct MockBwcData { - std::vector> sent_packets; + std::vector> sent_packets; std::vector reported_losses; - uint32_t friend_number = 0; + std::uint32_t friend_number = 0; - static int send_packet(void *user_data, const uint8_t *data, uint16_t length) + static int send_packet( + void *_Nullable user_data, const std::uint8_t *_Nonnull data, std::uint16_t length) { auto *sd = static_cast(user_data); if (sd->fail_send) { @@ -33,8 +37,8 @@ struct MockBwcData { return 0; } - static void loss_report( - BWController * /*bwc*/, uint32_t friend_number, float loss, void *user_data) + static void loss_report(BWController *_Nonnull /*bwc*/, std::uint32_t friend_number, float loss, + void *_Nullable user_data) { auto *sd = static_cast(user_data); sd->friend_number = friend_number; @@ -57,13 +61,13 @@ protected: void TearDown() override { - const Memory *mem = os_memory(); + const Memory *_Nonnull mem = os_memory(); mono_time_free(mem, mono_time); logger_kill(log); } - Logger *log; - Mono_Time *mono_time; + Logger *_Nullable log; + Mono_Time *_Nullable mono_time; BwcTimeMock tm; }; @@ -79,7 +83,7 @@ TEST_F(BwcTest, BasicNewKill) TEST_F(BwcTest, SendUpdate) { MockBwcData sd; - uint32_t friend_number = 123; + std::uint32_t friend_number = 123; BWController *bwc = bwc_new(log, friend_number, MockBwcData::loss_report, &sd, MockBwcData::send_packet, &sd, mono_time); ASSERT_NE(bwc, nullptr); @@ -107,7 +111,7 @@ TEST_F(BwcTest, SendUpdate) EXPECT_EQ(sd.sent_packets[0][0], BWC_PACKET_ID); // Packet contains lost (4 bytes) and recv (4 bytes) - uint32_t lost, recv; + std::uint32_t lost, recv; net_unpack_u32(sd.sent_packets[0].data() + 1, &lost); net_unpack_u32(sd.sent_packets[0].data() + 5, &recv); @@ -120,12 +124,12 @@ TEST_F(BwcTest, SendUpdate) TEST_F(BwcTest, HandlePacket) { MockBwcData sd; - uint32_t friend_number = 123; + std::uint32_t friend_number = 123; BWController *bwc = bwc_new(log, friend_number, MockBwcData::loss_report, &sd, MockBwcData::send_packet, &sd, mono_time); ASSERT_NE(bwc, nullptr); - uint8_t packet[9]; + std::uint8_t packet[9]; packet[0] = BWC_PACKET_ID; net_pack_u32(packet + 1, 100); // lost net_pack_u32(packet + 5, 900); // recv @@ -155,7 +159,7 @@ TEST_F(BwcTest, InvalidPacketSize) MockBwcData sd; BWController *bwc = bwc_new( log, 123, MockBwcData::loss_report, &sd, MockBwcData::send_packet, &sd, mono_time); - uint8_t packet[10] = {0}; + std::uint8_t packet[10] = {0}; // Correct size is 9 bwc_handle_packet(bwc, packet, 8); @@ -193,7 +197,7 @@ TEST_F(BwcTest, NullCallback) BWController *bwc = bwc_new(log, 123, nullptr, nullptr, MockBwcData::send_packet, &sd, mono_time); - uint8_t packet[9]; + std::uint8_t packet[9]; packet[0] = BWC_PACKET_ID; net_pack_u32(packet + 1, 100); // lost net_pack_u32(packet + 5, 900); // recv @@ -213,7 +217,7 @@ TEST_F(BwcTest, ZeroLoss) log, 123, MockBwcData::loss_report, &sd, MockBwcData::send_packet, &sd, mono_time); // 1. Peer sends update with zero loss - uint8_t packet[9]; + std::uint8_t packet[9]; packet[0] = BWC_PACKET_ID; net_pack_u32(packet + 1, 0); // lost net_pack_u32(packet + 5, 1000); // recv @@ -255,7 +259,7 @@ TEST_F(BwcTest, Overflow) bwc_add_recv(bwc, 1); ASSERT_EQ(sd.sent_packets.size(), 1); - uint32_t lost, recv; + std::uint32_t lost, recv; net_unpack_u32(sd.sent_packets[0].data() + 1, &lost); net_unpack_u32(sd.sent_packets[0].data() + 5, &recv); @@ -324,7 +328,7 @@ TEST_F(BwcTest, RecvPlusLostOverflowBug) MockBwcData sd; BWController *bwc = bwc_new( log, 123, MockBwcData::loss_report, &sd, MockBwcData::send_packet, &sd, mono_time); - uint8_t packet[9]; + std::uint8_t packet[9]; packet[0] = BWC_PACKET_ID; net_pack_u32(packet + 1, 1); net_pack_u32(packet + 5, 0xFFFFFFFF); @@ -342,7 +346,7 @@ TEST_F(BwcTest, RateLimitBypassBug) log, 123, MockBwcData::loss_report, &sd, MockBwcData::send_packet, &sd, mono_time); tm.t = 0xFFFFFFF0; mono_time_update(mono_time); - uint8_t packet[9]; + std::uint8_t packet[9]; packet[0] = BWC_PACKET_ID; net_pack_u32(packet + 1, 1); net_pack_u32(packet + 5, 100); diff --git a/toxav/msi_test.cc b/toxav/msi_test.cc index 0cadef76..2d6e4646 100644 --- a/toxav/msi_test.cc +++ b/toxav/msi_test.cc @@ -6,16 +6,19 @@ #include +#include +#include #include +#include "../toxcore/attributes.h" #include "../toxcore/logger.h" #include "../toxcore/os_memory.h" namespace { struct MockMsi { - std::vector> sent_packets; - std::vector sent_to_friends; + std::vector> sent_packets; + std::vector sent_to_friends; struct CallbackStats { int invite = 0; @@ -26,11 +29,11 @@ struct MockMsi { int capabilities = 0; } stats; - MSICall *last_call = nullptr; + MSICall *_Nullable last_call = nullptr; MSIError last_error = MSI_E_NONE; - static int send_packet( - void *user_data, uint32_t friend_number, const uint8_t *data, size_t length) + static int send_packet(void *_Nullable user_data, std::uint32_t friend_number, + const std::uint8_t *_Nonnull data, std::size_t length) { auto *self = static_cast(user_data); self->sent_packets.emplace_back(data, data + length); @@ -38,7 +41,7 @@ struct MockMsi { return 0; } - static int on_invite(void *object, MSICall *call) + static int on_invite(void *_Nullable object, MSICall *_Nonnull call) { auto *self = static_cast(object); self->stats.invite++; @@ -46,7 +49,7 @@ struct MockMsi { return 0; } - static int on_start(void *object, MSICall *call) + static int on_start(void *_Nullable object, MSICall *_Nonnull call) { auto *self = static_cast(object); self->stats.start++; @@ -54,7 +57,7 @@ struct MockMsi { return 0; } - static int on_end(void *object, MSICall *call) + static int on_end(void *_Nullable object, MSICall *_Nonnull call) { auto *self = static_cast(object); self->stats.end++; @@ -62,7 +65,7 @@ struct MockMsi { return 0; } - static int on_error(void *object, MSICall *call) + static int on_error(void *_Nullable object, MSICall *_Nonnull call) { auto *self = static_cast(object); self->stats.error++; @@ -71,7 +74,7 @@ struct MockMsi { return 0; } - static int on_peertimeout(void *object, MSICall *call) + static int on_peertimeout(void *_Nullable object, MSICall *_Nonnull call) { auto *self = static_cast(object); self->stats.peertimeout++; @@ -79,7 +82,7 @@ struct MockMsi { return 0; } - static int on_capabilities(void *object, MSICall *call) + static int on_capabilities(void *_Nullable object, MSICall *_Nonnull call) { auto *self = static_cast(object); self->stats.capabilities++; @@ -92,7 +95,7 @@ class MsiTest : public ::testing::Test { protected: void SetUp() override { - const Memory *mem = os_memory(); + const Memory *_Nonnull mem = os_memory(); log = logger_new(mem); MSICallbacks callbacks = {MockMsi::on_invite, MockMsi::on_start, MockMsi::on_end, @@ -109,8 +112,8 @@ protected: logger_kill(log); } - Logger *log; - MSISession *session = nullptr; + Logger *_Nullable log; + MSISession *_Nullable session = nullptr; MockMsi mock; }; @@ -122,8 +125,8 @@ TEST_F(MsiTest, BasicNewKill) TEST_F(MsiTest, Invite) { MSICall *call = nullptr; - uint32_t friend_number = 123; - uint8_t capabilities = MSI_CAP_S_AUDIO | MSI_CAP_R_AUDIO; + std::uint32_t friend_number = 123; + std::uint8_t capabilities = MSI_CAP_S_AUDIO | MSI_CAP_R_AUDIO; int rc = msi_invite(log, session, &call, friend_number, capabilities); ASSERT_EQ(rc, 0); @@ -146,11 +149,11 @@ TEST_F(MsiTest, Invite) TEST_F(MsiTest, HandleIncomingInvite) { - uint32_t friend_number = 456; - uint8_t peer_caps = MSI_CAP_S_VIDEO | MSI_CAP_R_VIDEO; + std::uint32_t friend_number = 456; + std::uint8_t peer_caps = MSI_CAP_S_VIDEO | MSI_CAP_R_VIDEO; // Craft invite packet - uint8_t invite_pkt[] = { + std::uint8_t invite_pkt[] = { 1, 1, 0, // ID_REQUEST, len 1, REQU_INIT 3, 1, peer_caps, // ID_CAPABILITIES, len 1, caps 0 // end @@ -168,14 +171,14 @@ TEST_F(MsiTest, HandleIncomingInvite) TEST_F(MsiTest, Answer) { // 1. Receive invite first - uint32_t friend_number = 456; - uint8_t peer_caps = MSI_CAP_S_VIDEO | MSI_CAP_R_VIDEO; - uint8_t invite_pkt[] = {1, 1, 0, 3, 1, peer_caps, 0}; + std::uint32_t friend_number = 456; + std::uint8_t peer_caps = MSI_CAP_S_VIDEO | MSI_CAP_R_VIDEO; + std::uint8_t invite_pkt[] = {1, 1, 0, 3, 1, peer_caps, 0}; msi_handle_packet(session, log, friend_number, invite_pkt, sizeof(invite_pkt)); MSICall *call = mock.last_call; // 2. Answer it - uint8_t my_caps = MSI_CAP_S_AUDIO | MSI_CAP_R_AUDIO; + std::uint8_t my_caps = MSI_CAP_S_AUDIO | MSI_CAP_R_AUDIO; int rc = msi_answer(log, call, my_caps); ASSERT_EQ(rc, 0); EXPECT_EQ(call->state, MSI_CALL_ACTIVE); @@ -206,14 +209,14 @@ TEST_F(MsiTest, Hangup) TEST_F(MsiTest, ChangeCapabilities) { // Setup active call - uint32_t friend_number = 123; - uint8_t invite_pkt[] = {1, 1, 0, 3, 1, 0, 0}; + std::uint32_t friend_number = 123; + std::uint8_t invite_pkt[] = {1, 1, 0, 3, 1, 0, 0}; msi_handle_packet(session, log, friend_number, invite_pkt, sizeof(invite_pkt)); MSICall *call = mock.last_call; msi_answer(log, call, 0); mock.sent_packets.clear(); - uint8_t new_caps = MSI_CAP_S_VIDEO; + std::uint8_t new_caps = MSI_CAP_S_VIDEO; int rc = msi_change_capabilities(log, call, new_caps); ASSERT_EQ(rc, 0); EXPECT_EQ(call->self_capabilities, new_caps); @@ -226,7 +229,7 @@ TEST_F(MsiTest, ChangeCapabilities) TEST_F(MsiTest, PeerTimeout) { MSICall *call = nullptr; - uint32_t friend_number = 123; + std::uint32_t friend_number = 123; msi_invite(log, session, &call, friend_number, 0); msi_call_timeout(session, log, friend_number); @@ -236,12 +239,12 @@ TEST_F(MsiTest, PeerTimeout) TEST_F(MsiTest, RemoteHangup) { - uint32_t friend_number = 123; + std::uint32_t friend_number = 123; MSICall *call = nullptr; msi_invite(log, session, &call, friend_number, 0); // Craft pop packet - uint8_t pop_pkt[] = {1, 1, 2, 0}; // REQU_POP + std::uint8_t pop_pkt[] = {1, 1, 2, 0}; // REQU_POP msi_handle_packet(session, log, friend_number, pop_pkt, sizeof(pop_pkt)); EXPECT_EQ(mock.stats.end, 1); @@ -249,12 +252,12 @@ TEST_F(MsiTest, RemoteHangup) TEST_F(MsiTest, RemoteError) { - uint32_t friend_number = 123; + std::uint32_t friend_number = 123; MSICall *call = nullptr; msi_invite(log, session, &call, friend_number, 0); // Craft error packet (ID_ERROR = 2) - uint8_t error_pkt[] = {1, 1, 2, 2, 1, 1, 0}; // REQU_POP + MSI_E_INVALID_MESSAGE + std::uint8_t error_pkt[] = {1, 1, 2, 2, 1, 1, 0}; // REQU_POP + MSI_E_INVALID_MESSAGE msi_handle_packet(session, log, friend_number, error_pkt, sizeof(error_pkt)); EXPECT_EQ(mock.stats.error, 1); @@ -278,7 +281,7 @@ TEST_F(MsiTest, MultipleConcurrentCalls) msi_hangup(log, call1); // Call 2 should still be there - uint8_t pop_pkt[] = {1, 1, 2, 0}; + std::uint8_t pop_pkt[] = {1, 1, 2, 0}; msi_handle_packet(session, log, 2, pop_pkt, sizeof(pop_pkt)); EXPECT_EQ(mock.stats.end, 1); } @@ -288,8 +291,8 @@ TEST_F(MsiTest, RemoteAnswer) MSICall *call = nullptr; msi_invite(log, session, &call, 123, 0); - uint8_t peer_caps = MSI_CAP_S_AUDIO; - uint8_t push_pkt[] = {1, 1, 1, 3, 1, peer_caps, 0}; // REQU_PUSH + capabilities + std::uint8_t peer_caps = MSI_CAP_S_AUDIO; + std::uint8_t push_pkt[] = {1, 1, 1, 3, 1, peer_caps, 0}; // REQU_PUSH + capabilities msi_handle_packet(session, log, 123, push_pkt, sizeof(push_pkt)); EXPECT_EQ(mock.stats.start, 1); @@ -299,14 +302,14 @@ TEST_F(MsiTest, RemoteAnswer) TEST_F(MsiTest, RemoteCapabilitiesChange) { - uint32_t friend_number = 123; - uint8_t invite_pkt[] = {1, 1, 0, 3, 1, 0, 0}; + std::uint32_t friend_number = 123; + std::uint8_t invite_pkt[] = {1, 1, 0, 3, 1, 0, 0}; msi_handle_packet(session, log, friend_number, invite_pkt, sizeof(invite_pkt)); MSICall *call = mock.last_call; msi_answer(log, call, 0); - uint8_t new_caps = MSI_CAP_S_VIDEO; - uint8_t push_pkt[] = {1, 1, 1, 3, 1, new_caps, 0}; // REQU_PUSH + new capabilities + std::uint8_t new_caps = MSI_CAP_S_VIDEO; + std::uint8_t push_pkt[] = {1, 1, 1, 3, 1, new_caps, 0}; // REQU_PUSH + new capabilities msi_handle_packet(session, log, friend_number, push_pkt, sizeof(push_pkt)); EXPECT_EQ(mock.stats.capabilities, 1); @@ -315,8 +318,8 @@ TEST_F(MsiTest, RemoteCapabilitiesChange) TEST_F(MsiTest, FriendRecall) { - uint32_t friend_number = 123; - uint8_t invite_pkt[] = {1, 1, 0, 3, 1, 0, 0}; + std::uint32_t friend_number = 123; + std::uint8_t invite_pkt[] = {1, 1, 0, 3, 1, 0, 0}; msi_handle_packet(session, log, friend_number, invite_pkt, sizeof(invite_pkt)); MSICall *call = mock.last_call; msi_answer(log, call, 0); @@ -348,30 +351,30 @@ TEST_F(MsiTest, GapInFriendNumbers) TEST_F(MsiTest, InvalidPackets) { - uint32_t friend_number = 123; + std::uint32_t friend_number = 123; // Empty packet - uint8_t empty = 0; + std::uint8_t empty = 0; msi_handle_packet(session, log, friend_number, &empty, 0); // Missing end byte - uint8_t no_end[] = {1, 1, 0}; + std::uint8_t no_end[] = {1, 1, 0}; msi_handle_packet(session, log, friend_number, no_end, sizeof(no_end)); // Invalid ID - uint8_t invalid_id[] = {99, 1, 0, 0}; + std::uint8_t invalid_id[] = {99, 1, 0, 0}; msi_handle_packet(session, log, friend_number, invalid_id, sizeof(invalid_id)); // Invalid size (too large) - uint8_t invalid_size[] = {1, 10, 0, 0}; + std::uint8_t invalid_size[] = {1, 10, 0, 0}; msi_handle_packet(session, log, friend_number, invalid_size, sizeof(invalid_size)); // Invalid size (mismatch) - uint8_t size_mismatch[] = {1, 2, 0, 0}; + std::uint8_t size_mismatch[] = {1, 2, 0, 0}; msi_handle_packet(session, log, friend_number, size_mismatch, sizeof(size_mismatch)); // Missing request field - uint8_t no_request[] = {3, 1, 0, 0}; + std::uint8_t no_request[] = {3, 1, 0, 0}; msi_handle_packet(session, log, friend_number, no_request, sizeof(no_request)); } @@ -387,7 +390,7 @@ TEST_F(MsiTest, CallbackFailure) MSISession *fail_session = msi_new(log, MockMsi::send_packet, &mock, &callbacks, &mock); - uint8_t invite_pkt[] = {1, 1, 0, 3, 1, 0, 0}; + std::uint8_t invite_pkt[] = {1, 1, 0, 3, 1, 0, 0}; msi_handle_packet(fail_session, log, 123, invite_pkt, sizeof(invite_pkt)); // Should have sent an error back @@ -417,14 +420,14 @@ TEST_F(MsiTest, InvalidStates) TEST_F(MsiTest, StrayPackets) { - uint32_t friend_number = 123; + std::uint32_t friend_number = 123; // PUSH for non-existent call - uint8_t push_pkt[] = {1, 1, 1, 3, 1, 0, 0}; + std::uint8_t push_pkt[] = {1, 1, 1, 3, 1, 0, 0}; msi_handle_packet(session, log, friend_number, push_pkt, sizeof(push_pkt)); // POP for non-existent call - uint8_t pop_pkt[] = {1, 1, 2, 0}; + std::uint8_t pop_pkt[] = {1, 1, 2, 0}; msi_handle_packet(session, log, friend_number, pop_pkt, sizeof(pop_pkt)); // Error sent back for stray PUSH diff --git a/toxav/ring_buffer.c b/toxav/ring_buffer.c index 186fa539..008f2078 100644 --- a/toxav/ring_buffer.c +++ b/toxav/ring_buffer.c @@ -66,6 +66,10 @@ bool rb_read(RingBuffer *b, void **p) RingBuffer *rb_new(int size) { + if (size < 0 || size >= 65535) { + return nullptr; + } + RingBuffer *buf = (RingBuffer *)calloc(1, sizeof(RingBuffer)); if (buf == nullptr) { @@ -103,10 +107,14 @@ uint16_t rb_size(const RingBuffer *b) (b->size - b->start) + b->end; } -uint16_t rb_data(const RingBuffer *b, void **dest) +uint16_t rb_data(const RingBuffer *b, void **dest, uint16_t dest_size) { uint16_t i; - const uint16_t size = rb_size(b); + uint16_t size = rb_size(b); + + if (size > dest_size) { + size = dest_size; + } for (i = 0; i < size; ++i) { dest[i] = b->data[(b->start + i) % b->size]; diff --git a/toxav/ring_buffer.h b/toxav/ring_buffer.h index 1adae738..c4067c13 100644 --- a/toxav/ring_buffer.h +++ b/toxav/ring_buffer.h @@ -24,7 +24,7 @@ bool rb_read(RingBuffer *_Nonnull b, void *_Nonnull *_Nullable p); RingBuffer *_Nullable rb_new(int size); void rb_kill(RingBuffer *_Nullable b); uint16_t rb_size(const RingBuffer *_Nonnull b); -uint16_t rb_data(const RingBuffer *_Nonnull b, void *_Nonnull *_Nonnull dest); +uint16_t rb_data(const RingBuffer *_Nonnull b, void *_Nonnull *_Nonnull dest, uint16_t dest_size); #ifdef __cplusplus } /* extern "C" */ diff --git a/toxav/ring_buffer_test.cc b/toxav/ring_buffer_test.cc index 1dfad28c..bc41fc00 100644 --- a/toxav/ring_buffer_test.cc +++ b/toxav/ring_buffer_test.cc @@ -4,8 +4,11 @@ #include #include +#include #include +#include "../toxcore/attributes.h" + namespace { template @@ -23,37 +26,39 @@ public: bool full() const { return rb_full(rb_); } bool empty() const { return rb_empty(rb_); } - T *write(T *p) { return static_cast(rb_write(rb_, p)); } - bool read(T **p) + T *_Nullable write(T *_Nullable p) { return static_cast(rb_write(rb_, p)); } + bool read(T *_Nullable *_Nonnull p) { - void *vp; + void *_Nullable vp; bool res = rb_read(rb_, &vp); *p = static_cast(vp); return res; } - uint16_t size() const { return rb_size(rb_); } - uint16_t data(T **dest) const + std::uint16_t size() const { return rb_size(rb_); } + std::uint16_t data(T *_Nullable *_Nonnull dest, std::uint16_t dest_size) const { - std::vector vdest(size()); - uint16_t res = rb_data(rb_, vdest.data()); - for (uint16_t i = 0; i < size(); i++) { + const std::uint16_t current_size = std::min(size(), dest_size); + std::vector vdest(current_size); + const std::uint16_t res = rb_data(rb_, vdest.data(), current_size); + for (std::uint16_t i = 0; i < res; i++) { dest[i] = static_cast(vdest.at(i)); } return res; } - bool contains(T *p) const + bool contains(T *_Nullable p) const { - std::vector elts(size()); - data(elts.data()); + const std::uint16_t current_size = size(); + std::vector elts(current_size); + data(elts.data(), current_size); return std::find(elts.begin(), elts.end(), p) != elts.end(); } bool ok() const { return rb_ != nullptr; } private: - RingBuffer *rb_; + RingBuffer *_Nullable rb_; }; TEST(RingBuffer, EmptyBufferReportsEmpty) @@ -211,4 +216,27 @@ TEST(RingBuffer, SizeIsLimitedByMaxSize) EXPECT_EQ(rb.size(), 4); } +TEST(RingBuffer, NewWithInvalidSizeReturnsNull) +{ + EXPECT_EQ(nullptr, rb_new(-1)); + EXPECT_EQ(nullptr, rb_new(65535)); +} + +TEST(RingBuffer, DataWithSmallerDestSizeIsTruncated) +{ + TypedRingBuffer rb(4); + ASSERT_TRUE(rb.ok()); + int values[] = {1, 2, 3, 4}; + rb.write(&values[0]); + rb.write(&values[1]); + rb.write(&values[2]); + rb.write(&values[3]); + + int *dest[2]; + std::uint16_t res = rb.data(dest, 2); + EXPECT_EQ(res, 2); + EXPECT_EQ(dest[0], &values[0]); + EXPECT_EQ(dest[1], &values[1]); +} + } // namespace diff --git a/toxav/rtp_bench.cc b/toxav/rtp_bench.cc index 2e834ec2..00b3b2d1 100644 --- a/toxav/rtp_bench.cc +++ b/toxav/rtp_bench.cc @@ -4,9 +4,12 @@ #include +#include +#include #include #include +#include "../toxcore/attributes.h" #include "../toxcore/logger.h" #include "../toxcore/mono_time.h" #include "../toxcore/os_memory.h" @@ -19,7 +22,7 @@ class RtpBench : public benchmark::Fixture { public: void SetUp(const ::benchmark::State &) override { - const Memory *mem = os_memory(); + const Memory *_Nonnull mem = os_memory(); log = logger_new(mem); mono_time = mono_time_new(mem, nullptr, nullptr); @@ -37,19 +40,19 @@ public: logger_kill(log); } - Logger *log = nullptr; - Mono_Time *mono_time = nullptr; - RTPSession *session = nullptr; + Logger *_Nullable log = nullptr; + Mono_Time *_Nullable mono_time = nullptr; + RTPSession *_Nullable session = nullptr; RtpMock mock; }; BENCHMARK_DEFINE_F(RtpBench, SendData)(benchmark::State &state) { - size_t data_size = static_cast(state.range(0)); - std::vector data(data_size, 0xAA); + std::size_t data_size = static_cast(state.range(0)); + std::vector data(data_size, 0xAA); for (auto _ : state) { - rtp_send_data(log, session, data.data(), static_cast(data.size()), false); + rtp_send_data(log, session, data.data(), static_cast(data.size()), false); benchmark::DoNotOptimize(mock.captured_packets.back()); } } @@ -57,10 +60,10 @@ BENCHMARK_REGISTER_F(RtpBench, SendData)->Arg(100)->Arg(1000)->Arg(5000); BENCHMARK_DEFINE_F(RtpBench, ReceivePacket)(benchmark::State &state) { - size_t data_size = static_cast(state.range(0)); - std::vector data(data_size, 0xAA); - rtp_send_data(log, session, data.data(), static_cast(data.size()), false); - std::vector packet = mock.captured_packets.back(); + std::size_t data_size = static_cast(state.range(0)); + std::vector data(data_size, 0xAA); + rtp_send_data(log, session, data.data(), static_cast(data.size()), false); + std::vector packet = mock.captured_packets.back(); for (auto _ : state) { rtp_receive_packet(session, packet.data(), packet.size()); diff --git a/toxav/rtp_fuzz_test.cc b/toxav/rtp_fuzz_test.cc index 12d86ed0..0ec458b9 100644 --- a/toxav/rtp_fuzz_test.cc +++ b/toxav/rtp_fuzz_test.cc @@ -1,10 +1,13 @@ #include "rtp.h" +#include +#include #include #include #include #include "../testing/support/public/fuzz_data.hh" +#include "../toxcore/attributes.h" #include "../toxcore/logger.h" #include "../toxcore/mono_time.h" #include "../toxcore/os_memory.h" @@ -15,12 +18,14 @@ using tox::test::Fuzz_Data; struct MockSessionData { }; -static int mock_send_packet(void * /*user_data*/, const uint8_t * /*data*/, uint16_t /*length*/) +static int mock_send_packet( + void *_Nullable /*user_data*/, const std::uint8_t *_Nonnull /*data*/, std::uint16_t /*length*/) { return 0; } -static int mock_m_cb(const Mono_Time * /*mono_time*/, void * /*cs*/, RTPMessage *msg) +static int mock_m_cb( + const Mono_Time *_Nonnull /*mono_time*/, void *_Nullable /*cs*/, RTPMessage *_Nonnull msg) { std::free(msg); return 0; @@ -28,28 +33,28 @@ static int mock_m_cb(const Mono_Time * /*mono_time*/, void * /*cs*/, RTPMessage void fuzz_rtp_receive(Fuzz_Data &input) { - const Memory *mem = os_memory(); + const Memory *_Nonnull mem = os_memory(); struct LoggerDeleter { - void operator()(Logger *l) { logger_kill(l); } + void operator()(Logger *_Nullable l) { logger_kill(l); } }; std::unique_ptr log(logger_new(mem)); - auto time_cb = [](void *) -> uint64_t { return 0; }; + auto time_cb = [](void *_Nullable) -> std::uint64_t { return 0; }; struct MonoTimeDeleter { - const Memory *m; - void operator()(Mono_Time *t) { mono_time_free(m, t); } + const Memory *_Nonnull m; + void operator()(Mono_Time *_Nullable t) { mono_time_free(m, t); } }; std::unique_ptr mono_time( mono_time_new(mem, time_cb, nullptr), MonoTimeDeleter{mem}); MockSessionData sd; - CONSUME1_OR_RETURN(uint8_t, payload_type_byte, input); + CONSUME1_OR_RETURN(std::uint8_t, payload_type_byte, input); int payload_type = (payload_type_byte % 2 == 0) ? RTP_TYPE_AUDIO : RTP_TYPE_VIDEO; struct RtpSessionDeleter { - Logger *l; - void operator()(RTPSession *s) { rtp_kill(l, s); } + Logger *_Nonnull l; + void operator()(RTPSession *_Nullable s) { rtp_kill(l, s); } }; std::unique_ptr session( rtp_new(log.get(), payload_type, mono_time.get(), mock_send_packet, &sd, nullptr, nullptr, @@ -57,7 +62,7 @@ void fuzz_rtp_receive(Fuzz_Data &input) RtpSessionDeleter{log.get()}); while (!input.empty()) { - CONSUME1_OR_RETURN(uint16_t, len, input); + CONSUME1_OR_RETURN(std::uint16_t, len, input); if (input.size() < len) { len = input.size(); @@ -67,16 +72,16 @@ void fuzz_rtp_receive(Fuzz_Data &input) break; } - const uint8_t *pkt_data = input.consume(__func__, len); + const std::uint8_t *pkt_data = input.consume(__func__, len); rtp_receive_packet(session.get(), pkt_data, len); } } } // namespace -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size); -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { Fuzz_Data input(data, size); fuzz_rtp_receive(input); diff --git a/toxav/rtp_test.cc b/toxav/rtp_test.cc index a764bcd4..b97f46b2 100644 --- a/toxav/rtp_test.cc +++ b/toxav/rtp_test.cc @@ -3,9 +3,14 @@ #include #include +#include +#include +#include +#include #include #include +#include "../toxcore/attributes.h" #include "../toxcore/logger.h" #include "../toxcore/mono_time.h" #include "../toxcore/net_crypto.h" @@ -17,45 +22,47 @@ struct MockSessionData { MockSessionData(); ~MockSessionData(); - std::vector> sent_packets; - std::vector> received_frames; - std::vector received_frame_lengths; - std::vector received_32bit_lengths; - std::vector received_full_lengths; - std::vector received_sequnums; - std::vector received_pts; - std::vector received_flags; + std::vector> sent_packets; + std::vector> received_frames; + std::vector received_frame_lengths; + std::vector received_32bit_lengths; + std::vector received_full_lengths; + std::vector received_sequnums; + std::vector received_pts; + std::vector received_flags; - uint32_t total_bytes_received = 0; - uint32_t total_bytes_lost = 0; + std::uint32_t total_bytes_received = 0; + std::uint32_t total_bytes_lost = 0; }; MockSessionData::MockSessionData() = default; MockSessionData::~MockSessionData() = default; -static int mock_send_packet(void *user_data, const uint8_t *data, uint16_t length) +static int mock_send_packet( + void *_Nullable user_data, const std::uint8_t *_Nonnull data, std::uint16_t length) { auto *sd = static_cast(user_data); sd->sent_packets.emplace_back(data, data + length); return 0; } -static int mock_m_cb(const Mono_Time * /*mono_time*/, void *cs, RTPMessage *msg) +static int mock_m_cb( + const Mono_Time *_Nonnull /*mono_time*/, void *_Nullable cs, RTPMessage *_Nonnull msg) { auto *sd = static_cast(cs); sd->received_pts.push_back(rtp_message_pt(msg)); sd->received_flags.push_back(rtp_message_flags(msg)); - const uint8_t *data = rtp_message_data(msg); - uint32_t len = rtp_message_len(msg); - uint32_t full_len = rtp_message_data_length_full(msg); + const std::uint8_t *_Nonnull data = rtp_message_data(msg); + std::uint32_t len = rtp_message_len(msg); + std::uint32_t full_len = rtp_message_data_length_full(msg); // If full_len is not set (old protocol), use len - uint32_t actual_len = (full_len > 0) ? full_len : len; + std::uint32_t actual_len = (full_len > 0) ? full_len : len; sd->received_frames.emplace_back(data, data + actual_len); - sd->received_frame_lengths.push_back(static_cast(len)); + sd->received_frame_lengths.push_back(static_cast(len)); sd->received_32bit_lengths.push_back(len); sd->received_full_lengths.push_back(full_len); sd->received_sequnums.push_back(rtp_message_sequnum(msg)); @@ -64,13 +71,13 @@ static int mock_m_cb(const Mono_Time * /*mono_time*/, void *cs, RTPMessage *msg) return 0; } -static void mock_add_recv(void *user_data, uint32_t bytes) +static void mock_add_recv(void *_Nullable user_data, std::uint32_t bytes) { auto *sd = static_cast(user_data); sd->total_bytes_received += bytes; } -static void mock_add_lost(void *user_data, uint32_t bytes) +static void mock_add_lost(void *_Nullable user_data, std::uint32_t bytes) { auto *sd = static_cast(user_data); sd->total_bytes_lost += bytes; @@ -88,13 +95,13 @@ protected: void TearDown() override { - const Memory *mem = os_memory(); + const Memory *_Nonnull mem = os_memory(); mono_time_free(mem, mono_time); logger_kill(log); } - Logger *log; - Mono_Time *mono_time; + Logger *_Nullable log; + Mono_Time *_Nullable mono_time; }; TEST_F(RtpPublicTest, BasicAudioSendReceive) @@ -104,7 +111,7 @@ TEST_F(RtpPublicTest, BasicAudioSendReceive) mock_add_recv, mock_add_lost, &sd, &sd, mock_m_cb); ASSERT_NE(session, nullptr); - uint8_t data[] = "Hello RTP"; + std::uint8_t data[] = "Hello RTP"; rtp_send_data(log, session, data, sizeof(data), false); ASSERT_EQ(sd.sent_packets.size(), 1); @@ -128,9 +135,9 @@ TEST_F(RtpPublicTest, LargeVideoFrameFragmentation) mock_add_recv, mock_add_lost, &sd, &sd, mock_m_cb); // Frame larger than MAX_CRYPTO_DATA_SIZE - const uint32_t frame_size = MAX_CRYPTO_DATA_SIZE + 500; - std::vector data(frame_size); - for (uint32_t i = 0; i < frame_size; ++i) + const std::uint32_t frame_size = MAX_CRYPTO_DATA_SIZE + 500; + std::vector data(frame_size); + for (std::uint32_t i = 0; i < frame_size; ++i) data[i] = i & 0xFF; rtp_send_data(log, session, data.data(), frame_size, true); @@ -158,8 +165,8 @@ TEST_F(RtpPublicTest, OutOfOrderVideoPackets) RTPSession *session = rtp_new(log, RTP_TYPE_VIDEO, mono_time, mock_send_packet, &sd, mock_add_recv, mock_add_lost, &sd, &sd, mock_m_cb); - const uint32_t frame_size = MAX_CRYPTO_DATA_SIZE + 100; - std::vector data(frame_size, 0x55); + const std::uint32_t frame_size = MAX_CRYPTO_DATA_SIZE + 100; + std::vector data(frame_size, 0x55); rtp_send_data(log, session, data.data(), frame_size, false); ASSERT_EQ(sd.sent_packets.size(), 2); @@ -183,15 +190,15 @@ TEST_F(RtpPublicTest, HandlingInvalidPackets) mock_add_recv, mock_add_lost, &sd, &sd, mock_m_cb); // Packet too short to even contain the Tox packet ID - uint8_t empty[1]; + std::uint8_t empty[1]; rtp_receive_packet(session, empty, 0); // Packet too short (less than RTP_HEADER_SIZE + 1) - uint8_t short_pkt[10] = {RTP_TYPE_AUDIO}; + std::uint8_t short_pkt[10] = {RTP_TYPE_AUDIO}; rtp_receive_packet(session, short_pkt, sizeof(short_pkt)); // Wrong packet ID (Tox level) - uint8_t wrong_id[RTP_HEADER_SIZE + 10]; + std::uint8_t wrong_id[RTP_HEADER_SIZE + 10]; std::memset(wrong_id, 0, sizeof(wrong_id)); wrong_id[0] = RTP_TYPE_VIDEO; // Session expects AUDIO rtp_receive_packet(session, wrong_id, sizeof(wrong_id)); @@ -239,8 +246,8 @@ TEST_F(RtpPublicTest, LargeAudioFragmentationOldProtocol) mock_add_recv, mock_add_lost, &sd, &sd, mock_m_cb); // Audio doesn't use RTP_LARGE_FRAME, so it uses the old 16-bit offset/length fields - const uint32_t frame_size = MAX_CRYPTO_DATA_SIZE + 500; - std::vector data(frame_size, 0x44); + const std::uint32_t frame_size = MAX_CRYPTO_DATA_SIZE + 500; + std::vector data(frame_size, 0x44); rtp_send_data(log, session, data.data(), frame_size, false); @@ -263,17 +270,17 @@ TEST_F(RtpPublicTest, WorkBufferEvictionAndKeyframePreservation) mock_add_recv, mock_add_lost, &sd, &sd, mock_m_cb); struct TimeMock { - uint64_t t; + std::uint64_t t; } tm = {1000}; - auto time_cb = [](void *ud) -> uint64_t { return static_cast(ud)->t; }; + auto time_cb = [](void *ud) -> std::uint64_t { return static_cast(ud)->t; }; mono_time_set_current_time_callback(mono_time, time_cb, &tm); mono_time_update(mono_time); // USED_RTP_WORKBUFFER_COUNT is 3. // 1. Start a keyframe (frame 0) but don't finish it. - const uint32_t frame_size = MAX_CRYPTO_DATA_SIZE + 100; - std::vector kf_data(frame_size, 0x11); + const std::uint32_t frame_size = MAX_CRYPTO_DATA_SIZE + 100; + std::vector kf_data(frame_size, 0x11); rtp_send_data(log, session, kf_data.data(), frame_size, true); rtp_receive_packet(session, sd.sent_packets[0].data(), sd.sent_packets[0].size()); sd.sent_packets.clear(); @@ -282,7 +289,7 @@ TEST_F(RtpPublicTest, WorkBufferEvictionAndKeyframePreservation) for (int i = 0; i < 2; ++i) { tm.t += 1; mono_time_update(mono_time); - std::vector if_data(frame_size, 0x20 + i); + std::vector if_data(frame_size, 0x20 + i); rtp_send_data(log, session, if_data.data(), frame_size, false); rtp_receive_packet(session, sd.sent_packets[0].data(), sd.sent_packets[0].size()); sd.sent_packets.clear(); @@ -297,7 +304,7 @@ TEST_F(RtpPublicTest, WorkBufferEvictionAndKeyframePreservation) // The new IF should be DROPPED because there's no space and slot 0 is a protected KF. tm.t += 1; mono_time_update(mono_time); - std::vector if3_data(frame_size, 0x33); + std::vector if3_data(frame_size, 0x33); rtp_send_data(log, session, if3_data.data(), frame_size, false); rtp_receive_packet(session, sd.sent_packets[0].data(), sd.sent_packets[0].size()); sd.sent_packets.clear(); @@ -311,7 +318,7 @@ TEST_F(RtpPublicTest, WorkBufferEvictionAndKeyframePreservation) // 5. Start another frame (frame 4). // Now the old KF should be evicted and processed (sent to callback), making room. - std::vector if4_data(frame_size, 0x44); + std::vector if4_data(frame_size, 0x44); rtp_send_data(log, session, if4_data.data(), frame_size, false); rtp_receive_packet(session, sd.sent_packets[0].data(), sd.sent_packets[0].size()); @@ -328,7 +335,7 @@ TEST_F(RtpPublicTest, BwcReporting) RTPSession *session = rtp_new(log, RTP_TYPE_VIDEO, mono_time, mock_send_packet, &sd, mock_add_recv, mock_add_lost, &sd, &sd, mock_m_cb); - uint8_t data[] = "test"; + std::uint8_t data[] = "test"; // DISMISS_FIRST_LOST_VIDEO_PACKET_COUNT is 10. // Packets 1-9 are dismissed. Packet 10 is reported. for (int i = 0; i < 10; ++i) { @@ -351,8 +358,8 @@ TEST_F(RtpPublicTest, OldProtocolEdgeCases) nullptr, nullptr, &sd, mock_m_cb); // 1. Multipart message interrupted by a newer message. - const uint32_t large_size = 5000; - std::vector data(large_size, 0xAA); + const std::uint32_t large_size = 5000; + std::vector data(large_size, 0xAA); rtp_send_data(log, session, data.data(), large_size, false); ASSERT_GE(sd.sent_packets.size(), 2); @@ -361,7 +368,7 @@ TEST_F(RtpPublicTest, OldProtocolEdgeCases) EXPECT_EQ(sd.received_frames.size(), 0); // Send a second message (newer) - std::vector data2 = {0x1, 0x2, 0x3}; + std::vector data2 = {0x1, 0x2, 0x3}; rtp_send_data(log, session, data2.data(), data2.size(), false); // The second message is the last one in sent_packets. rtp_receive_packet(session, sd.sent_packets.back().data(), sd.sent_packets.back().size()); @@ -370,7 +377,7 @@ TEST_F(RtpPublicTest, OldProtocolEdgeCases) ASSERT_EQ(sd.received_frames.size(), 2); EXPECT_LT(sd.received_frame_lengths[0], large_size); EXPECT_EQ(sd.received_pts[0], RTP_TYPE_AUDIO % 128); - EXPECT_EQ(sd.received_frame_lengths[1], static_cast(data2.size())); + EXPECT_EQ(sd.received_frame_lengths[1], static_cast(data2.size())); // 2. Discarding old message part sd.received_frames.clear(); @@ -379,7 +386,7 @@ TEST_F(RtpPublicTest, OldProtocolEdgeCases) sd.received_pts.clear(); // Send a very new message. - std::vector data3 = {0xDE, 0xAD}; + std::vector data3 = {0xDE, 0xAD}; rtp_send_data(log, session, data3.data(), data3.size(), false); rtp_receive_packet(session, sd.sent_packets.back().data(), sd.sent_packets.back().size()); EXPECT_EQ(sd.received_frames.size(), 1); @@ -399,13 +406,19 @@ TEST_F(RtpPublicTest, MoreInvalidPackets) nullptr, nullptr, &sd, mock_m_cb); // Get a valid packet to start with - uint8_t data[] = "test"; + std::uint8_t data[] = "test"; rtp_send_data(log, session, data, sizeof(data), false); - std::vector valid_pkt = sd.sent_packets[0]; + ASSERT_FALSE(sd.sent_packets.empty()); + if (sd.sent_packets.empty()) + return; + const std::vector &src_pkt = sd.sent_packets[0]; + if (src_pkt.size() > 65536) + return; + std::vector valid_pkt(src_pkt.begin(), src_pkt.end()); sd.sent_packets.clear(); // 1. RTPHeader packet type and Tox protocol packet type do not agree - std::vector bad_pkt_1 = valid_pkt; + std::vector bad_pkt_1 = valid_pkt; bad_pkt_1[0] = RTP_TYPE_AUDIO; // Tox ID says AUDIO, but header (byte 2) still says VIDEO rtp_receive_packet(session, bad_pkt_1.data(), bad_pkt_1.size()); EXPECT_EQ(sd.received_frames.size(), 0); @@ -421,7 +434,7 @@ TEST_F(RtpPublicTest, MoreInvalidPackets) // 3. Invalid video packet: offset >= length // From rtp.c, offset_full is at byte 20 and data_length_full at byte 24 of the RTP header. // The RTP header starts at index 1 of the packet. - std::vector bad_pkt_3 = valid_pkt; + std::vector bad_pkt_3 = valid_pkt; // Set offset (bytes 21-24) to be equal to length (bytes 25-28) // For a small packet, both are usually 0 and sizeof(data) respectively. // Let's just make offset very large. @@ -437,10 +450,16 @@ TEST_F(RtpPublicTest, MoreInvalidPackets) nullptr, nullptr, nullptr, &sd, mock_m_cb); rtp_send_data(log, session_audio2, data, sizeof(data), false); - std::vector audio_pkt = sd.sent_packets[0]; + ASSERT_FALSE(sd.sent_packets.empty()); + if (sd.sent_packets.empty()) + return; + const std::vector &src_audio = sd.sent_packets[0]; + if (src_audio.size() > 65536) + return; + std::vector audio_pkt(src_audio.begin(), src_audio.end()); sd.sent_packets.clear(); - std::vector bad_pkt_4 = audio_pkt; + std::vector bad_pkt_4 = audio_pkt; // Set offset_lower (byte 1 + 76) > data_length_lower (byte 1 + 78) bad_pkt_4[1 + 76] = 0x01; // offset = 256 bad_pkt_4[1 + 77] = 0x00; @@ -461,20 +480,20 @@ TEST_F(RtpPublicTest, VideoJitterBufferEdgeCases) nullptr, nullptr, &sd, mock_m_cb); // Use a large frame size to force fragmentation and keep slots occupied - const uint32_t frame_size = MAX_CRYPTO_DATA_SIZE + 100; - std::vector data(frame_size, 0); + const std::uint32_t frame_size = MAX_CRYPTO_DATA_SIZE + 100; + std::vector data(frame_size, 0); // Advancing time for subsequent frames struct TimeMock { - uint64_t t; + std::uint64_t t; } tm = {1000}; - auto time_cb = [](void *ud) -> uint64_t { return static_cast(ud)->t; }; + auto time_cb = [](void *ud) -> std::uint64_t { return static_cast(ud)->t; }; mono_time_set_current_time_callback(mono_time, time_cb, &tm); mono_time_update(mono_time); // 1. Packet too old for work buffer rtp_send_data(log, session, data.data(), frame_size, false); // Time 1000ms - std::vector old_pkt = sd.sent_packets[0]; + std::vector old_pkt = sd.sent_packets[0]; // Receive only first part to keep slot occupied rtp_receive_packet(session, sd.sent_packets[0].data(), sd.sent_packets[0].size()); EXPECT_EQ(sd.received_frames.size(), 0); @@ -501,7 +520,7 @@ TEST_F(RtpPublicTest, VideoJitterBufferEdgeCases) nullptr, &sd, mock_m_cb); // Fill slot 0 with an incomplete Keyframe - std::vector kf_data(frame_size, 0x11); + std::vector kf_data(frame_size, 0x11); tm.t = 3000; mono_time_update(mono_time); rtp_send_data(log, session, kf_data.data(), frame_size, true); @@ -510,7 +529,7 @@ TEST_F(RtpPublicTest, VideoJitterBufferEdgeCases) sd.sent_packets.clear(); // Now send a complete Interframe - std::vector if_data(10, 0x22); + std::vector if_data(10, 0x22); tm.t += 1; mono_time_update(mono_time); rtp_send_data(log, session, if_data.data(), if_data.size(), false); @@ -531,9 +550,9 @@ TEST_F(RtpPublicTest, OldProtocolCorruption) // 1. Packet claiming a smaller length than its payload. // This triggers the condition that previously caused a DoS crash via // an assertion failure in new_message(). - uint8_t data[10] = {0}; + std::uint8_t data[10] = {0}; rtp_send_data(log, session, data, sizeof(data), false); - std::vector pkt = sd.sent_packets[0]; + std::vector pkt = sd.sent_packets[0]; sd.sent_packets.clear(); // Modify data_length_lower (byte 1 + 78) to be 2, while payload is 10. @@ -545,8 +564,8 @@ TEST_F(RtpPublicTest, OldProtocolCorruption) EXPECT_EQ(sd.received_frames.size(), 0); // 2. Corruption check for an EXISTING multipart message. - const uint32_t multipart_size = 5000; - std::vector multipart_data(multipart_size, 0xBB); + const std::uint32_t multipart_size = 5000; + std::vector multipart_data(multipart_size, 0xBB); rtp_send_data(log, session, multipart_data.data(), multipart_size, false); // Receive the first part @@ -554,7 +573,7 @@ TEST_F(RtpPublicTest, OldProtocolCorruption) EXPECT_EQ(sd.received_frames.size(), 0); // Now receive a corrupted second part that claims a weird offset - std::vector corrupted_part = sd.sent_packets[1]; + std::vector corrupted_part = sd.sent_packets[1]; // offset_lower is at byte 76. Set it beyond data_length_lower. corrupted_part[1 + 76] = 0xFF; corrupted_part[1 + 77] = 0xFF; @@ -572,11 +591,11 @@ TEST_F(RtpPublicTest, HugeVideoFrameInternalLength) RTPSession *session = rtp_new(log, RTP_TYPE_VIDEO, mono_time, mock_send_packet, &sd, nullptr, nullptr, nullptr, &sd, mock_m_cb); - // Frame larger than 64KB (uint16_t max) - const uint32_t huge_frame_size = 65540; - std::vector data(huge_frame_size); - for (uint32_t i = 0; i < huge_frame_size; ++i) { - data[i] = static_cast(i & 0xFF); + // Frame larger than 64KB (std::uint16_t max) + const std::uint32_t huge_frame_size = 65540; + std::vector data(huge_frame_size); + for (std::uint32_t i = 0; i < huge_frame_size; ++i) { + data[i] = static_cast(i & 0xFF); } rtp_send_data(log, session, data.data(), huge_frame_size, false); @@ -592,7 +611,7 @@ TEST_F(RtpPublicTest, HugeVideoFrameInternalLength) ASSERT_EQ(sd.received_frames.size(), 1); // This verifies that the internal 32-bit length is working correctly. // We cast huge_frame_size to 16-bit to show what it would have been if it truncated. - EXPECT_NE(static_cast(sd.received_32bit_lengths[0]), huge_frame_size); + EXPECT_NE(static_cast(sd.received_32bit_lengths[0]), huge_frame_size); EXPECT_EQ(sd.received_32bit_lengths[0], huge_frame_size); EXPECT_EQ(sd.received_full_lengths[0], huge_frame_size); EXPECT_EQ(sd.received_frames[0].size(), huge_frame_size); @@ -609,10 +628,10 @@ TEST_F(RtpPublicTest, HeapBufferOverflowRaw) // Manually construct a malicious packet. // 1 byte ID + 80 bytes Header + 200 bytes Payload - const size_t header_size = 80; - const size_t payload_size = 200; - const size_t total_size = 1 + header_size + payload_size; - std::vector pkt(total_size, 0); + const std::size_t header_size = 80; + const std::size_t payload_size = 200; + const std::size_t total_size = 1 + header_size + payload_size; + std::vector pkt(total_size, 0); // 0: Packet ID pkt[0] = RTP_TYPE_VIDEO; @@ -650,21 +669,21 @@ TEST_F(RtpPublicTest, HeapBufferOverflow) nullptr, nullptr, &sd, mock_m_cb); // Common parameters - uint16_t sequnum = 100; - uint32_t timestamp = 12345; - uint32_t ssrc = 0x11223344; + std::uint16_t sequnum = 100; + std::uint32_t timestamp = 12345; + std::uint32_t ssrc = 0x11223344; // --- Packet 1: Small allocation --- // data_length_full = 10 // offset_full = 0 // payload_len = 5 { - uint8_t packet[100]; + std::uint8_t packet[100]; std::memset(packet, 0, sizeof(packet)); packet[0] = RTP_TYPE_VIDEO; // Tox Packet ID // RTP Header - uint8_t *h = &packet[1]; + std::uint8_t *h = &packet[1]; // Byte 0: VE=2 (0x80) h[0] = 0x80; // Byte 1: PT=0x41 @@ -720,11 +739,11 @@ TEST_F(RtpPublicTest, HeapBufferOverflow) // Memcpy to buf->data + 10. Buf was allocated with size 10. Writing 100 // bytes to offset 10 -> Overflow. { - uint8_t packet[200]; + std::uint8_t packet[200]; std::memset(packet, 0, sizeof(packet)); packet[0] = RTP_TYPE_VIDEO; - uint8_t *h = &packet[1]; + std::uint8_t *h = &packet[1]; h[0] = 0x80; h[1] = 0x41; h[2] = (sequnum >> 8) & 0xFF; @@ -769,15 +788,15 @@ TEST_F(RtpPublicTest, AudioHeapBufferOverflow) RTPSession *session = rtp_new(log, RTP_TYPE_AUDIO, mono_time, mock_send_packet, &sd, nullptr, nullptr, nullptr, &sd, mock_m_cb); - uint16_t sequnum = 100; - uint32_t timestamp = 12345; - uint32_t ssrc = 0x11223344; + std::uint16_t sequnum = 100; + std::uint32_t timestamp = 12345; + std::uint32_t ssrc = 0x11223344; - uint8_t packet[200]; + std::uint8_t packet[200]; std::memset(packet, 0, sizeof(packet)); packet[0] = RTP_TYPE_AUDIO; - uint8_t *h = &packet[1]; + std::uint8_t *h = &packet[1]; h[0] = 0x80; h[1] = 0x40; // 64 (Audio) h[2] = (sequnum >> 8) & 0xFF; @@ -816,21 +835,21 @@ TEST_F(RtpPublicTest, HeapBufferOverflowMultipartAudio) RTPSession *session = rtp_new(log, RTP_TYPE_AUDIO, mono_time, mock_send_packet, &sd, nullptr, nullptr, nullptr, &sd, mock_m_cb); - uint16_t sequnum = 200; - uint32_t timestamp = 67890; - uint32_t ssrc = 0x55667788; - uint16_t total_len = 100; + std::uint16_t sequnum = 200; + std::uint32_t timestamp = 67890; + std::uint32_t ssrc = 0x55667788; + std::uint16_t total_len = 100; // --- Packet 1: Allocate buffer --- // data_length_lower = 100 // offset_lower = 0 // payload_len = 10 { - uint8_t packet[200]; + std::uint8_t packet[200]; std::memset(packet, 0, sizeof(packet)); packet[0] = RTP_TYPE_AUDIO; - uint8_t *h = &packet[1]; + std::uint8_t *h = &packet[1]; h[0] = 0x80; h[1] = 0x40; // Audio h[2] = (sequnum >> 8) & 0xFF; @@ -865,11 +884,11 @@ TEST_F(RtpPublicTest, HeapBufferOverflowMultipartAudio) // Check 2: total (100) > offset (95). Safe. // Write: 95 + 10 = 105. Overflow. { - uint8_t packet[200]; + std::uint8_t packet[200]; std::memset(packet, 0, sizeof(packet)); packet[0] = RTP_TYPE_AUDIO; - uint8_t *h = &packet[1]; + std::uint8_t *h = &packet[1]; h[0] = 0x80; h[1] = 0x40; h[2] = (sequnum >> 8) & 0xFF; @@ -905,18 +924,18 @@ TEST_F(RtpPublicTest, HeapBufferOverflowLogRead) RTPSession *session = rtp_new(log, RTP_TYPE_VIDEO, mono_time, mock_send_packet, &sd, nullptr, nullptr, nullptr, &sd, mock_m_cb); - uint16_t sequnum = 123; - uint32_t timestamp = 99999; - uint32_t ssrc = 0x88776655; + std::uint16_t sequnum = 123; + std::uint32_t timestamp = 99999; + std::uint32_t ssrc = 0x88776655; // Packet with data_length_full = 1. // The logger tries to read data[0] and data[1]. // data[1] will be out of bounds if only 1 byte is allocated. - uint8_t packet[100]; + std::uint8_t packet[100]; std::memset(packet, 0, sizeof(packet)); packet[0] = RTP_TYPE_VIDEO; - uint8_t *h = &packet[1]; + std::uint8_t *h = &packet[1]; h[0] = 0x80; h[1] = 0x41; // Video h[2] = (sequnum >> 8) & 0xFF; diff --git a/toxav/toxav.c b/toxav/toxav.c index 2b71145c..3695099d 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c @@ -86,7 +86,7 @@ typedef struct DecodeTimeStats { } DecodeTimeStats; struct ToxAV { - const struct Tox_Memory *mem; + const struct Memory *mem; Logger *log; Tox *tox; MSISession *msi; @@ -95,8 +95,7 @@ struct ToxAV { ToxAVCall **calls; uint32_t calls_tail; uint32_t calls_head; - pthread_mutex_t mutex[1]; - pthread_mutex_t *mutable_mutex; + pthread_mutex_t *mutex; /* Call callback */ toxav_call_cb *ccb; @@ -345,11 +344,17 @@ ToxAV *toxav_new(Tox *tox, Toxav_Err_New *error) goto RETURN; } - if (create_recursive_mutex(av->mutex) != 0) { + av->mutex = (pthread_mutex_t *)mem_alloc(tox->sys.mem, sizeof(pthread_mutex_t)); + if (av->mutex == nullptr) { + rc = TOXAV_ERR_NEW_MALLOC; + goto RETURN; + } + + if (create_recursive_mutex(av->mutex) != 0) { + mem_delete(tox->sys.mem, av->mutex); rc = TOXAV_ERR_NEW_MALLOC; goto RETURN; } - av->mutable_mutex = av->mutex; av->mem = tox->sys.mem; av->log = tox->m->log; @@ -365,7 +370,15 @@ ToxAV *toxav_new(Tox *tox, Toxav_Err_New *error) av->toxav_mono_time = mono_time_new(tox->sys.mem, nullptr, nullptr); if (av->msi == nullptr) { + tox_callback_friend_lossy_packet_per_pktid(av->tox, nullptr, RTP_TYPE_AUDIO); + tox_callback_friend_lossy_packet_per_pktid(av->tox, nullptr, RTP_TYPE_VIDEO); + tox_callback_friend_lossy_packet_per_pktid(av->tox, nullptr, BWC_PACKET_ID); + tox_callback_friend_lossless_packet_per_pktid(av->tox, nullptr, PACKET_ID_MSI); + + mono_time_free(tox->sys.mem, av->toxav_mono_time); + pthread_mutex_destroy(av->mutex); + mem_delete(tox->sys.mem, av->mutex); rc = TOXAV_ERR_NEW_MALLOC; goto RETURN; } @@ -431,6 +444,7 @@ void toxav_kill(ToxAV *av) pthread_mutex_unlock(av->mutex); pthread_mutex_destroy(av->mutex); + mem_delete(av->tox->sys.mem, av->mutex); // set ToxAV object to NULL in toxcore, to signal ToxAV has been shutdown tox_set_av_object(av->tox, nullptr); @@ -441,9 +455,9 @@ void toxav_kill(ToxAV *av) Tox *toxav_get_tox(const ToxAV *av) { Tox *tox; - pthread_mutex_lock(av->mutable_mutex); + pthread_mutex_lock(av->mutex); tox = av->tox; - pthread_mutex_unlock(av->mutable_mutex); + pthread_mutex_unlock(av->mutex); return tox; } @@ -465,15 +479,15 @@ uint32_t toxav_iteration_interval(const ToxAV *av) /** * @brief calc_interval Calculates the needed iteration interval based on previous decode times - * @param av ToxAV struct to work on + * @param mono_time Mono_Time struct to work on * @param stats Statistics to update * @param frame_time the duration of the current frame in ms * @param start_time the timestamp when decoding of this frame started */ -static void calc_interval(const ToxAV *_Nonnull av, DecodeTimeStats *_Nonnull stats, int32_t frame_time, uint64_t start_time) +static void calc_interval(const Mono_Time *_Nonnull mono_time, DecodeTimeStats *_Nonnull stats, int32_t frame_time, uint64_t start_time) { stats->interval = frame_time < stats->average ? 0 : (frame_time - stats->average); - stats->total += current_time_monotonic(av->toxav_mono_time) - start_time; + stats->total += current_time_monotonic(mono_time) - start_time; if (++stats->count == 3) { /* NOTE: Magic Offset for precision */ @@ -497,7 +511,8 @@ static void iterate_common(ToxAV *_Nonnull av, bool audio) return; } - const uint64_t start = current_time_monotonic(av->toxav_mono_time); + const Mono_Time *mono_time = av->toxav_mono_time; + const uint64_t start = current_time_monotonic(mono_time); int32_t frame_time = IDLE_ITERATION_INTERVAL_MS; for (ToxAVCall *i = av->calls[av->calls_head]; i != nullptr; i = i->next) { @@ -547,7 +562,7 @@ static void iterate_common(ToxAV *_Nonnull av, bool audio) } DecodeTimeStats *stats = audio ? &av->audio_stats : &av->video_stats; - calc_interval(av, stats, frame_time, start); + calc_interval(mono_time, stats, frame_time, start); pthread_mutex_unlock(av->mutex); } @@ -1324,6 +1339,16 @@ static int callback_invite(void *_Nonnull object, MSICall *_Nonnull call) return 0; } +static void handle_call_error(ToxAV *toxav, MSICall *call) +{ + invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_ERROR); + + if (call->user_data != nullptr) { + call_kill_transmission((ToxAVCall *)call->user_data); + call_remove((ToxAVCall *)call->user_data); + } +} + static int callback_start(void *_Nonnull object, MSICall *_Nonnull call) { ToxAV *toxav = (ToxAV *)object; @@ -1338,13 +1363,13 @@ static int callback_start(void *_Nonnull object, MSICall *_Nonnull call) } if (!call_prepare_transmission(av_call)) { - callback_error(toxav, call); + handle_call_error(toxav, call); pthread_mutex_unlock(toxav->mutex); return -1; } if (!invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities)) { - callback_error(toxav, call); + handle_call_error(toxav, call); pthread_mutex_unlock(toxav->mutex); return -1; } @@ -1374,12 +1399,7 @@ static int callback_error(void *_Nonnull object, MSICall *_Nonnull call) ToxAV *toxav = (ToxAV *)object; pthread_mutex_lock(toxav->mutex); - invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_ERROR); - - if (call->user_data != nullptr) { - call_kill_transmission((ToxAVCall *)call->user_data); - call_remove((ToxAVCall *)call->user_data); - } + handle_call_error(toxav, call); pthread_mutex_unlock(toxav->mutex); return 0; @@ -1637,7 +1657,7 @@ static bool call_prepare_transmission(ToxAVCall *_Nullable call) { /* Prepare video */ call->vcb = av->vcb; call->vcb_user_data = av->vcb_user_data; - call->video = vc_new(av->log, av->toxav_mono_time, call->friend_number, handle_video_frame, call); + call->video = vc_new(av->mem, av->log, av->toxav_mono_time, call->friend_number, handle_video_frame, call); if (call->video == nullptr) { LOGGER_ERROR(av->log, "Failed to create video codec session"); diff --git a/toxav/toxav.h b/toxav/toxav.h index 27c9c49f..1f6befdb 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h @@ -158,7 +158,7 @@ Tox *toxav_get_tox(const ToxAV *av); /** * Returns the interval in milliseconds when the next toxav_iterate call should - * be. If no call is active at the moment, this function returns 200. + * be. If no call is active at the moment, this function returns 1000. * This function MUST be called from the same thread as toxav_iterate. */ uint32_t toxav_iteration_interval(const ToxAV *av); @@ -178,7 +178,7 @@ void toxav_iterate(ToxAV *av); /** * Returns the interval in milliseconds when the next toxav_audio_iterate call - * should be. If no call is active at the moment, this function returns 200. + * should be. If no call is active at the moment, this function returns 1000. * This function MUST be called from the same thread as toxav_audio_iterate. */ uint32_t toxav_audio_iteration_interval(const ToxAV *av); @@ -194,7 +194,7 @@ void toxav_audio_iterate(ToxAV *av); /** * Returns the interval in milliseconds when the next toxav_video_iterate call - * should be. If no call is active at the moment, this function returns 200. + * should be. If no call is active at the moment, this function returns 1000. * This function MUST be called from the same thread as toxav_video_iterate. */ uint32_t toxav_video_iteration_interval(const ToxAV *av); diff --git a/toxav/video.c b/toxav/video.c index ebb031c0..7e4a117e 100644 --- a/toxav/video.c +++ b/toxav/video.c @@ -28,6 +28,9 @@ struct VCSession { vpx_codec_ctx_t encoder[1]; uint32_t frame_counter; + vpx_image_t raw_encoder_frame; + bool raw_encoder_frame_allocated; + /* decoding */ vpx_codec_ctx_t decoder[1]; struct RingBuffer *vbuf_raw; /* Un-decoded data */ @@ -41,9 +44,9 @@ struct VCSession { vc_video_receive_frame_cb *vcb; void *user_data; - pthread_mutex_t queue_mutex[1]; - pthread_mutex_t *mutable_queue_mutex; + pthread_mutex_t *queue_mutex; const Logger *log; + const Memory *mem; vpx_codec_iter_t iter; }; @@ -159,7 +162,7 @@ static void vc_init_encoder_cfg(const Logger *_Nonnull log, vpx_codec_enc_cfg_t #endif /* 0 */ } -VCSession *vc_new(const Logger *log, const Mono_Time *mono_time, uint32_t friend_number, +VCSession *vc_new(const Memory *mem, const Logger *log, const Mono_Time *mono_time, uint32_t friend_number, vc_video_receive_frame_cb *cb, void *user_data) { if (mono_time == nullptr) { @@ -174,12 +177,21 @@ VCSession *vc_new(const Logger *log, const Mono_Time *mono_time, uint32_t friend return nullptr; } - if (create_recursive_mutex(vc->queue_mutex) != 0) { - LOGGER_WARNING(log, "Failed to create recursive mutex!"); + vc->mem = mem; + + vc->queue_mutex = (pthread_mutex_t *)mem_alloc(mem, sizeof(pthread_mutex_t)); + if (vc->queue_mutex == nullptr) { + LOGGER_WARNING(log, "Allocation failed! Application might misbehave!"); + free(vc); + return nullptr; + } + + if (create_recursive_mutex(vc->queue_mutex) != 0) { + LOGGER_WARNING(log, "Failed to create recursive mutex!"); + mem_delete(mem, vc->queue_mutex); free(vc); return nullptr; } - vc->mutable_queue_mutex = vc->queue_mutex; const int cpu_used_value = VP8E_SET_CPUUSED_VALUE; @@ -284,6 +296,7 @@ BASE_CLEANUP_1: vpx_codec_destroy(vc->decoder); BASE_CLEANUP: pthread_mutex_destroy(vc->queue_mutex); + mem_delete(vc->mem, vc->queue_mutex); rb_kill(vc->vbuf_raw); free(vc); @@ -296,6 +309,10 @@ void vc_kill(VCSession *vc) return; } + if (vc->raw_encoder_frame_allocated) { + vpx_img_free(&vc->raw_encoder_frame); + } + vpx_codec_destroy(vc->encoder); vpx_codec_destroy(vc->decoder); void *p; @@ -306,6 +323,7 @@ void vc_kill(VCSession *vc) rb_kill(vc->vbuf_raw); pthread_mutex_destroy(vc->queue_mutex); + mem_delete(vc->mem, vc->queue_mutex); LOGGER_DEBUG(vc->log, "Terminated video handler: %p", (void *)vc); free(vc); } @@ -493,27 +511,34 @@ int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uin int vc_encode(VCSession *vc, uint16_t width, uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, int encode_flags) { - vpx_image_t img; + if (vc->raw_encoder_frame_allocated && (vc->raw_encoder_frame.d_w != width || vc->raw_encoder_frame.d_h != height)) { + vpx_img_free(&vc->raw_encoder_frame); + vc->raw_encoder_frame_allocated = false; + } - // TODO(Green-Sky): figure out stride_align - // TODO(Green-Sky): check memory alignment? - if (vpx_img_wrap(&img, VPX_IMG_FMT_I420, width, height, 0, (uint8_t *)y) != nullptr) { + vpx_image_t *img = nullptr; + vpx_image_t img_wrapped; + if (vpx_img_wrap(&img_wrapped, VPX_IMG_FMT_I420, width, height, 1, (uint8_t *)y) != nullptr) { + img = &img_wrapped; // vpx_img_wrap assumes contigues memory, so we fix that - img.planes[VPX_PLANE_U] = (uint8_t *)u; - img.planes[VPX_PLANE_V] = (uint8_t *)v; + img->planes[VPX_PLANE_U] = (uint8_t *)u; + img->planes[VPX_PLANE_V] = (uint8_t *)v; } else { // call to wrap failed, falling back to copy - if (vpx_img_alloc(&img, VPX_IMG_FMT_I420, width, height, 0) == nullptr) { - LOGGER_ERROR(vc->log, "Could not allocate image for frame"); - return -1; + if (!vc->raw_encoder_frame_allocated) { + if (vpx_img_alloc(&vc->raw_encoder_frame, VPX_IMG_FMT_I420, width, height, 1) == nullptr) { + LOGGER_ERROR(vc->log, "Could not allocate image for frame"); + return -1; + } + + vc->raw_encoder_frame_allocated = true; } - /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes." - * http://fourcc.org/yuv.php#IYUV - */ - memcpy(img.planes[VPX_PLANE_Y], y, (size_t)width * height); - memcpy(img.planes[VPX_PLANE_U], u, ((size_t)width / 2) * (height / 2)); - memcpy(img.planes[VPX_PLANE_V], v, ((size_t)width / 2) * (height / 2)); + img = &vc->raw_encoder_frame; + + memcpy(img->planes[VPX_PLANE_Y], y, (size_t)width * height); + memcpy(img->planes[VPX_PLANE_U], u, ((size_t)width / 2) * (height / 2)); + memcpy(img->planes[VPX_PLANE_V], v, ((size_t)width / 2) * (height / 2)); } int vpx_flags = 0; @@ -522,11 +547,9 @@ int vc_encode(VCSession *vc, uint16_t width, uint16_t height, const uint8_t *y, vpx_flags |= VPX_EFLAG_FORCE_KF; } - const vpx_codec_err_t vrc = vpx_codec_encode(vc->encoder, &img, + const vpx_codec_err_t vrc = vpx_codec_encode(vc->encoder, img, vc->frame_counter, 1, vpx_flags, VPX_DL_REALTIME); - vpx_img_free(&img); - if (vrc != VPX_CODEC_OK) { LOGGER_ERROR(vc->log, "Could not encode video frame: %s", vpx_codec_err_to_string(vrc)); return -1; @@ -558,15 +581,15 @@ int vc_get_cx_data(VCSession *vc, uint8_t **data, uint32_t *size, bool *is_keyfr uint32_t vc_get_lcfd(const VCSession *vc) { uint32_t lcfd; - pthread_mutex_lock(vc->mutable_queue_mutex); + pthread_mutex_lock(vc->queue_mutex); lcfd = vc->lcfd; - pthread_mutex_unlock(vc->mutable_queue_mutex); + pthread_mutex_unlock(vc->queue_mutex); return lcfd; } pthread_mutex_t *vc_get_queue_mutex(VCSession *vc) { - return &vc->queue_mutex[0]; + return vc->queue_mutex; } void vc_increment_frame_counter(VCSession *vc) diff --git a/toxav/video.h b/toxav/video.h index 35dd9978..06876614 100644 --- a/toxav/video.h +++ b/toxav/video.h @@ -27,7 +27,7 @@ typedef struct VCSession VCSession; struct RTPMessage; -VCSession *_Nullable vc_new(const Logger *_Nonnull log, const Mono_Time *_Nonnull mono_time, uint32_t friend_number, +VCSession *_Nullable vc_new(const Memory *_Nonnull mem, const Logger *_Nonnull log, const Mono_Time *_Nonnull mono_time, uint32_t friend_number, vc_video_receive_frame_cb *_Nullable cb, void *_Nullable user_data); void vc_kill(VCSession *_Nullable vc); void vc_iterate(VCSession *_Nullable vc); diff --git a/toxav/video_bench.cc b/toxav/video_bench.cc index 4b376fb0..8fc0ecc1 100644 --- a/toxav/video_bench.cc +++ b/toxav/video_bench.cc @@ -4,8 +4,12 @@ #include +#include +#include +#include #include +#include "../toxcore/attributes.h" #include "../toxcore/logger.h" #include "../toxcore/mono_time.h" #include "../toxcore/os_memory.h" @@ -19,20 +23,20 @@ class VideoBench : public benchmark::Fixture { public: void SetUp(const ::benchmark::State &state) override { - const Memory *mem = os_memory(); + const Memory *_Nonnull mem = os_memory(); log = logger_new(mem); tm.t = 1000; mono_time = mono_time_new(mem, mock_time_cb, &tm); - vc = vc_new(log, mono_time, 123, nullptr, nullptr); + vc = vc_new(mem, log, mono_time, 123, nullptr, nullptr); - width = static_cast(state.range(0)); - height = static_cast(state.range(1)); + width = static_cast(state.range(0)); + height = static_cast(state.range(1)); // Use a standard bitrate for benchmarks vc_reconfigure_encoder(vc, 2000, width, height, -1); - y.resize(static_cast(width) * height); - u.resize((static_cast(width) / 2) * (static_cast(height) / 2)); - v.resize((static_cast(width) / 2) * (static_cast(height) / 2)); + y.resize(static_cast(width) * height); + u.resize((static_cast(width) / 2) * (static_cast(height) / 2)); + v.resize((static_cast(width) / 2) * (static_cast(height) / 2)); rtp_mock.capture_packets = false; // Disable capturing for benchmarks rtp_mock.auto_forward = true; @@ -58,13 +62,13 @@ public: } } - Logger *log = nullptr; - Mono_Time *mono_time = nullptr; + Logger *_Nullable log = nullptr; + Mono_Time *_Nullable mono_time = nullptr; MockTime tm; - VCSession *vc = nullptr; + VCSession *_Nullable vc = nullptr; RtpMock rtp_mock; - uint16_t width = 0, height = 0; - std::vector y, u, v; + std::uint16_t width = 0, height = 0; + std::vector y, u, v; }; // Benchmark encoding a sequence of frames. @@ -74,11 +78,12 @@ BENCHMARK_DEFINE_F(VideoBench, EncodeSequence)(benchmark::State &state) int frame_index = 0; // Pre-fill frames to avoid measuring fill_frame time const int num_prefilled = 100; - std::vector> ys(num_prefilled, std::vector(width * height)); - std::vector> us( - num_prefilled, std::vector((width / 2) * (height / 2))); - std::vector> vs( - num_prefilled, std::vector((width / 2) * (height / 2))); + std::vector> ys( + num_prefilled, std::vector(width * height)); + std::vector> us( + num_prefilled, std::vector((width / 2) * (height / 2))); + std::vector> vs( + num_prefilled, std::vector((width / 2) * (height / 2))); for (int i = 0; i < num_prefilled; ++i) { fill_video_frame(width, height, i, ys[i], us[i], vs[i]); } @@ -90,8 +95,8 @@ BENCHMARK_DEFINE_F(VideoBench, EncodeSequence)(benchmark::State &state) vc_encode(vc, width, height, ys[idx].data(), us[idx].data(), vs[idx].data(), flags); vc_increment_frame_counter(vc); - uint8_t *pkt_data; - uint32_t pkt_size; + std::uint8_t *pkt_data; + std::uint32_t pkt_size; bool is_keyframe; while (vc_get_cx_data(vc, &pkt_data, &pkt_size, &is_keyframe)) { benchmark::DoNotOptimize(pkt_data); @@ -112,7 +117,7 @@ BENCHMARK_REGISTER_F(VideoBench, EncodeSequence) BENCHMARK_DEFINE_F(VideoBench, DecodeSequence)(benchmark::State &state) { const int num_frames = 100; - std::vector> encoded_frames(num_frames); + std::vector> encoded_frames(num_frames); std::vector is_keyframe_list(num_frames); // Pre-encode @@ -122,8 +127,8 @@ BENCHMARK_DEFINE_F(VideoBench, DecodeSequence)(benchmark::State &state) vc_encode(vc, width, height, y.data(), u.data(), v.data(), flags); vc_increment_frame_counter(vc); - uint8_t *pkt_data; - uint32_t pkt_size; + std::uint8_t *pkt_data; + std::uint32_t pkt_size; bool is_kf; while (vc_get_cx_data(vc, &pkt_data, &pkt_size, &is_kf)) { encoded_frames[i].insert(encoded_frames[i].end(), pkt_data, pkt_data + pkt_size); @@ -136,7 +141,7 @@ BENCHMARK_DEFINE_F(VideoBench, DecodeSequence)(benchmark::State &state) int idx = frame_index % num_frames; const auto &encoded_data = encoded_frames[idx]; rtp_send_data(log, rtp_mock.recv_session, encoded_data.data(), - static_cast(encoded_data.size()), is_keyframe_list[idx]); + static_cast(encoded_data.size()), is_keyframe_list[idx]); vc_iterate(vc); frame_index++; } @@ -153,11 +158,12 @@ BENCHMARK_DEFINE_F(VideoBench, FullSequence)(benchmark::State &state) { int frame_index = 0; const int num_prefilled = 100; - std::vector> ys(num_prefilled, std::vector(width * height)); - std::vector> us( - num_prefilled, std::vector((width / 2) * (height / 2))); - std::vector> vs( - num_prefilled, std::vector((width / 2) * (height / 2))); + std::vector> ys( + num_prefilled, std::vector(width * height)); + std::vector> us( + num_prefilled, std::vector((width / 2) * (height / 2))); + std::vector> vs( + num_prefilled, std::vector((width / 2) * (height / 2))); for (int i = 0; i < num_prefilled; ++i) { fill_video_frame(width, height, i, ys[i], us[i], vs[i]); } @@ -168,17 +174,17 @@ BENCHMARK_DEFINE_F(VideoBench, FullSequence)(benchmark::State &state) vc_encode(vc, width, height, ys[idx].data(), us[idx].data(), vs[idx].data(), flags); vc_increment_frame_counter(vc); - uint8_t *pkt_data; - uint32_t pkt_size; + std::uint8_t *pkt_data; + std::uint32_t pkt_size; bool is_keyframe = false; // We need to collect all packets for the frame before sending to decoder - std::vector frame_data; + std::vector frame_data; while (vc_get_cx_data(vc, &pkt_data, &pkt_size, &is_keyframe)) { frame_data.insert(frame_data.end(), pkt_data, pkt_data + pkt_size); } rtp_send_data(log, rtp_mock.recv_session, frame_data.data(), - static_cast(frame_data.size()), is_keyframe); + static_cast(frame_data.size()), is_keyframe); vc_iterate(vc); frame_index++; diff --git a/toxav/video_test.cc b/toxav/video_test.cc index db5032d7..91cc0d99 100644 --- a/toxav/video_test.cc +++ b/toxav/video_test.cc @@ -3,7 +3,11 @@ #include #include -#include +#include +#include +#include +#include +#include #include #include "../toxcore/logger.h" @@ -20,7 +24,7 @@ using VideoTest = AvTest; TEST_F(VideoTest, BasicNewKill) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); vc_kill(vc); } @@ -28,7 +32,7 @@ TEST_F(VideoTest, BasicNewKill) TEST_F(VideoTest, EncodeDecodeLoop) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); RtpMock rtp_mock; @@ -38,21 +42,21 @@ TEST_F(VideoTest, EncodeDecodeLoop) nullptr, nullptr, nullptr, vc, RtpMock::video_cb); rtp_mock.recv_session = recv_rtp; - uint16_t width = 320; - uint16_t height = 240; - uint32_t bitrate = 500; + std::uint16_t width = 320; + std::uint16_t height = 240; + std::uint32_t bitrate = 500; ASSERT_EQ(vc_reconfigure_encoder(vc, bitrate, width, height, -1), 0); - std::vector y(width * height, 128); - std::vector u((width / 2) * (height / 2), 64); - std::vector v((width / 2) * (height / 2), 192); + std::vector y(width * height, 128); + std::vector u((width / 2) * (height / 2), 64); + std::vector v((width / 2) * (height / 2), 192); ASSERT_EQ(vc_encode(vc, width, height, y.data(), u.data(), v.data(), VC_EFLAG_FORCE_KF), 0); vc_increment_frame_counter(vc); - uint8_t *pkt_data; - uint32_t pkt_size; + std::uint8_t *pkt_data; + std::uint32_t pkt_size; bool is_keyframe; while (vc_get_cx_data(vc, &pkt_data, &pkt_size, &is_keyframe)) { @@ -75,7 +79,7 @@ TEST_F(VideoTest, EncodeDecodeLoop) TEST_F(VideoTest, EncodeDecodeSequence) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); RtpMock rtp_mock; @@ -85,16 +89,16 @@ TEST_F(VideoTest, EncodeDecodeSequence) nullptr, nullptr, nullptr, vc, RtpMock::video_cb); rtp_mock.recv_session = recv_rtp; - uint16_t width = 320; - uint16_t height = 240; - uint32_t bitrate = 2000; + std::uint16_t width = 320; + std::uint16_t height = 240; + std::uint32_t bitrate = 2000; ASSERT_EQ(vc_reconfigure_encoder(vc, bitrate, width, height, -1), 0); for (int i = 0; i < 20; ++i) { - std::vector y(width * height); - std::vector u((width / 2) * (height / 2)); - std::vector v((width / 2) * (height / 2)); + std::vector y(width * height); + std::vector u((width / 2) * (height / 2)); + std::vector v((width / 2) * (height / 2)); // Background std::fill(y.begin(), y.end(), 16); @@ -116,8 +120,8 @@ TEST_F(VideoTest, EncodeDecodeSequence) 0); vc_increment_frame_counter(vc); - uint8_t *pkt_data; - uint32_t pkt_size; + std::uint8_t *pkt_data; + std::uint32_t pkt_size; bool is_keyframe; while (vc_get_cx_data(vc, &pkt_data, &pkt_size, &is_keyframe)) { @@ -142,7 +146,7 @@ TEST_F(VideoTest, EncodeDecodeSequence) TEST_F(VideoTest, EncodeDecodeResolutionChange) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); RtpMock rtp_mock; @@ -152,20 +156,20 @@ TEST_F(VideoTest, EncodeDecodeResolutionChange) nullptr, nullptr, nullptr, vc, RtpMock::video_cb); rtp_mock.recv_session = recv_rtp; - uint16_t widths[] = {320, 160, 480}; - uint16_t heights[] = {240, 120, 360}; + std::uint16_t widths[] = {320, 160, 480}; + std::uint16_t heights[] = {240, 120, 360}; for (int res = 0; res < 3; ++res) { - uint16_t width = widths[res]; - uint16_t height = heights[res]; + std::uint16_t width = widths[res]; + std::uint16_t height = heights[res]; ASSERT_EQ(vc_reconfigure_encoder(vc, 2000, width, height, -1), 0); for (int i = 0; i < 5; ++i) { - std::vector y(width * height); - std::vector u((width / 2) * (height / 2)); - std::vector v((width / 2) * (height / 2)); + std::vector y(width * height); + std::vector u((width / 2) * (height / 2)); + std::vector v((width / 2) * (height / 2)); - std::fill(y.begin(), y.end(), static_cast((res * 50 + i * 10) % 256)); + std::fill(y.begin(), y.end(), static_cast((res * 50 + i * 10) % 256)); std::fill(u.begin(), u.end(), 128); std::fill(v.begin(), v.end(), 128); @@ -174,8 +178,8 @@ TEST_F(VideoTest, EncodeDecodeResolutionChange) 0); vc_increment_frame_counter(vc); - uint8_t *pkt_data; - uint32_t pkt_size; + std::uint8_t *pkt_data; + std::uint32_t pkt_size; bool is_keyframe; while (vc_get_cx_data(vc, &pkt_data, &pkt_size, &is_keyframe)) { @@ -199,15 +203,15 @@ TEST_F(VideoTest, EncodeDecodeResolutionChange) TEST_F(VideoTest, EncodeDecodeBitrateImpact) { - uint32_t bitrates[] = {100, 500, 2000}; + std::uint32_t bitrates[] = {100, 500, 2000}; double mses[3]; - uint16_t width = 320; - uint16_t height = 240; + std::uint16_t width = 320; + std::uint16_t height = 240; for (int b = 0; b < 3; ++b) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); RtpMock rtp_mock; @@ -222,12 +226,12 @@ TEST_F(VideoTest, EncodeDecodeBitrateImpact) double total_mse = 0; int frames = 10; for (int i = 0; i < frames; ++i) { - std::vector y(width * height); - std::vector u((width / 2) * (height / 2)); - std::vector v((width / 2) * (height / 2)); + std::vector y(width * height); + std::vector u((width / 2) * (height / 2)); + std::vector v((width / 2) * (height / 2)); - for (size_t j = 0; j < y.size(); ++j) - y[j] = static_cast((j + i * 10) % 256); + for (std::size_t j = 0; j < y.size(); ++j) + y[j] = static_cast((j + i * 10) % 256); std::fill(u.begin(), u.end(), 128); std::fill(v.begin(), v.end(), 128); @@ -236,8 +240,8 @@ TEST_F(VideoTest, EncodeDecodeBitrateImpact) 0); vc_increment_frame_counter(vc); - uint8_t *pkt_data; - uint32_t pkt_size; + std::uint8_t *pkt_data; + std::uint32_t pkt_size; bool is_keyframe; while (vc_get_cx_data(vc, &pkt_data, &pkt_size, &is_keyframe)) { @@ -260,13 +264,13 @@ TEST_F(VideoTest, EncodeDecodeBitrateImpact) EXPECT_GT(mses[0], mses[1]); EXPECT_GT(mses[1], mses[2]); - printf("MSE results: 100kbps: %f, 500kbps: %f, 2000kbps: %f\n", mses[0], mses[1], mses[2]); + std::printf("MSE results: 100kbps: %f, 500kbps: %f, 2000kbps: %f\n", mses[0], mses[1], mses[2]); } TEST_F(VideoTest, ReconfigureEncoder) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); // Initial reconfigure @@ -275,9 +279,9 @@ TEST_F(VideoTest, ReconfigureEncoder) // Change bitrate and resolution ASSERT_EQ(vc_reconfigure_encoder(vc, 1000, 640, 480, -1), 0); - std::vector y(640 * 480, 128); - std::vector u(320 * 240, 64); - std::vector v(320 * 240, 192); + std::vector y(640 * 480, 128); + std::vector u(320 * 240, 64); + std::vector v(320 * 240, 192); ASSERT_EQ(vc_encode(vc, 640, 480, y.data(), u.data(), v.data(), VC_EFLAG_NONE), 0); @@ -287,7 +291,7 @@ TEST_F(VideoTest, ReconfigureEncoder) TEST_F(VideoTest, GetLcfd) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); // Default lcfd is 60 in video.c @@ -299,7 +303,7 @@ TEST_F(VideoTest, GetLcfd) TEST_F(VideoTest, QueueInvalidMessage) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); RtpMock rtp_mock; @@ -310,9 +314,9 @@ TEST_F(VideoTest, QueueInvalidMessage) &rtp_mock, nullptr, nullptr, nullptr, vc, RtpMock::video_cb); rtp_mock.recv_session = video_recv_rtp; - std::vector dummy_audio(100, 0); + std::vector dummy_audio(100, 0); int rc = rtp_send_data( - log, audio_rtp, dummy_audio.data(), static_cast(dummy_audio.size()), false); + log, audio_rtp, dummy_audio.data(), static_cast(dummy_audio.size()), false); ASSERT_EQ(rc, 0); // Iterate should NOT trigger callback because payload type was wrong @@ -327,7 +331,7 @@ TEST_F(VideoTest, QueueInvalidMessage) TEST_F(VideoTest, ReconfigureOptimizations) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); // 1. Reconfigure with same values (should do nothing) @@ -346,7 +350,7 @@ TEST_F(VideoTest, ReconfigureOptimizations) TEST_F(VideoTest, LcfdAndSpecialPackets) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); RtpMock rtp_mock; @@ -357,9 +361,9 @@ TEST_F(VideoTest, LcfdAndSpecialPackets) // 1. Test lcfd update tm.t += 50; // Advance time by 50ms mono_time_update(mono_time); - std::vector dummy_frame(10, 0); - rtp_send_data( - log, video_recv_rtp, dummy_frame.data(), static_cast(dummy_frame.size()), true); + std::vector dummy_frame(10, 0); + rtp_send_data(log, video_recv_rtp, dummy_frame.data(), + static_cast(dummy_frame.size()), true); // lcfd should be updated. Initial linfts was set at vc_new (tm.t=1000). // Now tm.t is 1050. t_lcfd = 1050 - 1000 = 50. @@ -368,8 +372,8 @@ TEST_F(VideoTest, LcfdAndSpecialPackets) // 2. Test lcfd threshold (t_lcfd > 100 should be ignored) tm.t += 200; mono_time_update(mono_time); - rtp_send_data( - log, video_recv_rtp, dummy_frame.data(), static_cast(dummy_frame.size()), true); + rtp_send_data(log, video_recv_rtp, dummy_frame.data(), + static_cast(dummy_frame.size()), true); EXPECT_EQ(vc_get_lcfd(vc), 50u); // Should still be 50 // 3. Test dummy packet PT = (RTP_TYPE_VIDEO + 2) % 128 @@ -377,7 +381,7 @@ TEST_F(VideoTest, LcfdAndSpecialPackets) &rtp_mock, nullptr, nullptr, nullptr, vc, RtpMock::video_cb); rtp_mock.recv_session = dummy_rtp; rtp_send_data( - log, dummy_rtp, dummy_frame.data(), static_cast(dummy_frame.size()), false); + log, dummy_rtp, dummy_frame.data(), static_cast(dummy_frame.size()), false); // Should return 0 but do nothing (logged as "Got dummy!") // 4. Test GetQueueMutex @@ -391,15 +395,15 @@ TEST_F(VideoTest, LcfdAndSpecialPackets) TEST_F(VideoTest, MultiReconfigureEncode) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); for (int i = 0; i < 5; ++i) { - uint16_t w = static_cast(160 + (i * 16)); - uint16_t h = static_cast(120 + (i * 16)); - std::vector y(static_cast(w) * h, 128); - std::vector u((static_cast(w) / 2) * (h / 2), 64); - std::vector v((static_cast(w) / 2) * (h / 2), 192); + std::uint16_t w = static_cast(160 + (i * 16)); + std::uint16_t h = static_cast(120 + (i * 16)); + std::vector y(static_cast(w) * h, 128); + std::vector u((static_cast(w) / 2) * (h / 2), 64); + std::vector v((static_cast(w) / 2) * (h / 2), 192); ASSERT_EQ(vc_reconfigure_encoder(vc, 1000, w, h, -1), 0); ASSERT_EQ(vc_encode(vc, w, h, y.data(), u.data(), v.data(), VC_EFLAG_NONE), 0); @@ -411,7 +415,7 @@ TEST_F(VideoTest, MultiReconfigureEncode) TEST_F(VideoTest, ReconfigureFailDoS) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); // Trigger failure by passing invalid resolution (0) @@ -419,9 +423,9 @@ TEST_F(VideoTest, ReconfigureFailDoS) ASSERT_EQ(vc_reconfigure_encoder(vc, 1000, 0, 0, -1), -1); // Attempt to encode. This is expected to crash because vc->encoder is destroyed. - std::vector y(320 * 240, 128); - std::vector u(160 * 120, 64); - std::vector v(160 * 120, 192); + std::vector y(320 * 240, 128); + std::vector u(160 * 120, 64); + std::vector v(160 * 120, 192); // This call will crash in the current unfixed code. vc_encode(vc, 320, 240, y.data(), u.data(), v.data(), VC_EFLAG_NONE); @@ -431,7 +435,7 @@ TEST_F(VideoTest, ReconfigureFailDoS) TEST_F(VideoTest, LyingLengthOOB) { VideoTestData data; - VCSession *vc = vc_new(log, mono_time, 123, VideoTestData::receive_frame, &data); + VCSession *vc = vc_new(mem, log, mono_time, 123, VideoTestData::receive_frame, &data); ASSERT_NE(vc, nullptr); RtpMock rtp_mock; @@ -440,31 +444,31 @@ TEST_F(VideoTest, LyingLengthOOB) rtp_mock.recv_session = recv_rtp; // Craft a malicious RTP packet - uint16_t payload_len = 10; - uint8_t packet[RTP_HEADER_SIZE + 11]; // +1 for Tox ID - memset(packet, 0, sizeof(packet)); + std::uint16_t payload_len = 10; + std::uint8_t packet[RTP_HEADER_SIZE + 11]; // +1 for Tox ID + std::memset(packet, 0, sizeof(packet)); // Tox ID - packet[0] = static_cast(RTP_TYPE_VIDEO); + packet[0] = static_cast(RTP_TYPE_VIDEO); - auto pack_u16 = [](uint8_t *p, uint16_t v) { - p[0] = static_cast(v >> 8); - p[1] = static_cast(v & 0xff); + auto pack_u16 = [](std::uint8_t *p, std::uint16_t v) { + p[0] = static_cast(v >> 8); + p[1] = static_cast(v & 0xff); }; - auto pack_u32 = [](uint8_t *p, uint32_t v) { - p[0] = static_cast(v >> 24); - p[1] = static_cast((v >> 16) & 0xff); - p[2] = static_cast((v >> 8) & 0xff); - p[3] = static_cast(v & 0xff); + auto pack_u32 = [](std::uint8_t *p, std::uint32_t v) { + p[0] = static_cast(v >> 24); + p[1] = static_cast((v >> 16) & 0xff); + p[2] = static_cast((v >> 8) & 0xff); + p[3] = static_cast(v & 0xff); }; - auto pack_u64 = [&](uint8_t *p, uint64_t v) { - pack_u32(p, static_cast(v >> 32)); - pack_u32(p + 4, static_cast(v & 0xffffffff)); + auto pack_u64 = [&](std::uint8_t *p, std::uint64_t v) { + pack_u32(p, static_cast(v >> 32)); + pack_u32(p + 4, static_cast(v & 0xffffffff)); }; // RTP Header starts at packet[1] packet[1] = 2 << 6; // ve = 2 - packet[2] = static_cast(RTP_TYPE_VIDEO % 128); + packet[2] = static_cast(RTP_TYPE_VIDEO % 128); pack_u16(packet + 3, 1); // sequnum pack_u32(packet + 5, 1000); // timestamp diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel index cd3e8ee5..0a3a6f81 100644 --- a/toxcore/BUILD.bazel +++ b/toxcore/BUILD.bazel @@ -4,6 +4,7 @@ load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test") exports_files( srcs = [ "tox.h", + "tox_attributes.h", "tox_dispatch.h", "tox_events.h", "tox_log_level.h", @@ -18,6 +19,9 @@ cc_library( testonly = True, srcs = ["test_util.cc"], hdrs = ["test_util.hh"], + deps = [ + ":attributes", + ], ) cc_test( @@ -54,16 +58,13 @@ cc_library( ) cc_library( - name = "tox_memory", - srcs = ["tox_memory.c"], - hdrs = [ - "tox_memory.h", - "tox_memory_impl.h", - ], + name = "mem", + srcs = ["mem.c"], + hdrs = ["mem.h"], visibility = ["//c-toxcore:__subpackages__"], deps = [ + ":attributes", ":ccompat", - ":tox_attributes", ], ) @@ -74,22 +75,7 @@ cc_library( visibility = ["//c-toxcore:__subpackages__"], deps = [ ":attributes", - ":tox_memory", - ], -) - -cc_library( - name = "tox_random", - srcs = ["tox_random.c"], - hdrs = [ - "tox_random.h", - "tox_random_impl.h", - ], - visibility = ["//c-toxcore:__subpackages__"], - deps = [ - ":ccompat", - ":tox_attributes", - ":tox_memory", + ":mem", ], ) @@ -101,20 +87,90 @@ cc_library( deps = [ ":attributes", ":ccompat", - ":tox_random", + ":rng", "@libsodium", ], ) cc_library( - name = "mem", - srcs = ["mem.c"], - hdrs = ["mem.h"], + name = "rng", + srcs = ["rng.c"], + hdrs = ["rng.h"], + visibility = ["//c-toxcore:__subpackages__"], + deps = [ + ":attributes", + ], +) + +cc_library( + name = "ev", + srcs = ["ev.c"], + hdrs = ["ev.h"], visibility = ["//c-toxcore:__subpackages__"], deps = [ ":attributes", ":ccompat", - ":tox_memory", + ":net", + ], +) + +cc_library( + name = "os_event", + srcs = ["os_event.c"], + hdrs = ["os_event.h"], + copts = select({ + "//tools/config:linux": ["-DEV_USE_EPOLL=1"], + "//conditions:default": [], + }), + visibility = ["//c-toxcore:__subpackages__"], + deps = [ + ":ccompat", + ":ev", + ":logger", + ":mem", + ":net", + ":os_network", + ], +) + +cc_library( + name = "ev_test_util", + testonly = True, + srcs = ["ev_test_util.cc"], + hdrs = ["ev_test_util.hh"], + deps = [":net"], +) + +cc_test( + name = "ev_test", + size = "small", + srcs = ["ev_test.cc"], + deps = [ + ":ev", + ":ev_test_util", + ":logger", + ":net", + ":os_event", + ":os_memory", + ":os_network", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + +cc_binary( + name = "ev_bench", + testonly = True, + srcs = ["ev_bench.cc"], + deps = [ + ":ev", + ":ev_test_util", + ":logger", + ":net", + ":os_event", + ":os_memory", + ":os_network", + "@benchmark", ], ) @@ -123,6 +179,7 @@ cc_test( size = "small", srcs = ["mem_test.cc"], deps = [ + ":attributes", ":mem", ":os_memory", "@com_google_googletest//:gtest", @@ -177,6 +234,7 @@ cc_library( srcs = ["sort_test_util.cc"], hdrs = ["sort_test_util.hh"], deps = [ + ":attributes", ":sort", ":util", ], @@ -199,6 +257,7 @@ cc_binary( testonly = True, srcs = ["sort_bench.cc"], deps = [ + ":attributes", ":mem", ":sort", ":sort_test_util", @@ -254,11 +313,13 @@ cc_test( size = "small", srcs = ["bin_pack_test.cc"], deps = [ + ":attributes", ":bin_pack", ":bin_unpack", ":logger", ":mem", ":os_memory", + ":test_util", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", ], @@ -273,7 +334,7 @@ cc_library( ":attributes", ":ccompat", ":mem", - ":tox_random", + ":rng", ":util", "@libsodium", ], @@ -299,9 +360,10 @@ cc_library( srcs = ["crypto_core_test_util.cc"], hdrs = ["crypto_core_test_util.hh"], deps = [ + ":attributes", ":crypto_core", + ":rng", ":test_util", - ":tox_random", ], ) @@ -381,6 +443,7 @@ cc_library( srcs = ["mono_time_test_util.cc"], hdrs = ["mono_time_test_util.hh"], deps = [ + ":attributes", ":mono_time", "//c-toxcore/testing/support", ], @@ -391,6 +454,7 @@ cc_test( size = "small", srcs = ["mono_time_test.cc"], deps = [ + ":attributes", ":mono_time", ":mono_time_test_util", "//c-toxcore/testing/support", @@ -420,6 +484,25 @@ cc_library( ], ) +cc_test( + name = "shared_key_cache_test", + size = "small", + srcs = ["shared_key_cache_test.cc"], + deps = [ + ":attributes", + ":crypto_core_test_util", + ":logger", + ":mono_time", + ":mono_time_test_util", + ":os_memory", + ":rng", + ":shared_key_cache", + "//c-toxcore/testing/support", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "net_profile", srcs = ["net_profile.c"], @@ -436,6 +519,30 @@ cc_library( ], ) +cc_library( + name = "net", + srcs = ["net.c"], + hdrs = ["net.h"], + visibility = ["//c-toxcore:__subpackages__"], + deps = [ + ":attributes", + ":mem", + ], +) + +cc_library( + name = "os_network", + srcs = ["os_network.c"], + hdrs = ["os_network.h"], + visibility = ["//c-toxcore:__subpackages__"], + deps = [ + ":attributes", + ":ccompat", + ":mem", + ":net", + ], +) + cc_library( name = "network", srcs = [ @@ -450,6 +557,7 @@ cc_library( "//c-toxcore/auto_tests:__subpackages__", "//c-toxcore/other:__pkg__", "//c-toxcore/other/bootstrap_daemon:__pkg__", + "//c-toxcore/testing/bench:__pkg__", "//c-toxcore/testing/fuzzing:__pkg__", "//c-toxcore/testing/support:__pkg__", "//c-toxcore/toxav:__pkg__", @@ -459,10 +567,13 @@ cc_library( ":bin_pack", ":ccompat", ":crypto_core", + ":ev", ":logger", ":mem", ":mono_time", + ":net", ":net_profile", + ":os_network", ":util", "@libsodium", "@psocket", @@ -476,9 +587,12 @@ cc_library( srcs = ["network_test_util.cc"], hdrs = ["network_test_util.hh"], deps = [ + ":attributes", ":crypto_core", ":mem", + ":net", ":network", + ":rng", ":test_util", ], ) @@ -518,6 +632,7 @@ cc_library( ":crypto_core", ":mem", ":mono_time", + ":rng", ":util", ], ) @@ -527,9 +642,14 @@ cc_test( size = "small", srcs = ["ping_array_test.cc"], deps = [ + ":attributes", ":crypto_core_test_util", + ":logger", ":mono_time", + ":mono_time_test_util", + ":os_memory", ":ping_array", + ":rng", "//c-toxcore/testing/support", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", @@ -550,6 +670,7 @@ cc_library( ":ccompat", ":crypto_core", ":mem", + ":net", ":network", ":util", "@psocket", @@ -567,6 +688,7 @@ cc_library( "ping.h", ], visibility = [ + "//c-toxcore/auto_tests:__pkg__", "//c-toxcore/other:__pkg__", "//c-toxcore/other/bootstrap_daemon:__pkg__", "//c-toxcore/testing:__pkg__", @@ -580,8 +702,10 @@ cc_library( ":logger", ":mem", ":mono_time", + ":net", ":network", ":ping_array", + ":rng", ":shared_key_cache", ":sort", ":state", @@ -596,13 +720,18 @@ cc_library( hdrs = ["DHT_test_util.hh"], deps = [ ":DHT", + ":attributes", ":crypto_core", ":crypto_core_test_util", + ":ev", ":logger", ":mono_time", + ":net", ":net_crypto", ":network", ":network_test_util", + ":os_event", + ":rng", ":test_util", "//c-toxcore/testing/support", ], @@ -615,12 +744,15 @@ cc_test( deps = [ ":DHT", ":DHT_test_util", + ":attributes", ":crypto_core", ":crypto_core_test_util", + ":ev", ":logger", ":mono_time", ":network", ":network_test_util", + ":os_event", ":test_util", "//c-toxcore/testing/support", "@com_google_googletest//:gtest", @@ -658,7 +790,9 @@ cc_library( ":logger", ":mem", ":mono_time", + ":net", ":network", + ":rng", ":shared_key_cache", ":util", ], @@ -681,7 +815,9 @@ cc_library( ":logger", ":mem", ":mono_time", + ":net", ":network", + ":rng", ":timed_auth", ], ) @@ -692,7 +828,9 @@ cc_fuzz_test( srcs = ["forwarding_fuzz_test.cc"], corpus = ["//tools/toktok-fuzzer/corpus:forwarding_fuzz_test"], deps = [ + ":ev", ":forwarding", + ":os_event", "//c-toxcore/testing/support", ], ) @@ -716,6 +854,7 @@ cc_library( ":mem", ":mono_time", ":network", + ":rng", ":shared_key_cache", ":timed_auth", ":util", @@ -733,8 +872,10 @@ cc_library( ":crypto_core", ":logger", ":mem", + ":net", ":net_profile", ":network", + ":rng", ], ) @@ -742,10 +883,6 @@ cc_library( name = "TCP_server", srcs = ["TCP_server.c"], hdrs = ["TCP_server.h"], - copts = select({ - "//tools/config:linux": ["-DTCP_SERVER_USE_EPOLL=1"], - "//conditions:default": [], - }), visibility = [ "//c-toxcore/auto_tests:__pkg__", "//c-toxcore/other:__pkg__", @@ -756,14 +893,17 @@ cc_library( ":attributes", ":ccompat", ":crypto_core", + ":ev", ":forwarding", ":list", ":logger", ":mem", ":mono_time", + ":net", ":net_profile", ":network", ":onion", + ":rng", ":util", "@psocket", ], @@ -783,8 +923,10 @@ cc_library( ":logger", ":mem", ":mono_time", + ":net", ":net_profile", ":network", + ":rng", ":util", ], ) @@ -806,9 +948,11 @@ cc_library( ":logger", ":mem", ":mono_time", + ":net", ":net_profile", ":network", ":onion", + ":rng", ":util", ], ) @@ -831,11 +975,16 @@ cc_test( deps = [ ":TCP_client", ":TCP_common", + ":attributes", ":crypto_core", ":logger", ":mono_time", ":net_profile", ":network", + ":os_event", + ":os_memory", + ":rng", + ":test_util", ":util", "//c-toxcore/testing/support", "@com_google_googletest//:gtest", @@ -843,6 +992,21 @@ cc_test( ], ) +cc_test( + name = "TCP_common_test", + size = "small", + srcs = ["TCP_common_test.cc"], + deps = [ + ":TCP_common", + ":crypto_core", + ":logger", + ":os_memory", + ":os_random", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "net_crypto", srcs = ["net_crypto.c"], @@ -863,8 +1027,10 @@ cc_library( ":logger", ":mem", ":mono_time", + ":net", ":net_profile", ":network", + ":rng", ":util", "@pthread", ], @@ -876,12 +1042,14 @@ cc_test( srcs = ["net_crypto_test.cc"], deps = [ ":DHT_test_util", + ":attributes", ":crypto_core", ":logger", ":mono_time", ":net_crypto", ":net_profile", ":network", + ":test_util", "//c-toxcore/testing/support", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", @@ -894,6 +1062,7 @@ cc_test( srcs = ["friend_connection_test.cc"], deps = [ ":DHT_test_util", + ":attributes", ":crypto_core", ":friend_connection", ":logger", @@ -902,6 +1071,7 @@ cc_test( ":net_profile", ":network", ":onion_client", + ":test_util", "//c-toxcore/testing/support", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", @@ -911,15 +1081,17 @@ cc_test( cc_fuzz_test( name = "net_crypto_fuzz_test", size = "small", - testonly = True, srcs = ["net_crypto_fuzz_test.cc"], - corpus = ["//tools/toktok-fuzzer/corpus:net_crypto_fuzz_test"], + copts = ["-UNDEBUG"], deps = [ ":DHT", ":TCP_client", + ":attributes", + ":ev", ":net_crypto", ":net_profile", ":network", + ":os_event", "//c-toxcore/testing/support", ], ) @@ -942,8 +1114,10 @@ cc_library( ":logger", ":mem", ":mono_time", + ":net", ":network", ":onion", + ":rng", ":shared_key_cache", ":sort", ":timed_auth", @@ -969,6 +1143,7 @@ cc_library( ":logger", ":mem", ":mono_time", + ":net", ":network", ":util", ], @@ -980,6 +1155,7 @@ cc_test( srcs = ["group_announce_test.cc"], deps = [ ":DHT", + ":attributes", ":crypto_core", ":group_announce", ":logger", @@ -995,10 +1171,10 @@ cc_test( cc_fuzz_test( name = "group_announce_fuzz_test", size = "small", - testonly = True, srcs = ["group_announce_fuzz_test.cc"], - corpus = ["//tools/toktok-fuzzer/corpus:group_announce_fuzz_test"], + copts = ["-UNDEBUG"], deps = [ + ":attributes", ":group_announce", "//c-toxcore/testing/support", ], @@ -1024,6 +1200,7 @@ cc_library( ":mono_time", ":network", ":onion_announce", + ":rng", ":timed_auth", ], ) @@ -1046,11 +1223,13 @@ cc_library( ":logger", ":mem", ":mono_time", + ":net", ":net_crypto", ":network", ":onion", ":onion_announce", ":ping_array", + ":rng", ":sort", ":timed_auth", ":util", @@ -1059,10 +1238,11 @@ cc_library( cc_test( name = "onion_client_test", - size = "small", + size = "medium", srcs = ["onion_client_test.cc"], deps = [ ":DHT_test_util", + ":attributes", ":crypto_core", ":logger", ":mono_time", @@ -1083,12 +1263,17 @@ cc_fuzz_test( size = "small", testonly = True, srcs = ["onion_client_fuzz_test.cc"], - # corpus = ["//tools/toktok-fuzzer/corpus:onion_client_fuzz_test"], + copts = ["-UNDEBUG"], deps = [ ":DHT", + ":attributes", + ":ev", ":net_crypto", ":net_profile", + ":network", ":onion_client", + ":os_event", + ":test_util", "//c-toxcore/testing/support", ], ) @@ -1108,11 +1293,13 @@ cc_library( ":logger", ":mem", ":mono_time", + ":net", ":net_crypto", ":network", ":onion", ":onion_announce", ":onion_client", + ":rng", ":util", ], ) @@ -1136,6 +1323,7 @@ cc_library( ":onion", ":onion_announce", ":onion_client", + ":rng", ":util", ], ) @@ -1221,6 +1409,7 @@ cc_library( ":ccompat", ":crypto_core", ":crypto_core_pack", + ":ev", ":forwarding", ":friend_connection", ":friend_requests", @@ -1230,12 +1419,14 @@ cc_library( ":logger", ":mem", ":mono_time", + ":net", ":net_crypto", ":net_profile", ":network", ":onion", ":onion_announce", ":onion_client", + ":rng", ":state", ":util", "@libsodium", @@ -1259,6 +1450,7 @@ cc_library( ":mono_time", ":net_crypto", ":network", + ":rng", ":sort", ":state", ":util", @@ -1305,19 +1497,24 @@ cc_library( ":attributes", ":ccompat", ":crypto_core", + ":ev", ":friend_requests", ":group", ":group_moderation", ":logger", ":mem", ":mono_time", + ":net", ":net_crypto", ":net_profile", ":network", ":onion_client", + ":os_event", ":os_memory", + ":os_network", ":os_random", ":state", + ":tox_attributes", ":tox_log_level", ":tox_options", ":util", @@ -1331,6 +1528,7 @@ cc_test( size = "small", srcs = ["tox_test.cc"], deps = [ + ":attributes", ":crypto_core", ":os_random", ":tox", @@ -1390,6 +1588,7 @@ cc_library( ":logger", ":mem", ":tox", + ":tox_attributes", ":tox_pack", ":tox_unpack", "//c-toxcore/third_party:cmp", diff --git a/toxcore/DHT.c b/toxcore/DHT.c index aeab20e1..b913e263 100644 --- a/toxcore/DHT.c +++ b/toxcore/DHT.c @@ -55,6 +55,19 @@ #define MAX_KEYS_PER_SLOT 4 #define KEYS_TIMEOUT 600 +typedef struct NAT { + /* true if currently hole punching */ + bool hole_punching; + uint32_t punching_index; + uint32_t tries; + uint32_t punching_index2; + + uint64_t punching_timestamp; + uint64_t recv_nat_ping_timestamp; + uint64_t nat_ping_id; + uint64_t nat_ping_timestamp; +} NAT; + typedef struct DHT_Friend_Callback { dht_ip_cb *_Nullable ip_callback; void *_Nullable data; @@ -2002,7 +2015,7 @@ static uint32_t foreach_ip_port(const DHT *_Nonnull dht, const DHT_Friend *_Nonn /* If ip is not zero and node is good. */ if (!ip_isset(&assoc->ret_ip_port.ip) - && !mono_time_is_timeout(dht->mono_time, assoc->ret_timestamp, BAD_NODE_TIMEOUT)) { + || mono_time_is_timeout(dht->mono_time, assoc->ret_timestamp, BAD_NODE_TIMEOUT)) { continue; } diff --git a/toxcore/DHT.h b/toxcore/DHT.h index dc65d046..634bff26 100644 --- a/toxcore/DHT.h +++ b/toxcore/DHT.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-3.0-or-later - * Copyright © 2016-2025 The TokTok team. + * Copyright © 2016-2026 The TokTok team. * Copyright © 2013 Tox project. */ @@ -10,14 +10,17 @@ #define C_TOXCORE_TOXCORE_DHT_H #include +#include #include "attributes.h" #include "crypto_core.h" #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "network.h" #include "ping_array.h" +#include "rng.h" #ifdef __cplusplus extern "C" { @@ -156,19 +159,6 @@ typedef struct Client_data { /*----------------------------------------------------------------------------------*/ -typedef struct NAT { - /* true if currently hole punching */ - bool hole_punching; - uint32_t punching_index; - uint32_t tries; - uint32_t punching_index2; - - uint64_t punching_timestamp; - uint64_t recv_nat_ping_timestamp; - uint64_t nat_ping_id; - uint64_t nat_ping_timestamp; -} NAT; - typedef struct Node_format { uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; IP_Port ip_port; diff --git a/toxcore/DHT_fuzz_test.cc b/toxcore/DHT_fuzz_test.cc index c3ab1969..c6d95f4f 100644 --- a/toxcore/DHT_fuzz_test.cc +++ b/toxcore/DHT_fuzz_test.cc @@ -16,14 +16,14 @@ using tox::test::SimulatedEnvironment; void TestHandleRequest(Fuzz_Data &input) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); - CONSUME_OR_RETURN(const uint8_t *self_public_key, input, CRYPTO_PUBLIC_KEY_SIZE); - CONSUME_OR_RETURN(const uint8_t *self_secret_key, input, CRYPTO_SECRET_KEY_SIZE); + CONSUME_OR_RETURN(const std::uint8_t *self_public_key, input, CRYPTO_PUBLIC_KEY_SIZE); + CONSUME_OR_RETURN(const std::uint8_t *self_secret_key, input, CRYPTO_SECRET_KEY_SIZE); - uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t request[MAX_CRYPTO_REQUEST_SIZE]; - uint8_t request_id; + std::uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t request[MAX_CRYPTO_REQUEST_SIZE]; + std::uint8_t request_id; handle_request(&c_mem, self_public_key, self_secret_key, public_key, request, &request_id, input.data(), input.size()); } @@ -32,16 +32,16 @@ void TestUnpackNodes(Fuzz_Data &input) { CONSUME1_OR_RETURN(const bool, tcp_enabled, input); - const uint16_t node_count = 5; + const std::uint16_t node_count = 5; Node_format nodes[node_count]; - uint16_t processed_data_len; + std::uint16_t processed_data_len; const int packed_count = unpack_nodes( nodes, node_count, &processed_data_len, input.data(), input.size(), tcp_enabled); if (packed_count > 0) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Logger *logger = logger_new(&c_mem); - std::vector packed(packed_count * PACKED_NODE_SIZE_IP6); + std::vector packed(packed_count * PACKED_NODE_SIZE_IP6); const int packed_size = pack_nodes(logger, packed.data(), packed.size(), nodes, packed_count); LOGGER_ASSERT(logger, packed_size == processed_data_len, @@ -51,7 +51,7 @@ void TestUnpackNodes(Fuzz_Data &input) // Check that packed nodes can be unpacked again and result in the // original unpacked nodes. Node_format nodes2[node_count]; - uint16_t processed_data_len2; + std::uint16_t processed_data_len2; const int packed_count2 = unpack_nodes( nodes2, node_count, &processed_data_len2, packed.data(), packed.size(), tcp_enabled); (void)packed_count2; @@ -59,14 +59,14 @@ void TestUnpackNodes(Fuzz_Data &input) assert(processed_data_len2 == processed_data_len); assert(packed_count2 == packed_count); #endif - assert(memcmp(nodes, nodes2, sizeof(Node_format) * packed_count) == 0); + assert(std::memcmp(nodes, nodes2, sizeof(Node_format) * packed_count) == 0); } } } // namespace -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size); +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { tox::test::fuzz_select_target(data, size); return 0; diff --git a/toxcore/DHT_test.cc b/toxcore/DHT_test.cc index 63e547a5..123eab30 100644 --- a/toxcore/DHT_test.cc +++ b/toxcore/DHT_test.cc @@ -10,6 +10,7 @@ #include "../testing/support/public/simulated_environment.hh" #include "DHT_test_util.hh" +#include "attributes.h" #include "crypto_core.h" #include "crypto_core_test_util.hh" #include "logger.h" @@ -27,19 +28,19 @@ using ::testing::PrintToString; using ::testing::UnorderedElementsAre; using namespace tox::test; -using SecretKey = std::array; +using SecretKey = std::array; struct KeyPair { PublicKey pk; SecretKey sk; - explicit KeyPair(const Random *rng) { crypto_new_keypair(rng, pk.data(), sk.data()); } + explicit KeyPair(const Random *_Nonnull rng) { crypto_new_keypair(rng, pk.data(), sk.data()); } }; TEST(IdClosest, KeyIsClosestToItself) { SimulatedEnvironment env; - auto c_rng = env.fake_random().get_c_random(); + auto c_rng = env.fake_random().c_random(); PublicKey pk0 = random_pk(&c_rng); PublicKey pk1; @@ -54,7 +55,7 @@ TEST(IdClosest, KeyIsClosestToItself) TEST(IdClosest, IdenticalKeysAreSameDistance) { SimulatedEnvironment env; - auto c_rng = env.fake_random().get_c_random(); + auto c_rng = env.fake_random().c_random(); PublicKey pk0 = random_pk(&c_rng); PublicKey pk1 = random_pk(&c_rng); @@ -65,7 +66,7 @@ TEST(IdClosest, IdenticalKeysAreSameDistance) TEST(IdClosest, DistanceIsCommutative) { SimulatedEnvironment env; - auto c_rng = env.fake_random().get_c_random(); + auto c_rng = env.fake_random().c_random(); PublicKey pk0 = random_pk(&c_rng); PublicKey pk1 = random_pk(&c_rng); @@ -100,7 +101,7 @@ TEST(IdClosest, DistinctKeysCannotHaveTheSameDistance) PublicKey const pk1 = {0x00}; PublicKey pk2 = {0x00}; - for (uint8_t i = 1; i < 0xff; ++i) { + for (std::uint8_t i = 1; i < 0xff; ++i) { pk2[0] = i; EXPECT_NE(id_closest(pk0.data(), pk1.data(), pk2.data()), 0); } @@ -152,7 +153,7 @@ Node_format fill(Node_format v, PublicKey const &pk, IP_Port const &ip_port) TEST(AddToList, AddsFirstKeysInOrder) { SimulatedEnvironment env; - auto c_rng = env.fake_random().get_c_random(); + auto c_rng = env.fake_random().c_random(); // Make cmp_key the furthest away from 00000... as possible, so all initial inserts succeed. PublicKey const cmp_pk{0xff, 0xff, 0xff, 0xff}; @@ -255,7 +256,7 @@ TEST(AddToList, AddsFirstKeysInOrder) TEST(AddToList, KeepsKeysInOrder) { SimulatedEnvironment env; - auto c_rng = env.fake_random().get_c_random(); + auto c_rng = env.fake_random().c_random(); // Any random cmp_pk should work, as well as the smallest or (approximately) largest pk. for (PublicKey const cmp_pk : {random_pk(&c_rng), PublicKey{0x00}, PublicKey{0xff, 0xff}}) { @@ -281,24 +282,24 @@ TEST(AddToList, KeepsKeysInOrder) TEST(Request, CreateAndParse) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); - auto c_rng = env.fake_random().get_c_random(); + auto c_mem = env.fake_memory().c_memory(); + auto c_rng = env.fake_random().c_random(); // Peers. const KeyPair sender(&c_rng); const KeyPair receiver(&c_rng); - const uint8_t sent_pkt_id = CRYPTO_PACKET_FRIEND_REQ; + const std::uint8_t sent_pkt_id = CRYPTO_PACKET_FRIEND_REQ; // Encoded packet. - std::array packet; + std::array packet; // Received components. PublicKey pk; - std::array incoming; - uint8_t recvd_pkt_id; + std::array incoming; + std::uint8_t recvd_pkt_id; // Request data: maximum payload is 918 bytes, so create a payload 1 byte larger than max. - std::vector outgoing(919); + std::vector outgoing(919); random_bytes(&c_rng, outgoing.data(), outgoing.size()); EXPECT_LT(create_request(&c_mem, &c_rng, sender.pk.data(), sender.sk.data(), packet.data(), @@ -330,7 +331,7 @@ TEST(Request, CreateAndParse) ASSERT_GE(recvd_length, 0); EXPECT_EQ( - std::vector(incoming.begin(), incoming.begin() + recvd_length), outgoing); + std::vector(incoming.begin(), incoming.begin() + recvd_length), outgoing); outgoing.pop_back(); } @@ -339,8 +340,8 @@ TEST(Request, CreateAndParse) TEST(AnnounceNodes, SetAndTest) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); - auto c_rng = env.fake_random().get_c_random(); + auto c_mem = env.fake_memory().c_memory(); + auto c_rng = env.fake_random().c_random(); // Use FakeNetwork instead of Test_Network (which wrapped os_network) // Create endpoint bound to virtual port @@ -356,7 +357,7 @@ TEST(AnnounceNodes, SetAndTest) // Hook up simulation clock to mono_time mono_time_set_current_time_callback( mono_time, - [](void *user_data) -> uint64_t { + [](void *user_data) -> std::uint64_t { auto *clock = static_cast(user_data); return clock->current_time_ms(); }, @@ -367,8 +368,8 @@ TEST(AnnounceNodes, SetAndTest) Ptr dht(new_dht(log, &c_mem, &c_rng, &net_struct, mono_time, net.get(), true, true)); ASSERT_NE(dht, nullptr); - uint8_t pk_data[CRYPTO_PUBLIC_KEY_SIZE]; - memcpy(pk_data, dht_get_self_public_key(dht.get()), sizeof(pk_data)); + std::uint8_t pk_data[CRYPTO_PUBLIC_KEY_SIZE]; + std::memcpy(pk_data, dht_get_self_public_key(dht.get()), sizeof(pk_data)); PublicKey self_pk(to_array(pk_data)); PublicKey pk1 = random_pk(&c_rng); diff --git a/toxcore/DHT_test_util.cc b/toxcore/DHT_test_util.cc index 2f70e6aa..f35a684c 100644 --- a/toxcore/DHT_test_util.cc +++ b/toxcore/DHT_test_util.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include "../testing/support/public/simulated_environment.hh" #include "DHT.h" @@ -15,11 +16,14 @@ using tox::test::FakeClock; // --- Mock DHT Implementation --- -MockDHT::MockDHT(const Random *rng) { crypto_new_keypair(rng, self_public_key, self_secret_key); } - -const uint8_t *MockDHT::get_shared_key(const uint8_t *pk) +MockDHT::MockDHT(const Random *_Nonnull rng) { - std::array pk_arr; + crypto_new_keypair(rng, self_public_key, self_secret_key); +} + +const std::uint8_t *_Nullable MockDHT::get_shared_key(const std::uint8_t *_Nonnull pk) +{ + std::array pk_arr; std::copy(pk, pk + CRYPTO_PUBLIC_KEY_SIZE, pk_arr.begin()); auto it = shared_keys.find(pk_arr); if (it != shared_keys.end()) { @@ -29,28 +33,28 @@ const uint8_t *MockDHT::get_shared_key(const uint8_t *pk) ++computation_count; // Compute new shared key - std::array sk; + std::array sk; encrypt_precompute(pk, self_secret_key, sk.data()); shared_keys[pk_arr] = sk; return shared_keys[pk_arr].data(); } const Net_Crypto_DHT_Funcs MockDHT::funcs = { - [](void *obj, const uint8_t *public_key) { + [](void *_Nonnull obj, const std::uint8_t *_Nonnull public_key) { return static_cast(obj)->get_shared_key(public_key); }, - [](const void *obj) { return static_cast(obj)->self_public_key; }, - [](const void *obj) { return static_cast(obj)->self_secret_key; }, + [](const void *_Nonnull obj) { return static_cast(obj)->self_public_key; }, + [](const void *_Nonnull obj) { return static_cast(obj)->self_secret_key; }, }; // --- WrappedMockDHT Implementation --- -WrappedMockDHT::WrappedMockDHT(tox::test::SimulatedEnvironment &env, uint16_t port) +WrappedMockDHT::WrappedMockDHT(tox::test::SimulatedEnvironment &env, std::uint16_t port) : node_(env.create_node(0)) , logger_(logger_new(&node_->c_memory), [](Logger *l) { logger_kill(l); }) , mono_time_(mono_time_new( &node_->c_memory, - [](void *ud) -> uint64_t { + [](void *_Nullable ud) -> std::uint64_t { return static_cast(ud)->current_time_ms(); }, &env.fake_clock()), @@ -91,14 +95,15 @@ const Net_Crypto_DHT_Funcs WrappedMockDHT::funcs = MockDHT::funcs; // --- WrappedDHT Implementation --- -WrappedDHT::WrappedDHT(tox::test::SimulatedEnvironment &env, uint16_t port) +WrappedDHT::WrappedDHT(tox::test::SimulatedEnvironment &env, std::uint16_t port) : node_(env.create_node(0)) , logger_(logger_new(&node_->c_memory), [](Logger *l) { logger_kill(l); }) - , mono_time_( - mono_time_new( - &node_->c_memory, - [](void *ud) -> uint64_t { return static_cast(ud)->current_time_ms(); }, - &env.fake_clock()), + , mono_time_(mono_time_new( + &node_->c_memory, + [](void *_Nullable ud) -> std::uint64_t { + return static_cast(ud)->current_time_ms(); + }, + &env.fake_clock()), [mem = &node_->c_memory](Mono_Time *t) { mono_time_free(mem, t); }) , networking_(nullptr, [](Networking_Core *n) { kill_networking(n); }) , dht_(nullptr, [](DHT *d) { kill_dht(d); }) @@ -122,9 +127,15 @@ WrappedDHT::WrappedDHT(tox::test::SimulatedEnvironment &env, uint16_t port) WrappedDHT::~WrappedDHT() = default; -const uint8_t *WrappedDHT::dht_public_key() const { return dht_get_self_public_key(dht_.get()); } +const std::uint8_t *WrappedDHT::dht_public_key() const +{ + return dht_get_self_public_key(dht_.get()); +} -const uint8_t *WrappedDHT::dht_secret_key() const { return dht_get_self_secret_key(dht_.get()); } +const std::uint8_t *WrappedDHT::dht_secret_key() const +{ + return dht_get_self_secret_key(dht_.get()); +} IP_Port WrappedDHT::get_ip_port() const { @@ -142,16 +153,16 @@ void WrappedDHT::poll() } const Net_Crypto_DHT_Funcs WrappedDHT::funcs = { - [](void *obj, const uint8_t *public_key) { + [](void *_Nonnull obj, const std::uint8_t *_Nonnull public_key) { return dht_get_shared_key_sent(static_cast(obj), public_key); }, - [](const void *obj) { return dht_get_self_public_key(static_cast(obj)); }, - [](const void *obj) { return dht_get_self_secret_key(static_cast(obj)); }, + [](const void *_Nonnull obj) { return dht_get_self_public_key(static_cast(obj)); }, + [](const void *_Nonnull obj) { return dht_get_self_secret_key(static_cast(obj)); }, }; // --- Test Util Functions --- -Node_format random_node_format(const Random *rng) +Node_format random_node_format(const Random *_Nonnull rng) { Node_format node; auto const pk = random_pk(rng); diff --git a/toxcore/DHT_test_util.hh b/toxcore/DHT_test_util.hh index cb327fb3..81dc711b 100644 --- a/toxcore/DHT_test_util.hh +++ b/toxcore/DHT_test_util.hh @@ -2,6 +2,7 @@ #define C_TOXCORE_TOXCORE_DHT_TEST_UTIL_H #include +#include #include #include #include @@ -9,10 +10,13 @@ #include #include "DHT.h" +#include "attributes.h" #include "crypto_core.h" #include "logger.h" #include "mono_time.h" +#include "net.h" #include "net_crypto.h" +#include "rng.h" #include "test_util.hh" namespace tox::test { @@ -27,21 +31,21 @@ bool operator==(Node_format const &a, Node_format const &b); std::ostream &operator<<(std::ostream &out, Node_format const &v); -Node_format random_node_format(const Random *rng); +Node_format random_node_format(const Random *_Nonnull rng); // --- Mock DHT --- struct MockDHT { - uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE]; // Cache for shared keys: Public Key -> Shared Key - std::map, - std::array> + std::map, + std::array> shared_keys; int computation_count = 0; - explicit MockDHT(const Random *rng); + explicit MockDHT(const Random *_Nonnull rng); - const uint8_t *get_shared_key(const uint8_t *pk); + const std::uint8_t *_Nullable get_shared_key(const std::uint8_t *_Nonnull pk); static const Net_Crypto_DHT_Funcs funcs; }; @@ -50,11 +54,11 @@ struct MockDHT { // Wraps a MockDHT instance and its dependencies (networking, etc.) within a SimulatedEnvironment class WrappedMockDHT { public: - WrappedMockDHT(tox::test::SimulatedEnvironment &env, uint16_t port); + WrappedMockDHT(tox::test::SimulatedEnvironment &env, std::uint16_t port); - MockDHT *get_dht() { return &dht_; } - const uint8_t *dht_public_key() const { return dht_.self_public_key; } - const uint8_t *dht_secret_key() const { return dht_.self_secret_key; } + MockDHT *_Nonnull get_dht() { return &dht_; } + const std::uint8_t *_Nonnull dht_public_key() const { return dht_.self_public_key; } + const std::uint8_t *_Nonnull dht_secret_key() const { return dht_.self_secret_key; } int dht_computation_count() const { return dht_.computation_count; } // Returns a valid IP_Port for this node in the simulation (Localhost IPv6) @@ -64,9 +68,9 @@ public: tox::test::ScopedToxSystem &node() { return *node_; } const tox::test::ScopedToxSystem &node() const { return *node_; } - Networking_Core *networking() { return networking_.get(); } - Mono_Time *mono_time() { return mono_time_.get(); } - Logger *logger() { return logger_.get(); } + Networking_Core *_Nonnull networking() { return networking_.get(); } + Mono_Time *_Nonnull mono_time() { return mono_time_.get(); } + Logger *_Nonnull logger() { return logger_.get(); } ~WrappedMockDHT(); @@ -74,9 +78,9 @@ public: private: std::unique_ptr node_; - std::unique_ptr logger_; - std::unique_ptr> mono_time_; - std::unique_ptr networking_; + std::unique_ptr logger_; + std::unique_ptr> mono_time_; + std::unique_ptr networking_; MockDHT dht_; }; @@ -84,11 +88,11 @@ private: // Wraps a DHT instance and its dependencies within a SimulatedEnvironment class WrappedDHT { public: - WrappedDHT(tox::test::SimulatedEnvironment &env, uint16_t port); + WrappedDHT(tox::test::SimulatedEnvironment &env, std::uint16_t port); - DHT *get_dht() { return dht_.get(); } - const uint8_t *dht_public_key() const; - const uint8_t *dht_secret_key() const; + DHT *_Nonnull get_dht() { return dht_.get(); } + const std::uint8_t *_Nonnull dht_public_key() const; + const std::uint8_t *_Nonnull dht_secret_key() const; // Returns a valid IP_Port for this node in the simulation (Localhost IPv6) IP_Port get_ip_port() const; @@ -97,9 +101,9 @@ public: tox::test::ScopedToxSystem &node() { return *node_; } const tox::test::ScopedToxSystem &node() const { return *node_; } - Networking_Core *networking() { return networking_.get(); } - Mono_Time *mono_time() { return mono_time_.get(); } - Logger *logger() { return logger_.get(); } + Networking_Core *_Nonnull networking() { return networking_.get(); } + Mono_Time *_Nonnull mono_time() { return mono_time_.get(); } + Logger *_Nonnull logger() { return logger_.get(); } ~WrappedDHT(); @@ -107,10 +111,10 @@ public: private: std::unique_ptr node_; - std::unique_ptr logger_; - std::unique_ptr> mono_time_; - std::unique_ptr networking_; - std::unique_ptr dht_; + std::unique_ptr logger_; + std::unique_ptr> mono_time_; + std::unique_ptr networking_; + std::unique_ptr dht_; }; #endif // C_TOXCORE_TOXCORE_DHT_TEST_UTIL_H diff --git a/toxcore/LAN_discovery.h b/toxcore/LAN_discovery.h index 765c67d8..ab076f46 100644 --- a/toxcore/LAN_discovery.h +++ b/toxcore/LAN_discovery.h @@ -9,8 +9,12 @@ #ifndef C_TOXCORE_TOXCORE_LAN_DISCOVERY_H #define C_TOXCORE_TOXCORE_LAN_DISCOVERY_H +#include +#include + #include "attributes.h" #include "mem.h" +#include "net.h" #include "network.h" /** diff --git a/toxcore/Makefile.inc b/toxcore/Makefile.inc index 7689dd00..e1ee9280 100644 --- a/toxcore/Makefile.inc +++ b/toxcore/Makefile.inc @@ -66,6 +66,8 @@ libtoxcore_la_SOURCES = ../third_party/cmp/cmp.c \ ../toxcore/crypto_core.h \ ../toxcore/DHT.c \ ../toxcore/DHT.h \ + ../toxcore/ev.c \ + ../toxcore/ev.h \ ../toxcore/forwarding.c \ ../toxcore/forwarding.h \ ../toxcore/friend_connection.c \ @@ -105,6 +107,8 @@ libtoxcore_la_SOURCES = ../third_party/cmp/cmp.c \ ../toxcore/net_log.h \ ../toxcore/net_profile.c \ ../toxcore/net_profile.h \ + ../toxcore/net.c \ + ../toxcore/net.h \ ../toxcore/network.c \ ../toxcore/network.h \ ../toxcore/onion_announce.c \ @@ -113,14 +117,20 @@ libtoxcore_la_SOURCES = ../third_party/cmp/cmp.c \ ../toxcore/onion_client.h \ ../toxcore/onion.c \ ../toxcore/onion.h \ + ../toxcore/os_event.c \ + ../toxcore/os_event.h \ ../toxcore/os_memory.c \ ../toxcore/os_memory.h \ + ../toxcore/os_network.c \ + ../toxcore/os_network.h \ ../toxcore/os_random.c \ ../toxcore/os_random.h \ ../toxcore/ping_array.c \ ../toxcore/ping_array.h \ ../toxcore/ping.c \ ../toxcore/ping.h \ + ../toxcore/rng.c \ + ../toxcore/rng.h \ ../toxcore/shared_key_cache.c \ ../toxcore/shared_key_cache.h \ ../toxcore/sort.c \ @@ -147,18 +157,12 @@ libtoxcore_la_SOURCES = ../third_party/cmp/cmp.c \ ../toxcore/tox_events.h \ ../toxcore/tox_log_level.c \ ../toxcore/tox_log_level.h \ - ../toxcore/tox_memory.c \ - ../toxcore/tox_memory.h \ - ../toxcore/tox_memory_impl.h \ ../toxcore/tox_options.c \ ../toxcore/tox_options.h \ ../toxcore/tox_pack.c \ ../toxcore/tox_pack.h \ ../toxcore/tox_private.c \ ../toxcore/tox_private.h \ - ../toxcore/tox_random.c \ - ../toxcore/tox_random.h \ - ../toxcore/tox_random_impl.h \ ../toxcore/tox_struct.h \ ../toxcore/tox_unpack.c \ ../toxcore/tox_unpack.h \ diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index 3ae88fb6..3ff34973 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -1184,6 +1184,29 @@ int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, u return 0; } +int32_t file_by_id(const Messenger *m, uint32_t friendnumber, const uint8_t *file_id) +{ + if (friendnumber >= m->numfriends || m->friendlist[friendnumber].status == 0) { + return -1; + } + + for (uint32_t j = 0; j < MAX_CONCURRENT_FILE_PIPES; ++j) { + if (m->friendlist[friendnumber].file_sending[j].status != FILESTATUS_NONE) { + if (memcmp(m->friendlist[friendnumber].file_sending[j].id, file_id, FILE_ID_LENGTH) == 0) { + return (int32_t)j; + } + } + + if (m->friendlist[friendnumber].file_receiving[j].status != FILESTATUS_NONE) { + if (memcmp(m->friendlist[friendnumber].file_receiving[j].id, file_id, FILE_ID_LENGTH) == 0) { + return (int32_t)((j + 1) << 16); + } + } + } + + return -2; +} + /** @brief Send a file send request. * Maximum filename length is 255 bytes. * @retval 1 on success @@ -3394,6 +3417,7 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * return nullptr; } + m->log = options->log; m->mono_time = mono_time; m->mem = mem; m->rng = rng; @@ -3409,16 +3433,6 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * } m->fr = fr; - Logger *log = logger_new(mem); - if (log == nullptr) { - friendreq_kill(m->fr); - mem_delete(mem, m); - return nullptr; - } - m->log = log; - - 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) { @@ -3444,7 +3458,6 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * *error = MESSENGER_ERROR_PORT; } - logger_kill(m->log); mem_delete(mem, m); return nullptr; } @@ -3454,7 +3467,6 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * if (dht == nullptr) { kill_networking(m->net); friendreq_kill(m->fr); - logger_kill(m->log); mem_delete(mem, m); return nullptr; } @@ -3466,7 +3478,6 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * kill_dht(m->dht); kill_networking(m->net); friendreq_kill(m->fr); - logger_kill(m->log); mem_delete(mem, m); return nullptr; } @@ -3481,7 +3492,6 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * kill_dht(m->dht); kill_networking(m->net); friendreq_kill(m->fr); - logger_kill(m->log); mem_delete(mem, m); return nullptr; } @@ -3496,7 +3506,6 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * kill_dht(m->dht); kill_networking(m->net); friendreq_kill(m->fr); - logger_kill(m->log); mem_delete(mem, m); return nullptr; } @@ -3534,7 +3543,6 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * kill_dht(m->dht); kill_networking(m->net); friendreq_kill(m->fr); - logger_kill(m->log); mem_delete(mem, m); return nullptr; } @@ -3561,7 +3569,6 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * kill_dht(m->dht); kill_networking(m->net); friendreq_kill(m->fr); - logger_kill(m->log); mem_delete(mem, m); return nullptr; } @@ -3588,7 +3595,6 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * kill_dht(m->dht); kill_networking(m->net); friendreq_kill(m->fr); - logger_kill(m->log); mem_delete(mem, m); if (error != nullptr) { @@ -3652,7 +3658,6 @@ void kill_messenger(Messenger *m) friendreq_kill(m->fr); mem_delete(m->mem, m->options.state_plugins); - logger_kill(m->log); mem_delete(m->mem, m); } diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h index d090abde..3b15d202 100644 --- a/toxcore/Messenger.h +++ b/toxcore/Messenger.h @@ -10,6 +10,9 @@ #ifndef C_TOXCORE_TOXCORE_MESSENGER_H #define C_TOXCORE_TOXCORE_MESSENGER_H +#include +#include + #include "DHT.h" #include "TCP_client.h" #include "TCP_server.h" @@ -24,6 +27,7 @@ #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "net_crypto.h" #include "net_profile.h" #include "network.h" @@ -70,6 +74,8 @@ typedef struct Messenger_State_Plugin { } Messenger_State_Plugin; typedef struct Messenger_Options { + Logger *_Nonnull log; + bool ipv6enabled; bool udp_disabled; TCP_Proxy_Info proxy_info; @@ -81,10 +87,6 @@ typedef struct Messenger_Options { bool dht_announcements_enabled; bool groups_persistence_enabled; - logger_cb *_Nullable log_callback; - void *_Nullable log_context; - void *_Nullable log_user_data; - Messenger_State_Plugin *_Nullable state_plugins; uint8_t state_plugins_length; @@ -626,6 +628,7 @@ void callback_file_reqchunk(Messenger *_Nonnull m, m_file_chunk_request_cb *_Non * @retval -2 if filenumber not valid */ int file_get_id(const Messenger *_Nonnull m, int32_t friendnumber, uint32_t filenumber, uint8_t *_Nonnull file_id); +int32_t file_by_id(const Messenger *_Nonnull m, uint32_t friendnumber, const uint8_t *_Nonnull file_id); /** @brief Send a file send request. * diff --git a/toxcore/TCP_client.h b/toxcore/TCP_client.h index 4fa477e3..353d7b97 100644 --- a/toxcore/TCP_client.h +++ b/toxcore/TCP_client.h @@ -9,14 +9,18 @@ #ifndef C_TOXCORE_TOXCORE_TCP_CLIENT_H #define C_TOXCORE_TOXCORE_TCP_CLIENT_H +#include + #include "attributes.h" #include "crypto_core.h" #include "forwarding.h" #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "net_profile.h" #include "network.h" +#include "rng.h" #ifdef __cplusplus extern "C" { diff --git a/toxcore/TCP_client_test.cc b/toxcore/TCP_client_test.cc index 92cdde10..afe83260 100644 --- a/toxcore/TCP_client_test.cc +++ b/toxcore/TCP_client_test.cc @@ -5,14 +5,18 @@ #include +#include +#include #include #include "TCP_common.h" +#include "attributes.h" #include "crypto_core.h" #include "logger.h" #include "mono_time.h" #include "net_profile.h" #include "network.h" +#include "test_util.hh" #include "util.h" namespace { @@ -23,12 +27,12 @@ class TCPClientTest : public ::testing::Test { protected: SimulatedEnvironment env; - Mono_Time *create_mono_time(const Memory *mem) + Mono_Time *_Nonnull create_mono_time(const Memory *_Nonnull mem) { - Mono_Time *mt = mono_time_new(mem, nullptr, nullptr); + Mono_Time *_Nonnull mt = REQUIRE_NOT_NULL(mono_time_new(mem, nullptr, nullptr)); mono_time_set_current_time_callback( mt, - [](void *user_data) -> uint64_t { + [](void *_Nullable user_data) -> std::uint64_t { auto *clock = static_cast(user_data); return clock->current_time_ms(); }, @@ -36,15 +40,19 @@ protected: return mt; } - static void log_cb(void *context, Logger_Level level, const char *file, uint32_t line, - const char *func, const char *message, void *userdata) + static void log_cb(void *_Nullable context, Logger_Level level, const char *_Nonnull file, + std::uint32_t line, const char *_Nonnull func, const char *_Nonnull message, + void *_Nullable userdata) { if (level > LOGGER_LEVEL_TRACE) { fprintf(stderr, "[%d] %s:%u %s: %s\n", level, file, line, func, message); } } - static void net_profile_deleter(Net_Profile *p, const Memory *mem) { netprof_kill(mem, p); } + static void net_profile_deleter(Net_Profile *_Nullable p, const Memory *_Nonnull mem) + { + netprof_kill(mem, p); + } }; TEST_F(TCPClientTest, ConnectsToRelay) @@ -68,13 +76,13 @@ TEST_F(TCPClientTest, ConnectsToRelay) ASSERT_EQ(0, net_listen(&server_node->c_network, server_sock, 5)); // Server Keys - uint8_t server_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t server_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t server_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t server_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(&server_node->c_random, server_pk, server_sk); // Client Keys - uint8_t client_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t client_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t client_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t client_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(&client_node->c_random, client_pk, client_sk); Net_Profile *client_profile = netprof_new(client_log, &client_node->c_memory); @@ -92,7 +100,7 @@ TEST_F(TCPClientTest, ConnectsToRelay) // 3. Simulation Loop bool connected = false; Socket accepted_sock = net_invalid_socket(); - uint64_t start_time = env.clock().current_time_ms(); + std::uint64_t start_time = env.clock().current_time_ms(); while (env.clock().current_time_ms() - start_time < 5000) { env.advance_time(10); @@ -109,7 +117,7 @@ TEST_F(TCPClientTest, ConnectsToRelay) // Server handles handshake if (sock_valid(accepted_sock)) { - uint8_t buf[TCP_CLIENT_HANDSHAKE_SIZE]; + std::uint8_t buf[TCP_CLIENT_HANDSHAKE_SIZE]; IP_Port remote = {{{0}}}; int len = net_recv( &server_node->c_network, server_log, accepted_sock, buf, sizeof(buf), &remote); @@ -120,15 +128,16 @@ TEST_F(TCPClientTest, ConnectsToRelay) if (len == TCP_CLIENT_HANDSHAKE_SIZE) { // Verify client PK - EXPECT_EQ(0, memcmp(buf, client_pk, CRYPTO_PUBLIC_KEY_SIZE)); + EXPECT_EQ(0, std::memcmp(buf, client_pk, CRYPTO_PUBLIC_KEY_SIZE)); // Decrypt - uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; + std::uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; encrypt_precompute(client_pk, server_sk, shared_key); - uint8_t plain[TCP_HANDSHAKE_PLAIN_SIZE]; - const uint8_t *nonce_ptr = buf + CRYPTO_PUBLIC_KEY_SIZE; - const uint8_t *ciphertext_ptr = buf + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE; + std::uint8_t plain[TCP_HANDSHAKE_PLAIN_SIZE]; + const std::uint8_t *nonce_ptr = buf + CRYPTO_PUBLIC_KEY_SIZE; + const std::uint8_t *ciphertext_ptr + = buf + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE; int res = decrypt_data_symmetric(&server_node->c_memory, shared_key, nonce_ptr, ciphertext_ptr, @@ -143,19 +152,19 @@ TEST_F(TCPClientTest, ConnectsToRelay) // Generate Response // [Nonce (24)] [Encrypted (PK(32)+Nonce(24)+MAC(16))] - uint8_t resp_nonce[CRYPTO_NONCE_SIZE]; + std::uint8_t resp_nonce[CRYPTO_NONCE_SIZE]; random_nonce(&server_node->c_random, resp_nonce); - uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t temp_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t temp_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(&server_node->c_random, temp_pk, temp_sk); - uint8_t resp_plain[TCP_HANDSHAKE_PLAIN_SIZE]; - memcpy(resp_plain, temp_pk, CRYPTO_PUBLIC_KEY_SIZE); + std::uint8_t resp_plain[TCP_HANDSHAKE_PLAIN_SIZE]; + std::memcpy(resp_plain, temp_pk, CRYPTO_PUBLIC_KEY_SIZE); random_nonce(&server_node->c_random, resp_plain + CRYPTO_PUBLIC_KEY_SIZE); - uint8_t response[TCP_SERVER_HANDSHAKE_SIZE]; - memcpy(response, resp_nonce, CRYPTO_NONCE_SIZE); + std::uint8_t response[TCP_SERVER_HANDSHAKE_SIZE]; + std::memcpy(response, resp_nonce, CRYPTO_NONCE_SIZE); encrypt_data_symmetric(&server_node->c_memory, shared_key, resp_nonce, // nonce @@ -209,12 +218,12 @@ TEST_F(TCPClientTest, SendDataIntegerOverflow) ASSERT_TRUE(bind_to_port(&server_node->c_network, server_sock, net_family_ipv4(), 33446)); ASSERT_EQ(0, net_listen(&server_node->c_network, server_sock, 5)); - uint8_t server_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t server_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t server_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t server_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(&server_node->c_random, server_pk, server_sk); - uint8_t client_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t client_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t client_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t client_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(&client_node->c_random, client_pk, client_sk); Net_Profile *client_profile = netprof_new(client_log, &client_node->c_memory); @@ -230,20 +239,20 @@ TEST_F(TCPClientTest, SendDataIntegerOverflow) bool connected = false; Socket accepted_sock = net_invalid_socket(); - uint64_t start_time = env.clock().current_time_ms(); - uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; - uint8_t sent_nonce[CRYPTO_NONCE_SIZE] = {0}; - uint8_t recv_nonce[CRYPTO_NONCE_SIZE] = {0}; + std::uint64_t start_time = env.clock().current_time_ms(); + std::uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; + std::uint8_t sent_nonce[CRYPTO_NONCE_SIZE] = {0}; + std::uint8_t recv_nonce[CRYPTO_NONCE_SIZE] = {0}; // Helper to send encrypted packet from server to client - auto server_send_packet = [&](const uint8_t *data, uint16_t length) { - uint16_t packet_size = sizeof(uint16_t) + length + CRYPTO_MAC_SIZE; - std::vector packet(packet_size); - uint16_t c_length = net_htons(length + CRYPTO_MAC_SIZE); - memcpy(packet.data(), &c_length, sizeof(uint16_t)); + auto server_send_packet = [&](const std::uint8_t *data, std::uint16_t length) { + std::uint16_t packet_size = sizeof(std::uint16_t) + length + CRYPTO_MAC_SIZE; + std::vector packet(packet_size); + std::uint16_t c_length = net_htons(length + CRYPTO_MAC_SIZE); + std::memcpy(packet.data(), &c_length, sizeof(std::uint16_t)); encrypt_data_symmetric(&server_node->c_memory, shared_key, sent_nonce, data, length, - packet.data() + sizeof(uint16_t)); + packet.data() + sizeof(std::uint16_t)); increment_nonce(sent_nonce); IP_Port remote = {{{0}}}; @@ -263,39 +272,39 @@ TEST_F(TCPClientTest, SendDataIntegerOverflow) } if (sock_valid(accepted_sock) && !connected) { - uint8_t buf[TCP_CLIENT_HANDSHAKE_SIZE]; + std::uint8_t buf[TCP_CLIENT_HANDSHAKE_SIZE]; IP_Port remote = {{{0}}}; int len = net_recv( &server_node->c_network, server_log, accepted_sock, buf, sizeof(buf), &remote); if (len == TCP_CLIENT_HANDSHAKE_SIZE) { encrypt_precompute(client_pk, server_sk, shared_key); - uint8_t plain[TCP_HANDSHAKE_PLAIN_SIZE]; + std::uint8_t plain[TCP_HANDSHAKE_PLAIN_SIZE]; if (decrypt_data_symmetric(&server_node->c_memory, shared_key, buf + CRYPTO_PUBLIC_KEY_SIZE, buf + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, TCP_CLIENT_HANDSHAKE_SIZE - (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE), plain) == TCP_HANDSHAKE_PLAIN_SIZE) { - memcpy(recv_nonce, plain + CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_NONCE_SIZE); + std::memcpy(recv_nonce, plain + CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_NONCE_SIZE); - uint8_t resp_nonce[CRYPTO_NONCE_SIZE]; + std::uint8_t resp_nonce[CRYPTO_NONCE_SIZE]; random_nonce(&server_node->c_random, resp_nonce); - memcpy(sent_nonce, resp_nonce, CRYPTO_NONCE_SIZE); + std::memcpy(sent_nonce, resp_nonce, CRYPTO_NONCE_SIZE); - uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t temp_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t temp_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(&server_node->c_random, temp_pk, temp_sk); - uint8_t resp_plain[TCP_HANDSHAKE_PLAIN_SIZE]; - memcpy(resp_plain, temp_pk, CRYPTO_PUBLIC_KEY_SIZE); + std::uint8_t resp_plain[TCP_HANDSHAKE_PLAIN_SIZE]; + std::memcpy(resp_plain, temp_pk, CRYPTO_PUBLIC_KEY_SIZE); random_nonce(&server_node->c_random, resp_plain + CRYPTO_PUBLIC_KEY_SIZE); // FIX: Save the nonce that client will use for receiving - memcpy(sent_nonce, resp_plain + CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_NONCE_SIZE); + std::memcpy(sent_nonce, resp_plain + CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_NONCE_SIZE); - uint8_t response[TCP_SERVER_HANDSHAKE_SIZE]; - memcpy(response, resp_nonce, CRYPTO_NONCE_SIZE); + std::uint8_t response[TCP_SERVER_HANDSHAKE_SIZE]; + std::memcpy(response, resp_nonce, CRYPTO_NONCE_SIZE); encrypt_data_symmetric(&server_node->c_memory, shared_key, resp_nonce, resp_plain, TCP_HANDSHAKE_PLAIN_SIZE, response + CRYPTO_NONCE_SIZE); net_send(&server_node->c_network, server_log, accepted_sock, response, @@ -315,14 +324,14 @@ TEST_F(TCPClientTest, SendDataIntegerOverflow) ASSERT_TRUE(connected); // Establish sub-connection 0 - uint8_t con_id = 0; - uint8_t other_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0}; // Dummy PK + std::uint8_t con_id = 0; + std::uint8_t other_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0}; // Dummy PK // 1. Send Routing Response to set status=1 { - uint8_t packet[1 + 1 + CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t packet[1 + 1 + CRYPTO_PUBLIC_KEY_SIZE]; packet[0] = TCP_PACKET_ROUTING_RESPONSE; packet[1] = con_id + NUM_RESERVED_PORTS; - memcpy(packet + 2, other_pk, CRYPTO_PUBLIC_KEY_SIZE); + std::memcpy(packet + 2, other_pk, CRYPTO_PUBLIC_KEY_SIZE); server_send_packet(packet, sizeof(packet)); } @@ -334,7 +343,7 @@ TEST_F(TCPClientTest, SendDataIntegerOverflow) // 2. Send Connection Notification to set status=2 { - uint8_t packet[1 + 1]; + std::uint8_t packet[1 + 1]; packet[0] = TCP_PACKET_CONNECTION_NOTIFICATION; packet[1] = con_id + NUM_RESERVED_PORTS; server_send_packet(packet, sizeof(packet)); @@ -347,7 +356,7 @@ TEST_F(TCPClientTest, SendDataIntegerOverflow) } // Now call send_data with 65535 bytes - std::vector large_data(65535); + std::vector large_data(65535); // This should trigger integer overflow: 1 + 65535 = 0. VLA(0). packet[0] write -> Crash/UB send_data(client_log, client_conn, con_id, large_data.data(), 65535); diff --git a/toxcore/TCP_common.c b/toxcore/TCP_common.c index 7e358af9..f17a4a57 100644 --- a/toxcore/TCP_common.c +++ b/toxcore/TCP_common.c @@ -316,3 +316,69 @@ int read_packet_tcp_secure_connection( return len; } + +const char *tcp_packet_type_to_string(Tcp_Packet type) +{ + switch (type) { + case TCP_PACKET_ROUTING_REQUEST: + return "TCP_PACKET_ROUTING_REQUEST"; + + case TCP_PACKET_ROUTING_RESPONSE: + return "TCP_PACKET_ROUTING_RESPONSE"; + + case TCP_PACKET_CONNECTION_NOTIFICATION: + return "TCP_PACKET_CONNECTION_NOTIFICATION"; + + case TCP_PACKET_DISCONNECT_NOTIFICATION: + return "TCP_PACKET_DISCONNECT_NOTIFICATION"; + + case TCP_PACKET_PING: + return "TCP_PACKET_PING"; + + case TCP_PACKET_PONG: + return "TCP_PACKET_PONG"; + + case TCP_PACKET_OOB_SEND: + return "TCP_PACKET_OOB_SEND"; + + case TCP_PACKET_OOB_RECV: + return "TCP_PACKET_OOB_RECV"; + + case TCP_PACKET_ONION_REQUEST: + return "TCP_PACKET_ONION_REQUEST"; + + case TCP_PACKET_ONION_RESPONSE: + return "TCP_PACKET_ONION_RESPONSE"; + + case TCP_PACKET_FORWARD_REQUEST: + return "TCP_PACKET_FORWARD_REQUEST"; + + case TCP_PACKET_FORWARDING: + return "TCP_PACKET_FORWARDING"; + } + + return ""; +} + +bool tcp_packet_from_int(uint32_t value, Tcp_Packet *_Nonnull out_enum) +{ + switch (value) { + case TCP_PACKET_ROUTING_REQUEST: + case TCP_PACKET_ROUTING_RESPONSE: + case TCP_PACKET_CONNECTION_NOTIFICATION: + case TCP_PACKET_DISCONNECT_NOTIFICATION: + case TCP_PACKET_PING: + case TCP_PACKET_PONG: + case TCP_PACKET_OOB_SEND: + case TCP_PACKET_OOB_RECV: + case TCP_PACKET_ONION_REQUEST: + case TCP_PACKET_ONION_RESPONSE: + case TCP_PACKET_FORWARD_REQUEST: + case TCP_PACKET_FORWARDING: { + *out_enum = (Tcp_Packet)value; + return true; + } + } + + return false; +} diff --git a/toxcore/TCP_common.h b/toxcore/TCP_common.h index 1d4c3d49..c00cfd5b 100644 --- a/toxcore/TCP_common.h +++ b/toxcore/TCP_common.h @@ -6,12 +6,21 @@ #ifndef C_TOXCORE_TOXCORE_TCP_COMMON_H #define C_TOXCORE_TOXCORE_TCP_COMMON_H +#include +#include + #include "attributes.h" #include "crypto_core.h" #include "logger.h" #include "mem.h" +#include "net.h" #include "net_profile.h" #include "network.h" +#include "rng.h" + +#ifdef __cplusplus +extern "C" { +#endif typedef struct TCP_Priority_List TCP_Priority_List; struct TCP_Priority_List { @@ -40,6 +49,9 @@ typedef enum Tcp_Packet { TCP_PACKET_FORWARDING = 11, } Tcp_Packet; +const char *_Nonnull tcp_packet_type_to_string(Tcp_Packet type); +bool tcp_packet_from_int(uint32_t value, Tcp_Packet *_Nonnull out_enum); + #define TCP_HANDSHAKE_PLAIN_SIZE (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE) #define TCP_SERVER_HANDSHAKE_SIZE (CRYPTO_NONCE_SIZE + TCP_HANDSHAKE_PLAIN_SIZE + CRYPTO_MAC_SIZE) #define TCP_CLIENT_HANDSHAKE_SIZE (CRYPTO_PUBLIC_KEY_SIZE + TCP_SERVER_HANDSHAKE_SIZE) @@ -106,4 +118,8 @@ int read_tcp_packet(const Logger *_Nonnull logger, const Memory *_Nonnull mem, c int read_packet_tcp_secure_connection(const Logger *_Nonnull logger, const Memory *_Nonnull mem, const Network *_Nonnull ns, Socket sock, uint16_t *_Nonnull next_packet_length, const uint8_t *_Nonnull shared_key, uint8_t *_Nonnull recv_nonce, uint8_t *_Nonnull data, uint16_t max_len, const IP_Port *_Nonnull ip_port); +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif /* C_TOXCORE_TOXCORE_TCP_COMMON_H */ diff --git a/toxcore/TCP_common_test.cc b/toxcore/TCP_common_test.cc new file mode 100644 index 00000000..90bf78bb --- /dev/null +++ b/toxcore/TCP_common_test.cc @@ -0,0 +1,98 @@ +#include "TCP_common.h" + +#include + +#include "logger.h" +#include "os_memory.h" +#include "os_random.h" + +namespace { + +TEST(TCP_common, PriorityQueueOrderingAndIntegrity) +{ + constexpr Network_Funcs mock_funcs = { + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + // Mock net_send to simulate a full buffer (returns 0) + // This forces packets into the priority queue. + [](void *obj, Socket sock, const uint8_t *buf, size_t len) { + (void)obj; + (void)sock; + (void)buf; + (void)len; + return 0; + }, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + }; + + TCP_Connection con; + memset(&con, 0, sizeof(con)); + con.mem = os_memory(); + con.rng = os_random(); + Network ns = {&mock_funcs, nullptr}; + con.ns = &ns; + + Logger *logger = logger_new(con.mem); + ASSERT_NE(logger, nullptr); + + // Minimal initialization to make write_packet_tcp_secure_connection happy + // It calls encrypt_data_symmetric which needs shared_key and sent_nonce + memset(con.shared_key, 0x42, sizeof(con.shared_key)); + memset(con.sent_nonce, 0x12, sizeof(con.sent_nonce)); + + uint8_t data1[] = "packet1"; + uint8_t data2[] = "packet2"; + uint8_t data3[] = "packet3"; + + // First packet: will fail net_send (mocked to 0) and go to add_priority + // Queue: [packet1] + int ret1 = write_packet_tcp_secure_connection(logger, &con, data1, sizeof(data1), true); + ASSERT_EQ(ret1, 1); + ASSERT_NE(con.priority_queue_start, nullptr); + ASSERT_EQ(con.priority_queue_start, con.priority_queue_end); + + // Second packet: will go to add_priority + // Queue: [packet1] -> [packet2] + int ret2 = write_packet_tcp_secure_connection(logger, &con, data2, sizeof(data2), true); + ASSERT_EQ(ret2, 1); + ASSERT_NE(con.priority_queue_start->next, nullptr); + ASSERT_EQ(con.priority_queue_start->next, con.priority_queue_end); + + // Third packet: will go to add_priority + // WITH BUG: Queue becomes [packet1] -> [packet3], packet2 is LOST + // WITHOUT BUG: Queue: [packet1] -> [packet2] -> [packet3] + int ret3 = write_packet_tcp_secure_connection(logger, &con, data3, sizeof(data3), true); + ASSERT_EQ(ret3, 1); + + // Verify integrity + TCP_Priority_List *p = con.priority_queue_start; + int count = 0; + while (p) { + count++; + p = p->next; + } + + // This is where it will fail if the change in + // https://github.com/TokTok/c-toxcore/pull/2387 is applied. + // The count will be 2 instead of 3. + EXPECT_EQ(count, 3) + << "Priority queue lost packets! (likely due to incorrect tail pointer usage)"; + + // Clean up + wipe_priority_list(con.mem, con.priority_queue_start); + logger_kill(logger); +} + +} diff --git a/toxcore/TCP_connection.h b/toxcore/TCP_connection.h index b77d502e..defe52c5 100644 --- a/toxcore/TCP_connection.h +++ b/toxcore/TCP_connection.h @@ -21,8 +21,10 @@ #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "net_profile.h" #include "network.h" +#include "rng.h" #define TCP_CONN_NONE 0 #define TCP_CONN_VALID 1 diff --git a/toxcore/TCP_connection_test.cc b/toxcore/TCP_connection_test.cc index 939137ca..c910e2c9 100644 --- a/toxcore/TCP_connection_test.cc +++ b/toxcore/TCP_connection_test.cc @@ -5,6 +5,6 @@ namespace { // TODO(Jfreegman) make this useful or remove it after NGC is merged -TEST(TCP_connection, NullTest) { (void)tcp_send_oob_packet_using_relay; } +TEST(TCP_connection, NullTest) { (void)&tcp_send_oob_packet_using_relay; } } // namespace diff --git a/toxcore/TCP_server.c b/toxcore/TCP_server.c index 1cf525bb..6de6e36d 100644 --- a/toxcore/TCP_server.c +++ b/toxcore/TCP_server.c @@ -974,7 +974,7 @@ TCP_Server *new_tcp_server(const Logger *logger, const Memory *mem, const Random temp->socks_listening = socks_listening; #ifdef TCP_SERVER_USE_EPOLL - temp->efd = epoll_create(8); + temp->efd = epoll_create1(EPOLL_CLOEXEC); if (temp->efd == -1) { LOGGER_ERROR(logger, "epoll initialisation failed"); diff --git a/toxcore/TCP_server.h b/toxcore/TCP_server.h index de8ba74c..b5f1ee0d 100644 --- a/toxcore/TCP_server.h +++ b/toxcore/TCP_server.h @@ -9,15 +9,20 @@ #ifndef C_TOXCORE_TOXCORE_TCP_SERVER_H #define C_TOXCORE_TOXCORE_TCP_SERVER_H +#include +#include + #include "attributes.h" #include "crypto_core.h" #include "forwarding.h" #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "net_profile.h" #include "network.h" #include "onion.h" +#include "rng.h" #ifdef __cplusplus extern "C" { diff --git a/toxcore/announce.c b/toxcore/announce.c index 45e9a927..ab59336f 100644 --- a/toxcore/announce.c +++ b/toxcore/announce.c @@ -25,7 +25,7 @@ #include "timed_auth.h" #include "util.h" -// Settings for the shared key cache +/* Settings for the shared key cache */ #define MAX_KEYS_PER_SLOT 4 #define KEYS_TIMEOUT 600 diff --git a/toxcore/announce.h b/toxcore/announce.h index 2c1183c4..5b43c9d6 100644 --- a/toxcore/announce.h +++ b/toxcore/announce.h @@ -5,6 +5,7 @@ #ifndef C_TOXCORE_TOXCORE_ANNOUNCE_H #define C_TOXCORE_TOXCORE_ANNOUNCE_H +#include #include #include "DHT.h" @@ -15,6 +16,7 @@ #include "mem.h" #include "mono_time.h" #include "network.h" +#include "rng.h" #define MAX_ANNOUNCEMENT_SIZE 512 diff --git a/toxcore/bin_pack.c b/toxcore/bin_pack.c index ab5df3d0..8e8518b8 100644 --- a/toxcore/bin_pack.c +++ b/toxcore/bin_pack.c @@ -19,7 +19,7 @@ struct Bin_Pack { cmp_ctx_t ctx; }; -static bool null_reader(cmp_ctx_t *_Nonnull ctx, void *_Nonnull data, size_t limit) +static bool null_reader(cmp_ctx_t *_Nonnull ctx, void *_Nullable data, size_t limit) { assert(limit == 0); return false; @@ -31,10 +31,18 @@ static bool null_skipper(cmp_ctx_t *_Nonnull ctx, size_t count) return false; } -static size_t buf_writer(cmp_ctx_t *_Nonnull ctx, const void *_Nonnull data, size_t count) +static size_t buf_writer(cmp_ctx_t *_Nonnull ctx, const void *_Nullable data, size_t count) { - const uint8_t *bytes = (const uint8_t *)data; - Bin_Pack *bp = (Bin_Pack *)ctx->buf; + const uint8_t *const bytes = (const uint8_t *)data; + + if (count == 0) { + return 0; + } + + if (bytes == nullptr) { + return 0; + } + Bin_Pack *const bp = (Bin_Pack *)ctx->buf; assert(bp != nullptr); const uint32_t new_pos = bp->bytes_pos + count; if (new_pos < bp->bytes_pos) { @@ -159,9 +167,30 @@ bool bin_pack_u64(Bin_Pack *bp, uint64_t val) bool bin_pack_bin(Bin_Pack *bp, const uint8_t *data, uint32_t length) { + if (length == 0) { + return cmp_write_bin_marker(&bp->ctx, 0); + } + + if (data == nullptr) { + return false; + } + return cmp_write_bin(&bp->ctx, data, length); } +bool bin_pack_str(Bin_Pack *bp, const char *data, uint32_t length) +{ + if (length == 0) { + return cmp_write_str_marker(&bp->ctx, 0); + } + + if (data == nullptr) { + return false; + } + + return cmp_write_str(&bp->ctx, data, length); +} + bool bin_pack_nil(Bin_Pack *bp) { return cmp_write_nil(&bp->ctx); @@ -197,5 +226,9 @@ bool bin_pack_u64_b(Bin_Pack *bp, uint64_t val) bool bin_pack_bin_b(Bin_Pack *bp, const uint8_t *data, uint32_t length) { + if (length > 0 && data == nullptr) { + return false; + } + return bp->ctx.write(&bp->ctx, data, length) == length; } diff --git a/toxcore/bin_pack.h b/toxcore/bin_pack.h index 83c3cfaa..c70f5cd3 100644 --- a/toxcore/bin_pack.h +++ b/toxcore/bin_pack.h @@ -162,7 +162,9 @@ bool bin_pack_u64(Bin_Pack *_Nonnull bp, uint64_t val); /** @brief Pack an empty array member as a MessagePack nil value. */ bool bin_pack_nil(Bin_Pack *_Nonnull bp); /** @brief Pack a byte array as MessagePack bin. */ -bool bin_pack_bin(Bin_Pack *_Nonnull bp, const uint8_t *_Nonnull data, uint32_t length); +bool bin_pack_bin(Bin_Pack *_Nonnull bp, const uint8_t *_Nullable data, uint32_t length); +/** @brief Pack a string as MessagePack str. */ +bool bin_pack_str(Bin_Pack *_Nonnull bp, const char *_Nullable 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. @@ -183,7 +185,7 @@ bool bin_pack_u64_b(Bin_Pack *_Nonnull bp, uint64_t val); * Note that unless you prepend the array length manually, there is no record of it in the resulting * serialised representation. */ -bool bin_pack_bin_b(Bin_Pack *_Nonnull bp, const uint8_t *_Nonnull data, uint32_t length); +bool bin_pack_bin_b(Bin_Pack *_Nonnull bp, const uint8_t *_Nullable data, uint32_t length); #ifdef __cplusplus } /* extern "C" */ diff --git a/toxcore/bin_pack_test.cc b/toxcore/bin_pack_test.cc index b17cfa80..ecff1769 100644 --- a/toxcore/bin_pack_test.cc +++ b/toxcore/bin_pack_test.cc @@ -4,40 +4,42 @@ #include +#include "attributes.h" #include "bin_unpack.h" #include "logger.h" #include "mem.h" #include "os_memory.h" +#include "test_util.hh" namespace { TEST(BinPack, TooSmallBufferIsNotExceeded) { - const uint64_t orig = 1234567812345678LL; - std::array buf; + const std::uint64_t orig = 1234567812345678LL; + std::array buf; EXPECT_FALSE(bin_pack_obj( - [](const void *obj, const Logger *logger, Bin_Pack *bp) { - return bin_pack_u64_b(bp, *static_cast(obj)); + [](const void *_Nullable obj, const Logger *_Nullable logger, Bin_Pack *_Nonnull bp) { + return bin_pack_u64_b(bp, *static_cast(REQUIRE_NOT_NULL(obj))); }, &orig, nullptr, buf.data(), buf.size())); } TEST(BinPack, PackedUint64CanBeUnpacked) { - const Memory *mem = os_memory(); - const uint64_t orig = 1234567812345678LL; - std::array buf; + const Memory *_Nonnull mem = os_memory(); + const std::uint64_t orig = 1234567812345678LL; + std::array buf; EXPECT_TRUE(bin_pack_obj( - [](const void *obj, const Logger *logger, Bin_Pack *bp) { - return bin_pack_u64_b(bp, *static_cast(obj)); + [](const void *_Nullable obj, const Logger *_Nullable logger, Bin_Pack *_Nonnull bp) { + return bin_pack_u64_b(bp, *static_cast(REQUIRE_NOT_NULL(obj))); }, &orig, nullptr, buf.data(), buf.size())); - uint64_t unpacked = 0; + std::uint64_t unpacked = 0; EXPECT_TRUE(bin_unpack_obj( mem, - [](void *obj, Bin_Unpack *bu) { - return bin_unpack_u64_b(bu, static_cast(obj)); + [](void *_Nonnull obj, Bin_Unpack *_Nonnull bu) { + return bin_unpack_u64_b(bu, static_cast(obj)); }, &unpacked, buf.data(), buf.size())); EXPECT_EQ(unpacked, 1234567812345678LL); @@ -45,38 +47,42 @@ TEST(BinPack, PackedUint64CanBeUnpacked) TEST(BinPack, MsgPackedUint8CanBeUnpackedAsUint32) { - const Memory *mem = os_memory(); - const uint8_t orig = 123; - std::array buf; + const Memory *_Nonnull mem = os_memory(); + const std::uint8_t orig = 123; + std::array buf; EXPECT_TRUE(bin_pack_obj( - [](const void *obj, const Logger *logger, Bin_Pack *bp) { - return bin_pack_u08(bp, *static_cast(obj)); + [](const void *_Nullable obj, const Logger *_Nullable logger, Bin_Pack *_Nonnull bp) { + return bin_pack_u08(bp, *static_cast(REQUIRE_NOT_NULL(obj))); }, &orig, nullptr, buf.data(), buf.size())); - uint32_t unpacked = 0; + std::uint32_t unpacked = 0; EXPECT_TRUE(bin_unpack_obj( mem, - [](void *obj, Bin_Unpack *bu) { return bin_unpack_u32(bu, static_cast(obj)); }, + [](void *_Nonnull obj, Bin_Unpack *_Nonnull bu) { + return bin_unpack_u32(bu, static_cast(obj)); + }, &unpacked, buf.data(), buf.size())); EXPECT_EQ(unpacked, 123); } TEST(BinPack, MsgPackedUint32CanBeUnpackedAsUint8IfSmallEnough) { - const Memory *mem = os_memory(); - const uint32_t orig = 123; - std::array buf; + const Memory *_Nonnull mem = os_memory(); + const std::uint32_t orig = 123; + std::array buf; EXPECT_TRUE(bin_pack_obj( - [](const void *obj, const Logger *logger, Bin_Pack *bp) { - return bin_pack_u32(bp, *static_cast(obj)); + [](const void *_Nullable obj, const Logger *_Nullable logger, Bin_Pack *_Nonnull bp) { + return bin_pack_u32(bp, *static_cast(REQUIRE_NOT_NULL(obj))); }, &orig, nullptr, buf.data(), buf.size())); - uint8_t unpacked = 0; + std::uint8_t unpacked = 0; EXPECT_TRUE(bin_unpack_obj( mem, - [](void *obj, Bin_Unpack *bu) { return bin_unpack_u08(bu, static_cast(obj)); }, + [](void *_Nonnull obj, Bin_Unpack *_Nonnull bu) { + return bin_unpack_u08(bu, static_cast(obj)); + }, &unpacked, buf.data(), buf.size())); EXPECT_EQ(unpacked, 123); @@ -85,18 +91,20 @@ TEST(BinPack, MsgPackedUint32CanBeUnpackedAsUint8IfSmallEnough) TEST(BinPack, LargeMsgPackedUint32CannotBeUnpackedAsUint8) { const Memory *mem = os_memory(); - const uint32_t orig = 1234567; - std::array buf; + const std::uint32_t orig = 1234567; + std::array buf; EXPECT_TRUE(bin_pack_obj( [](const void *obj, const Logger *logger, Bin_Pack *bp) { - return bin_pack_u32(bp, *static_cast(obj)); + return bin_pack_u32(bp, *static_cast(obj)); }, &orig, nullptr, buf.data(), buf.size())); - uint8_t unpacked = 0; + std::uint8_t unpacked = 0; EXPECT_FALSE(bin_unpack_obj( mem, - [](void *obj, Bin_Unpack *bu) { return bin_unpack_u08(bu, static_cast(obj)); }, + [](void *obj, Bin_Unpack *bu) { + return bin_unpack_u08(bu, static_cast(obj)); + }, &unpacked, buf.data(), buf.size())); } @@ -104,13 +112,13 @@ TEST(BinPack, BinCanHoldPackedInts) { const Memory *mem = os_memory(); struct Stuff { - uint64_t u64; - uint16_t u16; + std::uint64_t u64; + std::uint16_t u16; }; const Stuff orig = {1234567812345678LL, 54321}; - static const uint32_t packed_size = sizeof(uint64_t) + sizeof(uint16_t); + static const std::uint32_t packed_size = sizeof(std::uint64_t) + sizeof(std::uint16_t); - std::array buf; + std::array buf; EXPECT_TRUE(bin_pack_obj( [](const void *obj, const Logger *logger, Bin_Pack *bp) { const Stuff *self = static_cast(obj); @@ -125,7 +133,7 @@ TEST(BinPack, BinCanHoldPackedInts) mem, [](void *obj, Bin_Unpack *bu) { Stuff *stuff = static_cast(obj); - uint32_t size; + std::uint32_t size; return bin_unpack_bin_size(bu, &size) // && size == 10 // && bin_unpack_u64_b(bu, &stuff->u64) // @@ -139,38 +147,293 @@ TEST(BinPack, BinCanHoldPackedInts) TEST(BinPack, BinCanHoldArbitraryData) { const Memory *mem = os_memory(); - std::array buf; + std::array buf; EXPECT_TRUE(bin_pack_obj( [](const void *obj, const Logger *logger, Bin_Pack *bp) { return bin_pack_bin_marker(bp, 5) // - && bin_pack_bin_b(bp, reinterpret_cast("hello"), 5); + && bin_pack_bin_b(bp, reinterpret_cast("hello"), 5); }, nullptr, nullptr, buf.data(), buf.size())); - std::array str; + std::array str; EXPECT_TRUE(bin_unpack_obj( mem, [](void *obj, Bin_Unpack *bu) { - uint8_t *data = static_cast(obj); + std::uint8_t *data = static_cast(obj); return bin_unpack_bin_fixed(bu, data, 5); }, str.data(), buf.data(), buf.size())); - EXPECT_EQ(str, (std::array{'h', 'e', 'l', 'l', 'o'})); + EXPECT_EQ(str, (std::array{'h', 'e', 'l', 'l', 'o'})); } TEST(BinPack, OversizedArrayFailsUnpack) { const Memory *mem = os_memory(); - std::array buf = {0x91}; + std::array buf = {0x91}; - uint32_t size; + std::uint32_t size; EXPECT_FALSE(bin_unpack_obj( mem, [](void *obj, Bin_Unpack *bu) { - uint32_t *size_ptr = static_cast(obj); + std::uint32_t *size_ptr = static_cast(obj); return bin_unpack_array(bu, size_ptr); }, &size, buf.data(), buf.size())); } +TEST(BinPack, StringCanBePackedAndUnpacked) +{ + const Memory *mem = os_memory(); + const char *orig = "hello world"; + const uint32_t orig_len = strlen(orig); + + std::array buf; + EXPECT_TRUE(bin_pack_obj( + [](const void *obj, const Logger *logger, Bin_Pack *bp) { + const char *str = static_cast(obj); + return bin_pack_str(bp, str, strlen(str)); + }, + orig, nullptr, buf.data(), buf.size())); + + struct { + char *str; + uint32_t len; + } unpacked = {nullptr, 0}; + + EXPECT_TRUE(bin_unpack_obj( + mem, + [](void *obj, Bin_Unpack *bu) { + auto *res = static_cast(obj); + return bin_unpack_str(bu, &res->str, &res->len); + }, + &unpacked, buf.data(), buf.size())); + + EXPECT_EQ(unpacked.len, orig_len); + EXPECT_STREQ(unpacked.str, orig); + mem_delete(mem, unpacked.str); +} + +TEST(BinPack, EmptyStringCanBePackedAndUnpacked) +{ + const Memory *mem = os_memory(); + const char *orig = ""; + const uint32_t orig_len = 0; + + std::array buf; + EXPECT_TRUE(bin_pack_obj( + [](const void *obj, const Logger *logger, Bin_Pack *bp) { + const char *str = static_cast(obj); + return bin_pack_str(bp, str, 0); + }, + orig, nullptr, buf.data(), buf.size())); + + struct { + char *str; + uint32_t len; + } unpacked = {nullptr, 0}; + + EXPECT_TRUE(bin_unpack_obj( + mem, + [](void *obj, Bin_Unpack *bu) { + auto *res = static_cast(obj); + return bin_unpack_str(bu, &res->str, &res->len); + }, + &unpacked, buf.data(), buf.size())); + + EXPECT_EQ(unpacked.len, orig_len); + EXPECT_STREQ(unpacked.str, orig); + mem_delete(mem, unpacked.str); +} + +TEST(BinPack, EmptyBinCanBePackedAndUnpacked) +{ + const Memory *mem = os_memory(); + + std::array buf; + EXPECT_TRUE(bin_pack_obj( + [](const void *obj, const Logger *logger, Bin_Pack *bp) { + uint8_t dummy = 0; + return bin_pack_bin(bp, &dummy, 0); + }, + nullptr, nullptr, buf.data(), buf.size())); + + struct { + uint8_t *data; + uint32_t len; + } unpacked = {reinterpret_cast(1), + 1}; // Initialize with non-null to check it gets set to null + + EXPECT_TRUE(bin_unpack_obj( + mem, + [](void *obj, Bin_Unpack *bu) { + auto *res = static_cast(obj); + return bin_unpack_bin(bu, &res->data, &res->len); + }, + &unpacked, buf.data(), buf.size())); + + EXPECT_EQ(unpacked.len, 0); + EXPECT_EQ(unpacked.data, nullptr); +} + +TEST(BinPack, NullStringWithZeroLengthCanBePackedAndUnpacked) +{ + const Memory *mem = os_memory(); + + std::array buf; + EXPECT_TRUE(bin_pack_obj([](const void *obj, const Logger *logger, + Bin_Pack *bp) { return bin_pack_str(bp, nullptr, 0); }, + nullptr, nullptr, buf.data(), buf.size())); + + struct { + char *str; + uint32_t len; + } unpacked = {nullptr, 0}; + + EXPECT_TRUE(bin_unpack_obj( + mem, + [](void *obj, Bin_Unpack *bu) { + auto *res = static_cast(obj); + return bin_unpack_str(bu, &res->str, &res->len); + }, + &unpacked, buf.data(), buf.size())); + + EXPECT_EQ(unpacked.len, 0); + ASSERT_NE(unpacked.str, nullptr); + EXPECT_EQ(unpacked.str[0], '\0'); + mem_delete(mem, unpacked.str); +} + +TEST(BinPack, NullBinWithZeroLengthCanBePackedAndUnpacked) +{ + const Memory *mem = os_memory(); + + std::array buf; + EXPECT_TRUE(bin_pack_obj([](const void *obj, const Logger *logger, + Bin_Pack *bp) { return bin_pack_bin(bp, nullptr, 0); }, + nullptr, nullptr, buf.data(), buf.size())); + + struct { + uint8_t *data; + uint32_t len; + } unpacked = {reinterpret_cast(1), 1}; + + EXPECT_TRUE(bin_unpack_obj( + mem, + [](void *obj, Bin_Unpack *bu) { + auto *res = static_cast(obj); + return bin_unpack_bin(bu, &res->data, &res->len); + }, + &unpacked, buf.data(), buf.size())); + + EXPECT_EQ(unpacked.len, 0); + EXPECT_EQ(unpacked.data, nullptr); +} + +TEST(BinPack, PackFailsWithNullAndNonZeroLength) +{ + std::array buf; + // bin_pack_str + EXPECT_FALSE(bin_pack_obj([](const void *obj, const Logger *logger, + Bin_Pack *bp) { return bin_pack_str(bp, nullptr, 1); }, + nullptr, nullptr, buf.data(), buf.size())); + + // bin_pack_bin + EXPECT_FALSE(bin_pack_obj([](const void *obj, const Logger *logger, + Bin_Pack *bp) { return bin_pack_bin(bp, nullptr, 1); }, + nullptr, nullptr, buf.data(), buf.size())); + + // bin_pack_bin_b + EXPECT_FALSE(bin_pack_obj([](const void *obj, const Logger *logger, + Bin_Pack *bp) { return bin_pack_bin_b(bp, nullptr, 1); }, + nullptr, nullptr, buf.data(), buf.size())); +} + +TEST(BinPack, PlainBinaryZeroLengthCanBePackedAndUnpacked) +{ + const Memory *mem = os_memory(); + + std::array buf = {0xAA}; // Canary + EXPECT_TRUE(bin_pack_obj([](const void *obj, const Logger *logger, + Bin_Pack *bp) { return bin_pack_bin_b(bp, nullptr, 0); }, + nullptr, nullptr, buf.data(), buf.size())); + + // pos should not have advanced + EXPECT_EQ(buf[0], 0xAA); + + std::uint8_t dummy = 0xBB; + EXPECT_TRUE(bin_unpack_obj( + mem, + [](void *obj, Bin_Unpack *bu) { + return bin_unpack_bin_b(bu, static_cast(obj), 0); + }, + &dummy, buf.data(), 0)); + + EXPECT_EQ(dummy, 0xBB); +} + +TEST(BinPack, StringUnpackGuaranteesNonNull) +{ + const Memory *mem = os_memory(); + std::array buf; + + // Pack empty string + bin_pack_obj( + [](const void *obj, const Logger *logger, Bin_Pack *bp) { return bin_pack_str(bp, "", 0); }, + nullptr, nullptr, buf.data(), buf.size()); + + char *res = nullptr; + EXPECT_TRUE(bin_unpack_obj( + mem, + [](void *obj, Bin_Unpack *bu) { + auto **ptr = static_cast(obj); + uint32_t dummy_len; + return bin_unpack_str(bu, ptr, &dummy_len); + }, + &res, buf.data(), buf.size())); + + ASSERT_NE(res, nullptr); + EXPECT_EQ(res[0], '\0'); + mem_delete(mem, res); +} + +TEST(BinPack, UnpackFailsOnBufferOverrun) +{ + const Memory *mem = os_memory(); + + // 1. String claiming to be 100 bytes in a 5 byte buffer + std::array buf; + buf[0] = 0xD9; // str 8 + buf[1] = 100; + + struct StrRes { + char *s; + uint32_t l; + } res_str = {nullptr, 0}; + + EXPECT_FALSE(bin_unpack_obj( + mem, + [](void *obj, Bin_Unpack *bu) { + auto *res = static_cast(obj); + return bin_unpack_str(bu, &res->s, &res->l); + }, + &res_str, buf.data(), buf.size())); + + // 2. Bin claiming to be 100 bytes + buf[0] = 0xC4; // bin 8 + buf[1] = 100; + + struct BinRes { + uint8_t *b; + uint32_t l; + } res_bin = {nullptr, 0}; + + EXPECT_FALSE(bin_unpack_obj( + mem, + [](void *obj, Bin_Unpack *bu) { + auto *res = static_cast(obj); + return bin_unpack_bin(bu, &res->b, &res->l); + }, + &res_bin, buf.data(), buf.size())); +} + } // namespace diff --git a/toxcore/bin_unpack.c b/toxcore/bin_unpack.c index 883071e0..1cd0e97e 100644 --- a/toxcore/bin_unpack.c +++ b/toxcore/bin_unpack.c @@ -20,10 +20,18 @@ struct Bin_Unpack { cmp_ctx_t ctx; }; -static bool buf_reader(cmp_ctx_t *_Nonnull ctx, void *_Nonnull data, size_t limit) +static bool buf_reader(cmp_ctx_t *_Nonnull ctx, void *_Nullable data, size_t limit) { - uint8_t *bytes = (uint8_t *)data; - Bin_Unpack *reader = (Bin_Unpack *)ctx->buf; + uint8_t *const bytes = (uint8_t *)data; + + if (limit == 0) { + return true; + } + + if (bytes == nullptr) { + return false; + } + Bin_Unpack *const reader = (Bin_Unpack *)ctx->buf; assert(reader != nullptr && reader->bytes != nullptr); if (limit > reader->bytes_size) { return false; @@ -36,7 +44,10 @@ static bool buf_reader(cmp_ctx_t *_Nonnull ctx, void *_Nonnull data, size_t limi static bool buf_skipper(cmp_ctx_t *_Nonnull ctx, size_t count) { - Bin_Unpack *reader = (Bin_Unpack *)ctx->buf; + if (count == 0) { + return true; + } + Bin_Unpack *const reader = (Bin_Unpack *)ctx->buf; assert(reader != nullptr && reader->bytes != nullptr); if (count > reader->bytes_size) { return false; @@ -46,7 +57,7 @@ static bool buf_skipper(cmp_ctx_t *_Nonnull ctx, size_t count) return true; } -static size_t null_writer(cmp_ctx_t *_Nonnull ctx, const void *_Nonnull data, size_t count) +static size_t null_writer(cmp_ctx_t *_Nonnull ctx, const void *_Nullable data, size_t count) { assert(count == 0); return 0; @@ -119,6 +130,13 @@ bool bin_unpack_bin(Bin_Unpack *bu, uint8_t **data_ptr, uint32_t *data_length_pt // There aren't as many bytes as this bin claims to want to allocate. return false; } + + if (bin_size == 0) { + *data_ptr = nullptr; + *data_length_ptr = 0; + return true; + } + uint8_t *const data = (uint8_t *)mem_balloc(bu->mem, bin_size); if (data == nullptr) { @@ -135,6 +153,36 @@ bool bin_unpack_bin(Bin_Unpack *bu, uint8_t **data_ptr, uint32_t *data_length_pt return true; } +bool bin_unpack_str(Bin_Unpack *bu, char **str_ptr, uint32_t *str_length_ptr) +{ + uint32_t str_size; + if (!cmp_read_str_size(&bu->ctx, &str_size) || str_size > bu->bytes_size) { + return false; + } + + if (str_size == UINT32_MAX) { + return false; + } + + char *const str = (char *)mem_balloc(bu->mem, str_size + 1); + + if (str == nullptr) { + return false; + } + + if (str_size > 0) { + if (!bin_unpack_bin_b(bu, (uint8_t *)str, str_size)) { + mem_delete(bu->mem, str); + return false; + } + } + + str[str_size] = 0; + *str_ptr = str; + *str_length_ptr = str_size; + return true; +} + bool bin_unpack_bin_max(Bin_Unpack *bu, uint8_t *data, uint16_t *data_length_ptr, uint16_t max_data_length) { uint32_t bin_size; diff --git a/toxcore/bin_unpack.h b/toxcore/bin_unpack.h index 44bf0c93..1f676609 100644 --- a/toxcore/bin_unpack.h +++ b/toxcore/bin_unpack.h @@ -84,7 +84,9 @@ bool bin_unpack_nil(Bin_Unpack *_Nonnull bu); * 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. */ -bool bin_unpack_bin(Bin_Unpack *_Nonnull bu, uint8_t *_Nonnull *_Nonnull data_ptr, uint32_t *_Nonnull data_length_ptr); +bool bin_unpack_bin(Bin_Unpack *_Nonnull bu, uint8_t *_Nullable *_Nonnull data_ptr, uint32_t *_Nonnull data_length_ptr); +/** @brief Unpack a MessagePack str into a newly allocated string. */ +bool bin_unpack_str(Bin_Unpack *_Nonnull bu, char *_Nonnull *_Nonnull str_ptr, uint32_t *_Nonnull str_length_ptr); /** @brief Unpack a variable size MessagePack bin into a fixed size byte array. * * Stores unpacked data into `data` with its length stored in `data_length_ptr`. This function does @@ -114,7 +116,7 @@ bool bin_unpack_u32_b(Bin_Unpack *_Nonnull bu, uint32_t *_Nonnull val); bool bin_unpack_u64_b(Bin_Unpack *_Nonnull bu, uint64_t *_Nonnull val); /** @brief Read a byte array directly from the packer, consuming `length` bytes. */ -bool bin_unpack_bin_b(Bin_Unpack *_Nonnull bu, uint8_t *_Nonnull data, uint32_t length); +bool bin_unpack_bin_b(Bin_Unpack *_Nonnull bu, uint8_t *_Nullable data, uint32_t length); #ifdef __cplusplus } /* extern "C" */ diff --git a/toxcore/crypto_core.c b/toxcore/crypto_core.c index 9bf983d8..085e6db5 100644 --- a/toxcore/crypto_core.c +++ b/toxcore/crypto_core.c @@ -13,7 +13,7 @@ #include "attributes.h" #include "ccompat.h" #include "mem.h" -#include "tox_random.h" +#include "rng.h" #include "util.h" static_assert(CRYPTO_PUBLIC_KEY_SIZE == crypto_box_PUBLICKEYBYTES, @@ -195,7 +195,7 @@ uint64_t random_u64(const Random *rng) uint32_t random_range_u32(const Random *rng, uint32_t upper_bound) { - return tox_random_uniform(rng, upper_bound); + return rng_uniform(rng, upper_bound); } bool crypto_signature_create(uint8_t signature[CRYPTO_SIGNATURE_SIZE], @@ -486,5 +486,5 @@ void crypto_sha512(uint8_t hash[CRYPTO_SHA512_SIZE], const uint8_t *data, size_t void random_bytes(const Random *rng, uint8_t *bytes, size_t length) { - tox_random_bytes(rng, bytes, length); + rng_bytes(rng, bytes, length); } diff --git a/toxcore/crypto_core.h b/toxcore/crypto_core.h index 99b976ae..5f6c0628 100644 --- a/toxcore/crypto_core.h +++ b/toxcore/crypto_core.h @@ -17,7 +17,7 @@ #include "attributes.h" #include "mem.h" -#include "tox_random.h" +#include "rng.h" #ifdef __cplusplus extern "C" { @@ -190,11 +190,6 @@ bool crypto_sha512_eq(const uint8_t cksum1[_Nonnull CRYPTO_SHA512_SIZE], const u */ bool crypto_sha256_eq(const uint8_t cksum1[_Nonnull CRYPTO_SHA256_SIZE], const uint8_t cksum2[_Nonnull CRYPTO_SHA256_SIZE]); -/** - * @brief Shorter internal name for the RNG type. - */ -typedef Tox_Random Random; - /** * @brief Return a random 8 bit integer. */ diff --git a/toxcore/crypto_core_test.cc b/toxcore/crypto_core_test.cc index c85c2d49..f7cd1d74 100644 --- a/toxcore/crypto_core_test.cc +++ b/toxcore/crypto_core_test.cc @@ -7,17 +7,18 @@ #include #include +#include #include #include "crypto_core_test_util.hh" namespace { -using HmacKey = std::array; -using Hmac = std::array; -using SecretKey = std::array; -using Signature = std::array; -using Nonce = std::array; +using HmacKey = std::array; +using Hmac = std::array; +using SecretKey = std::array; +using Signature = std::array; +using Nonce = std::array; using tox::test::SimulatedEnvironment; @@ -26,8 +27,8 @@ TEST(PkEqual, TwoRandomIdsAreNotEqual) SimulatedEnvironment env; auto &rng = env.fake_random(); - uint8_t pk1[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t pk2[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t pk1[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t pk2[CRYPTO_PUBLIC_KEY_SIZE]; rng.bytes(pk1, sizeof(pk1)); rng.bytes(pk2, sizeof(pk2)); @@ -40,8 +41,8 @@ TEST(PkEqual, IdCopyMakesKeysEqual) SimulatedEnvironment env; auto &rng = env.fake_random(); - uint8_t pk1[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t pk2[CRYPTO_PUBLIC_KEY_SIZE] = {0}; + std::uint8_t pk1[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t pk2[CRYPTO_PUBLIC_KEY_SIZE] = {0}; rng.bytes(pk1, sizeof(pk1)); @@ -53,8 +54,8 @@ TEST(PkEqual, IdCopyMakesKeysEqual) TEST(CryptoCore, EncryptLargeData) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); - auto c_rng = env.fake_random().get_c_random(); + auto c_mem = env.fake_memory().c_memory(); + auto c_rng = env.fake_random().c_random(); Nonce nonce{}; PublicKey pk; @@ -62,8 +63,8 @@ TEST(CryptoCore, EncryptLargeData) crypto_new_keypair(&c_rng, pk.data(), sk.data()); // 100 MiB of data (all zeroes, doesn't matter what's inside). - std::vector plain(100 * 1024 * 1024); - std::vector encrypted(plain.size() + CRYPTO_MAC_SIZE); + std::vector plain(100 * 1024 * 1024); + std::vector encrypted(plain.size() + CRYPTO_MAC_SIZE); encrypt_data( &c_mem, pk.data(), sk.data(), nonce.data(), plain.data(), plain.size(), encrypted.data()); @@ -105,18 +106,18 @@ TEST(CryptoCore, IncrementNonceNumber) TEST(CryptoCore, Signatures) { SimulatedEnvironment env; - auto c_rng = env.fake_random().get_c_random(); + auto c_rng = env.fake_random().c_random(); Extended_Public_Key pk; Extended_Secret_Key sk; EXPECT_TRUE(create_extended_keypair(&pk, &sk, &c_rng)); - std::vector message{0}; + std::vector message{0}; message.clear(); // Try a few different sizes, including empty 0 length message. - for (uint8_t i = 0; i < 100; ++i) { + for (std::uint8_t i = 0; i < 100; ++i) { Signature signature; EXPECT_TRUE(crypto_signature_create( signature.data(), message.data(), message.size(), get_sig_sk(&sk))); @@ -130,16 +131,16 @@ TEST(CryptoCore, Signatures) TEST(CryptoCore, Hmac) { SimulatedEnvironment env; - auto c_rng = env.fake_random().get_c_random(); + auto c_rng = env.fake_random().c_random(); HmacKey sk; new_hmac_key(&c_rng, sk.data()); - std::vector message{0}; + std::vector message{0}; message.clear(); // Try a few different sizes, including empty 0 length message. - for (uint8_t i = 0; i < 100; ++i) { + for (std::uint8_t i = 0; i < 100; ++i) { Hmac auth; crypto_hmac(auth.data(), sk.data(), message.data(), message.size()); EXPECT_TRUE(crypto_hmac_verify(auth.data(), sk.data(), message.data(), message.size())); diff --git a/toxcore/crypto_core_test_util.cc b/toxcore/crypto_core_test_util.cc index 9eb3346e..b352fa7f 100644 --- a/toxcore/crypto_core_test_util.cc +++ b/toxcore/crypto_core_test_util.cc @@ -2,22 +2,30 @@ #include #include +#include #include "crypto_core.h" #include "test_util.hh" -PublicKey random_pk(const Random *rng) +PublicKey random_pk(const Random *_Nonnull rng) { PublicKey pk; random_bytes(rng, pk.data(), pk.size()); return pk; } +std::array random_sk(const Random *rng) +{ + std::array sk; + random_bytes(rng, sk.data(), sk.size()); + return sk; +} + std::ostream &operator<<(std::ostream &out, PublicKey const &pk) { out << '"'; - for (uint8_t byte : pk) { - out << std::setw(2) << std::setfill('0') << std::hex << uint32_t(byte); + for (std::uint8_t byte : pk) { + out << std::setw(2) << std::setfill('0') << std::hex << std::uint32_t(byte); } out << '"'; return out; diff --git a/toxcore/crypto_core_test_util.hh b/toxcore/crypto_core_test_util.hh index 5f85d04c..816d8cb4 100644 --- a/toxcore/crypto_core_test_util.hh +++ b/toxcore/crypto_core_test_util.hh @@ -3,13 +3,15 @@ #include #include +#include #include +#include "attributes.h" #include "crypto_core.h" #include "test_util.hh" -struct PublicKey : private std::array { - using Base = std::array; +struct PublicKey : private std::array { + using Base = std::array; using Base::begin; using Base::data; @@ -18,16 +20,16 @@ struct PublicKey : private std::array { using Base::operator[]; PublicKey() = default; - explicit PublicKey(uint8_t const (&arr)[CRYPTO_PUBLIC_KEY_SIZE]) + explicit PublicKey(std::uint8_t const (&arr)[CRYPTO_PUBLIC_KEY_SIZE]) : PublicKey(to_array(arr)) { } - explicit PublicKey(std::array const &arr) + explicit PublicKey(std::array const &arr) { std::copy(arr.begin(), arr.end(), begin()); } - PublicKey(std::initializer_list const &arr) + PublicKey(std::initializer_list const &arr) { std::copy(arr.begin(), arr.end(), begin()); } @@ -52,6 +54,8 @@ inline bool operator==(PublicKey::Base const &pk1, PublicKey const &pk2) std::ostream &operator<<(std::ostream &out, PublicKey const &pk); -PublicKey random_pk(const Tox_Random *rng); +PublicKey random_pk(const Random *_Nonnull rng); + +std::array random_sk(const Random *_Nonnull rng); #endif // C_TOXCORE_TOXCORE_CRYPTO_CORE_TEST_UTIL_H diff --git a/toxcore/ev.c b/toxcore/ev.c new file mode 100644 index 00000000..c47dd310 --- /dev/null +++ b/toxcore/ev.c @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#include "ev.h" + +#include + +#include "ccompat.h" + +bool ev_add(Ev *ev, Socket sock, Ev_Events events, void *data) +{ + assert(ev != nullptr); + return ev->funcs->add_callback(ev->user_data, sock, events, data); +} + +bool ev_mod(Ev *ev, Socket sock, Ev_Events events, void *data) +{ + assert(ev != nullptr); + return ev->funcs->mod_callback(ev->user_data, sock, events, data); +} + +bool ev_del(Ev *ev, Socket sock) +{ + assert(ev != nullptr); + return ev->funcs->del_callback(ev->user_data, sock); +} + +int32_t ev_run(Ev *ev, Ev_Result *results, uint32_t max_results, int32_t timeout_ms) +{ + assert(ev != nullptr); + + if (max_results == 0) { + return 0; + } + + return ev->funcs->run_callback(ev->user_data, results, max_results, timeout_ms); +} + +void ev_kill(Ev *ev) +{ + if (ev == nullptr) { + return; + } + + ev->funcs->kill_callback(ev); +} diff --git a/toxcore/ev.h b/toxcore/ev.h new file mode 100644 index 00000000..954281f9 --- /dev/null +++ b/toxcore/ev.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#ifndef C_TOXCORE_TOXCORE_EV_H +#define C_TOXCORE_TOXCORE_EV_H + +#include +#include + +#include "attributes.h" +#include "net.h" // For Socket + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Ev Ev; + +/** + * @brief Event types to monitor. + */ +typedef uint8_t Ev_Events; + +#define EV_READ (1 << 0) +#define EV_WRITE (1 << 1) +#define EV_ERROR (1 << 2) + +/** + * @brief Result of a triggered event. + */ +typedef struct Ev_Result { + Socket sock; + Ev_Events events; + void *_Nullable data; +} Ev_Result; + +/** + * @brief Add a socket to the monitored set. + * + * This is called by `ev_add` to register a socket for event monitoring. + * + * @param self Backend-specific state. + * @param sock The socket to monitor. + * @param events Bitmask of events to monitor (EV_READ, EV_WRITE). + * @param data User context pointer to be returned in Ev_Result. + * + * @return true on success, false if the socket is already registered or on error. + */ +typedef bool ev_add_cb(void *_Nullable self, Socket sock, Ev_Events events, void *_Nullable data); + +/** + * @brief Modify a registered socket's monitoring parameters. + * + * This is called by `ev_mod` to change the events or user data for a socket + * that is already being monitored. + * + * @param self Backend-specific state. + * @param sock The socket to modify. + * @param events New bitmask of events to monitor. + * @param data New user context pointer. + * + * @return true on success, false if the socket is not found or on error. + */ +typedef bool ev_mod_cb(void *_Nullable self, Socket sock, Ev_Events events, void *_Nullable data); + +/** + * @brief Remove a socket from the monitored set. + * + * This is called by `ev_del` to stop monitoring a socket. + * + * @param self Backend-specific state. + * @param sock The socket to remove. + * + * @return true on success, false if the socket is not found. + */ +typedef bool ev_del_cb(void *_Nullable self, Socket sock); + +/** + * @brief Wait for events on registered sockets. + * + * This is called by `ev_run`. It blocks until at least one event occurs, + * the timeout expires, or `ev_break` is called. + * + * @param self Backend-specific state. + * @param results Array to be populated with triggered events. + * @param max_results Maximum number of results to store in the array. + * @param timeout_ms Maximum time to wait in milliseconds. 0 for non-blocking, + * -1 for infinite wait. + * + * @return Number of events stored in `results`, 0 on timeout, or -1 on error. + */ +typedef int32_t ev_run_cb(void *_Nullable self, Ev_Result results[_Nonnull], uint32_t max_results, int32_t timeout_ms); + +/** @brief Cleanup the event loop and free the Ev object. */ +typedef void ev_kill_cb(Ev *_Nonnull ev); + +/** + * @brief Virtual function table for Ev. + */ +typedef struct Ev_Funcs { + ev_add_cb *_Nonnull add_callback; + ev_mod_cb *_Nonnull mod_callback; + ev_del_cb *_Nonnull del_callback; + ev_run_cb *_Nonnull run_callback; + ev_kill_cb *_Nonnull kill_callback; +} Ev_Funcs; + +/** + * @brief The Event Loop object. + */ +struct Ev { + const Ev_Funcs *_Nonnull funcs; + void *_Nullable user_data; +}; + +/** + * @brief Wrapper functions. + */ +bool ev_add(Ev *_Nonnull ev, Socket sock, Ev_Events events, void *_Nullable data); +bool ev_mod(Ev *_Nonnull ev, Socket sock, Ev_Events events, void *_Nullable data); +bool ev_del(Ev *_Nonnull ev, Socket sock); +int32_t ev_run(Ev *_Nonnull ev, Ev_Result results[_Nonnull], uint32_t max_results, int32_t timeout_ms); +void ev_kill(Ev *_Nullable ev); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* C_TOXCORE_TOXCORE_EV_H */ diff --git a/toxcore/ev_bench.cc b/toxcore/ev_bench.cc new file mode 100644 index 00000000..d4e0240a --- /dev/null +++ b/toxcore/ev_bench.cc @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#include + +#include +#include + +#include "ev_test_util.hh" +#include "logger.h" +#include "net.h" +#include "os_event.h" +#include "os_memory.h" +#include "os_network.h" + +namespace { + +class EvBenchFixture : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State &state) override + { + if (os_network() == nullptr) { + setup_error = "os_network failed"; + return; + } + mem = os_memory(); + log = logger_new(mem); + // We don't want log output during benchmark + ev = os_event_new(mem, log); + } + + void TearDown(const ::benchmark::State &state) override + { + for (size_t i = 0; i < sockets_r.size(); ++i) { + close_pair(sockets_r[i], sockets_w[i]); + } + sockets_r.clear(); + sockets_w.clear(); + ev_kill(ev); + logger_kill(log); + } + + const Memory *mem = nullptr; + Logger *log = nullptr; + Ev *ev = nullptr; + std::vector sockets_r; + std::vector sockets_w; + int tag = 1; + std::string setup_error; +}; + +// Specialized SetUp to handle socket creation based on range +class EvBenchSocketsFixture : public EvBenchFixture { +public: + void SetUp(const ::benchmark::State &state) override + { + EvBenchFixture::SetUp(state); + if (!setup_error.empty()) { + return; + } + const int num_sockets = state.range(0); + + for (int i = 0; i < num_sockets; ++i) { + Socket r{}, w{}; + if (create_pair(&r, &w) != 0) { + setup_error = "Failed to create socket pair"; + return; + } + sockets_r.push_back(r); + sockets_w.push_back(w); + if (!ev_add(ev, r, EV_READ, &tag)) { + setup_error = "Failed to add socket to event loop"; + return; + } + } + } +}; + +BENCHMARK_DEFINE_F(EvBenchSocketsFixture, EventLoopRun)(benchmark::State &state) +{ + if (!setup_error.empty()) { + state.SkipWithError(setup_error.c_str()); + return; + } + // Sockets are already created in SetUp. + + Ev_Result results[1]; + char buf = 'x'; + + // We trigger the last socket + if (write_socket(sockets_w.back(), &buf, 1) != 1) { + state.SkipWithError("Failed to write to socket"); + return; + } + + for (auto _ : state) { + int32_t n = ev_run(ev, results, 1, 100); + if (n != 1) { + state.SkipWithError("ev_run did not return event"); + break; + } + } +} + +BENCHMARK_REGISTER_F(EvBenchSocketsFixture, EventLoopRun)->Range(8, 1024); + +BENCHMARK_DEFINE_F(EvBenchSocketsFixture, EventLoopMultipleActive)(benchmark::State &state) +{ + if (!setup_error.empty()) { + state.SkipWithError(setup_error.c_str()); + return; + } + + const int num_sockets = state.range(0); + const int num_active = state.range(1); + + if (num_active > num_sockets) { + state.SkipWithError("Active sockets cannot exceed total sockets"); + return; + } + + char buf = 'x'; + // Trigger 'num_active' sockets + for (int i = 0; i < num_active; ++i) { + if (write_socket(sockets_w[i], &buf, 1) != 1) { + state.SkipWithError("Failed to write socket"); + return; + } + } + + std::vector results(num_active); + + for (auto _ : state) { + int32_t n = ev_run(ev, results.data(), num_active, 100); + if (n != num_active) { + state.SkipWithError("ev_run did not return expected number of events"); + break; + } + } +} + +// Fix N=1024, vary M +BENCHMARK_REGISTER_F(EvBenchSocketsFixture, EventLoopMultipleActive) + ->Args({1024, 1}) + ->Args({1024, 10}) + ->Args({1024, 100}) + ->Args({1024, 1024}); + +} // namespace + +BENCHMARK_MAIN(); diff --git a/toxcore/ev_test.cc b/toxcore/ev_test.cc new file mode 100644 index 00000000..438d4eb7 --- /dev/null +++ b/toxcore/ev_test.cc @@ -0,0 +1,329 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#include "ev.h" + +#include + +#include + +#include "ev_test_util.hh" +#include "logger.h" +#include "net.h" +#include "os_event.h" +#include "os_memory.h" +#include "os_network.h" + +#ifndef _WIN32 +#include +#endif + +namespace { + +class EvTest : public ::testing::Test { + static void logger_cb_stderr(void *context, Logger_Level level, const char *file, uint32_t line, + const char *func, const char *message, void *userdata) + { + fprintf(stderr, "[%d] %s:%u: %s: %s\n", level, file, line, func, message); + } + +protected: + void SetUp() override + { + ASSERT_NE(os_network(), nullptr); // WSAStartup + mem = os_memory(); + log = logger_new(mem); + logger_callback_log(log, logger_cb_stderr, nullptr, nullptr); + ev = os_event_new(mem, log); + ASSERT_NE(ev, nullptr); + } + + void TearDown() override + { + ev_kill(ev); + logger_kill(log); + } + + const Memory *mem; + Logger *log; + Ev *ev; + int tag1; + int tag2; + int tag3; + int tag4; +}; + +TEST_F(EvTest, Lifecycle) +{ + // Already covered by SetUp/TearDown +} + +TEST_F(EvTest, AddDel) +{ + Socket s1{}, s2{}; + ASSERT_EQ(create_pair(&s1, &s2), 0); + + EXPECT_TRUE(ev_add(ev, s1, EV_READ, &tag1)); + EXPECT_TRUE(ev_add(ev, s2, EV_WRITE, &tag2)); + + // Adding same socket again should fail + EXPECT_FALSE(ev_add(ev, s1, EV_READ, &tag3)); + + EXPECT_TRUE(ev_del(ev, s1)); + EXPECT_TRUE(ev_del(ev, s2)); + + // Deleting non-existent socket should fail + EXPECT_FALSE(ev_del(ev, s1)); + + close_pair(s1, s2); +} + +TEST_F(EvTest, RunPipe) +{ + Socket rs{}, ws{}; + ASSERT_EQ(create_pair(&rs, &ws), 0); + + EXPECT_TRUE(ev_add(ev, rs, EV_READ, &tag4)); + + Ev_Result results[1]; + // Should timeout immediately + EXPECT_EQ(ev_run(ev, results, 1, 0), 0); + + // Write something to the pipe/socket + char buf = 'x'; + ASSERT_EQ(write_socket(ws, &buf, 1), 1); + + // Should now be readable + int32_t n = ev_run(ev, results, 1, 100); + EXPECT_EQ(n, 1); + EXPECT_EQ(net_socket_to_native(results[0].sock), net_socket_to_native(rs)); + EXPECT_EQ(results[0].events, EV_READ); + EXPECT_EQ(results[0].data, &tag4); + + close_pair(rs, ws); +} + +TEST_F(EvTest, WriteEvent) +{ + Socket rs{}, ws{}; + ASSERT_EQ(create_pair(&rs, &ws), 0); + + // Register write end for EV_WRITE + EXPECT_TRUE(ev_add(ev, ws, EV_WRITE, &tag1)); + + Ev_Result results[1]; + // Should be immediately writable + int32_t n = ev_run(ev, results, 1, 100); + EXPECT_EQ(n, 1); + EXPECT_EQ(net_socket_to_native(results[0].sock), net_socket_to_native(ws)); + EXPECT_EQ(results[0].events, EV_WRITE); + EXPECT_EQ(results[0].data, &tag1); + + close_pair(rs, ws); +} + +TEST_F(EvTest, Mod) +{ + Socket rs{}, ws{}; + ASSERT_EQ(create_pair(&rs, &ws), 0); + + EXPECT_TRUE(ev_add(ev, rs, EV_READ, &tag1)); + EXPECT_TRUE(ev_mod(ev, rs, EV_READ, &tag2)); + + // Write something to the pipe/socket to make it readable + char buf = 'x'; + ASSERT_EQ(write_socket(ws, &buf, 1), 1); + + Ev_Result results[1]; + int32_t n = ev_run(ev, results, 1, 100); + EXPECT_EQ(n, 1); + EXPECT_EQ(net_socket_to_native(results[0].sock), net_socket_to_native(rs)); + EXPECT_EQ(results[0].events, EV_READ); + EXPECT_EQ(results[0].data, &tag2); + + close_pair(rs, ws); +} + +TEST_F(EvTest, MultipleEvents) +{ + Socket rs1{}, ws1{}; + Socket rs2{}, ws2{}; + ASSERT_EQ(create_pair(&rs1, &ws1), 0); + ASSERT_EQ(create_pair(&rs2, &ws2), 0); + + EXPECT_TRUE(ev_add(ev, rs1, EV_READ, &tag1)); + EXPECT_TRUE(ev_add(ev, rs2, EV_READ, &tag2)); + + char buf = 'x'; + ASSERT_EQ(write_socket(ws1, &buf, 1), 1); + ASSERT_EQ(write_socket(ws2, &buf, 1), 1); + + Ev_Result results[2]; + int32_t n = ev_run(ev, results, 2, 100); + EXPECT_EQ(n, 2); + + bool found1 = false; + bool found2 = false; + for (int i = 0; i < 2; ++i) { + if (results[i].data == &tag1) + found1 = true; + if (results[i].data == &tag2) + found2 = true; + } + EXPECT_TRUE(found1); + EXPECT_TRUE(found2); + + close_pair(rs1, ws1); + close_pair(rs2, ws2); +} + +TEST_F(EvTest, MaxResults) +{ + Socket rs1{}, ws1{}; + Socket rs2{}, ws2{}; + ASSERT_EQ(create_pair(&rs1, &ws1), 0); + ASSERT_EQ(create_pair(&rs2, &ws2), 0); + + EXPECT_TRUE(ev_add(ev, rs1, EV_READ, &tag1)); + EXPECT_TRUE(ev_add(ev, rs2, EV_READ, &tag2)); + + char buf = 'x'; + ASSERT_EQ(write_socket(ws1, &buf, 1), 1); + ASSERT_EQ(write_socket(ws2, &buf, 1), 1); + + Ev_Result results[1]; + int32_t n = ev_run(ev, results, 1, 100); + EXPECT_EQ(n, 1); + + // The second event should still be there for the next run + n = ev_run(ev, results, 1, 100); + EXPECT_EQ(n, 1); + + close_pair(rs1, ws1); + close_pair(rs2, ws2); +} + +TEST_F(EvTest, EmptyLoop) +{ + Ev_Result results[1]; + // Should timeout immediately + EXPECT_EQ(ev_run(ev, results, 1, 10), 0); +} + +TEST_F(EvTest, ZeroMaxResults) +{ + Socket rs{}, ws{}; + ASSERT_EQ(create_pair(&rs, &ws), 0); + EXPECT_TRUE(ev_add(ev, rs, EV_READ, nullptr)); + + char buf = 'x'; + ASSERT_EQ(write_socket(ws, &buf, 1), 1); + + Ev_Result results[1]; + int32_t n = ev_run(ev, results, 0, 100); + EXPECT_LE(n, 0); + + close_pair(rs, ws); +} + +TEST_F(EvTest, ErrorEvent) +{ + Socket rs{}, ws{}; + ASSERT_EQ(create_pair(&rs, &ws), 0); + + EXPECT_TRUE(ev_add(ev, rs, EV_READ, &tag1)); + + // Close the write end to potentially trigger something on the read end + close_socket(ws); + + Ev_Result results[1]; + int32_t n = ev_run(ev, results, 1, 100); + // On Linux, closing the write end of a pipe makes the read end readable (EOF). + EXPECT_EQ(n, 1); + EXPECT_EQ(net_socket_to_native(results[0].sock), net_socket_to_native(rs)); + + close_socket(rs); +} + +TEST_F(EvTest, ReallocPointers) +{ + Socket rs{}, ws{}; + ASSERT_EQ(create_pair(&rs, &ws), 0); + + // Add first socket. This will be at index 0. + EXPECT_TRUE(ev_add(ev, rs, EV_READ, &tag1)); + + // Add enough sockets to force realloc of regs array. + // Initial capacity is 0, then 8. + // We need > 8 sockets. Let's add 20 more. + std::vector extra_sockets_r; + std::vector extra_sockets_w; + + for (int i = 0; i < 20; ++i) { + Socket r{}, w{}; + ASSERT_EQ(create_pair(&r, &w), 0); + extra_sockets_r.push_back(r); + extra_sockets_w.push_back(w); + EXPECT_TRUE(ev_add(ev, r, EV_READ, nullptr)); + } + + // Now write to the first socket to trigger event. + char buf = 'x'; + ASSERT_EQ(write_socket(ws, &buf, 1), 1); + + Ev_Result results[1]; + int32_t n = ev_run(ev, results, 1, 100); + EXPECT_EQ(n, 1); + + // Check if we got the correct data back + EXPECT_EQ(net_socket_to_native(results[0].sock), net_socket_to_native(rs)); + EXPECT_EQ(results[0].data, &tag1); + + // Cleanup + close_pair(rs, ws); + for (size_t i = 0; i < extra_sockets_r.size(); ++i) { + close_pair(extra_sockets_r[i], extra_sockets_w[i]); + } +} + +#ifdef EV_USE_EPOLL +TEST(EvManualTest, ExhaustFds) +{ + ASSERT_NE(os_network(), nullptr); + const Memory *mem = os_memory(); + Logger *log = logger_new(mem); + + // Consume all file descriptors + std::vector fds; + while (true) { + int fd = dup(0); + if (fd < 0) { + break; + } + fds.push_back(fd); + } + + // New event loop creation should fail gracefully + Ev *ev = os_event_new(mem, log); + EXPECT_EQ(ev, nullptr); + + // Release one FD and try again (epoll_create needs 1 FD) + if (!fds.empty()) { + close(fds.back()); + fds.pop_back(); + + ev = os_event_new(mem, log); + EXPECT_NE(ev, nullptr); + ev_kill(ev); + } + + // Cleanup + for (int fd : fds) { + close(fd); + } + logger_kill(log); +} +#endif // EV_USE_EPOLL + +} // namespace diff --git a/toxcore/ev_test_util.cc b/toxcore/ev_test_util.cc new file mode 100644 index 00000000..d2510203 --- /dev/null +++ b/toxcore/ev_test_util.cc @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#include "ev_test_util.hh" + +#include "net.h" + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +#ifdef _WIN32 +int create_pair(Socket *rs, Socket *ws) +{ + SOCKET listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listener == INVALID_SOCKET) + return -1; + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = 0; + + if (bind(listener, (struct sockaddr *)&addr, sizeof(addr)) != 0) { + closesocket(listener); + return -1; + } + + if (listen(listener, 1) != 0) { + closesocket(listener); + return -1; + } + + socklen_t len = sizeof(addr); + if (getsockname(listener, (struct sockaddr *)&addr, &len) != 0) { + closesocket(listener); + return -1; + } + + SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (client == INVALID_SOCKET) { + closesocket(listener); + return -1; + } + + if (connect(client, (struct sockaddr *)&addr, sizeof(addr)) != 0) { + closesocket(client); + closesocket(listener); + return -1; + } + + SOCKET server = accept(listener, nullptr, nullptr); + if (server == INVALID_SOCKET) { + closesocket(client); + closesocket(listener); + return -1; + } + + closesocket(listener); + + *rs = net_socket_from_native((int)client); + *ws = net_socket_from_native((int)server); + return 0; +} + +void close_socket(Socket s) { closesocket(net_socket_to_native(s)); } + +void close_pair(Socket s1, Socket s2) +{ + closesocket(net_socket_to_native(s1)); + closesocket(net_socket_to_native(s2)); +} + +int write_socket(Socket s, const void *buf, size_t count) +{ + return send(net_socket_to_native(s), (const char *)buf, (int)count, 0); +} +#else +int create_pair(Socket *rs, Socket *ws) +{ + int pipefds[2]; + if (pipe(pipefds) != 0) + return -1; + *rs = net_socket_from_native(pipefds[0]); + *ws = net_socket_from_native(pipefds[1]); + return 0; +} + +void close_socket(Socket s) { close(net_socket_to_native(s)); } + +void close_pair(Socket s1, Socket s2) +{ + close(net_socket_to_native(s1)); + close(net_socket_to_native(s2)); +} + +int write_socket(Socket s, const void *buf, size_t count) +{ + return write(net_socket_to_native(s), buf, count); +} +#endif diff --git a/toxcore/ev_test_util.hh b/toxcore/ev_test_util.hh new file mode 100644 index 00000000..c56f1fe0 --- /dev/null +++ b/toxcore/ev_test_util.hh @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#ifndef C_TOXCORE_TOXCORE_EV_TEST_UTIL_HH +#define C_TOXCORE_TOXCORE_EV_TEST_UTIL_HH + +#include + +#include "net.h" + +/** + * @brief Creates a connected pair of sockets (like socketpair or a TCP loopback connection). + * @param rs [out] The read-side socket. + * @param ws [out] The write-side socket. + * @return 0 on success, -1 on failure. + */ +int create_pair(Socket *rs, Socket *ws); + +/** + * @brief Closes the socket pair. + */ +void close_pair(Socket s1, Socket s2); + +/** + * @brief Closes a single socket. + */ +void close_socket(Socket s); + +/** + * @brief Writes data to a socket. + */ +int write_socket(Socket s, const void *buf, size_t count); + +#endif /* C_TOXCORE_TOXCORE_EV_TEST_UTIL_HH */ diff --git a/toxcore/events/conference_connected.c b/toxcore/events/conference_connected.c index cf03b355..437c4d2d 100644 --- a/toxcore/events/conference_connected.c +++ b/toxcore/events/conference_connected.c @@ -14,6 +14,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -86,7 +87,7 @@ Tox_Event_Conference_Connected *tox_event_conference_connected_new(const Memory void tox_event_conference_connected_free(Tox_Event_Conference_Connected *conference_connected, const Memory *mem) { if (conference_connected != nullptr) { - tox_event_conference_connected_destruct((Tox_Event_Conference_Connected * _Nonnull)conference_connected, mem); + tox_event_conference_connected_destruct(conference_connected, mem); } mem_delete(mem, conference_connected); } @@ -147,7 +148,8 @@ static Tox_Event_Conference_Connected *tox_event_conference_connected_alloc(Tox_ *****************************************************/ void tox_events_handle_conference_connected( - Tox *tox, uint32_t conference_number, + Tox *tox, + uint32_t conference_number, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -159,3 +161,14 @@ void tox_events_handle_conference_connected( tox_event_conference_connected_set_conference_number(conference_connected, conference_number); } + +void tox_events_handle_conference_connected_dispatch(Tox *tox, const Tox_Event_Conference_Connected *event, void *user_data) +{ + if (tox->conference_connected_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->conference_connected_callback(tox, event->conference_number, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/conference_invite.c b/toxcore/events/conference_invite.c index 856eec1e..117be3db 100644 --- a/toxcore/events/conference_invite.c +++ b/toxcore/events/conference_invite.c @@ -16,6 +16,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -27,7 +28,7 @@ struct Tox_Event_Conference_Invite { uint32_t friend_number; Tox_Conference_Type type; - uint8_t *cookie; + uint8_t *_Nullable cookie; uint32_t cookie_length; }; @@ -68,6 +69,12 @@ static bool tox_event_conference_invite_set_cookie(Tox_Event_Conference_Invite * return true; } + if (cookie_length == 0) { + conference_invite->cookie = nullptr; + conference_invite->cookie_length = 0; + return true; + } + uint8_t *cookie_copy = (uint8_t *)mem_balloc(mem, cookie_length); if (cookie_copy == nullptr) { @@ -149,7 +156,7 @@ Tox_Event_Conference_Invite *tox_event_conference_invite_new(const Memory *mem) void tox_event_conference_invite_free(Tox_Event_Conference_Invite *conference_invite, const Memory *mem) { if (conference_invite != nullptr) { - tox_event_conference_invite_destruct((Tox_Event_Conference_Invite * _Nonnull)conference_invite, mem); + tox_event_conference_invite_destruct(conference_invite, mem); } mem_delete(mem, conference_invite); } @@ -210,7 +217,10 @@ static Tox_Event_Conference_Invite *tox_event_conference_invite_alloc(Tox_Events *****************************************************/ void tox_events_handle_conference_invite( - Tox *tox, uint32_t friend_number, Tox_Conference_Type type, const uint8_t *cookie, size_t length, + 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); @@ -222,5 +232,18 @@ void tox_events_handle_conference_invite( 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, state->mem, cookie, length); + if (!tox_event_conference_invite_set_cookie(conference_invite, state->mem, cookie, length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_conference_invite_dispatch(Tox *tox, const Tox_Event_Conference_Invite *event, void *user_data) +{ + if (tox->conference_invite_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->conference_invite_callback(tox, event->friend_number, event->type, event->cookie, event->cookie_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/conference_message.c b/toxcore/events/conference_message.c index b59e86bc..02bd8ccd 100644 --- a/toxcore/events/conference_message.c +++ b/toxcore/events/conference_message.c @@ -16,6 +16,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -28,7 +29,7 @@ struct Tox_Event_Conference_Message { uint32_t conference_number; uint32_t peer_number; Tox_Message_Type type; - uint8_t *message; + uint8_t *_Nullable message; uint32_t message_length; }; @@ -80,6 +81,12 @@ static bool tox_event_conference_message_set_message(Tox_Event_Conference_Messag return true; } + if (message_length == 0) { + conference_message->message = nullptr; + conference_message->message_length = 0; + return true; + } + uint8_t *message_copy = (uint8_t *)mem_balloc(mem, message_length); if (message_copy == nullptr) { @@ -163,7 +170,7 @@ Tox_Event_Conference_Message *tox_event_conference_message_new(const Memory *mem void tox_event_conference_message_free(Tox_Event_Conference_Message *conference_message, const Memory *mem) { if (conference_message != nullptr) { - tox_event_conference_message_destruct((Tox_Event_Conference_Message * _Nonnull)conference_message, mem); + tox_event_conference_message_destruct(conference_message, mem); } mem_delete(mem, conference_message); } @@ -224,7 +231,11 @@ static Tox_Event_Conference_Message *tox_event_conference_message_alloc(Tox_Even *****************************************************/ 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, + 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); @@ -237,5 +248,18 @@ void tox_events_handle_conference_message( 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, state->mem, message, length); + if (!tox_event_conference_message_set_message(conference_message, state->mem, message, length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_conference_message_dispatch(Tox *tox, const Tox_Event_Conference_Message *event, void *user_data) +{ + if (tox->conference_message_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->conference_message_callback(tox, event->conference_number, event->peer_number, event->type, event->message, event->message_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/conference_peer_list_changed.c b/toxcore/events/conference_peer_list_changed.c index b821511a..93b06335 100644 --- a/toxcore/events/conference_peer_list_changed.c +++ b/toxcore/events/conference_peer_list_changed.c @@ -14,6 +14,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -86,7 +87,7 @@ Tox_Event_Conference_Peer_List_Changed *tox_event_conference_peer_list_changed_n void tox_event_conference_peer_list_changed_free(Tox_Event_Conference_Peer_List_Changed *conference_peer_list_changed, const Memory *mem) { if (conference_peer_list_changed != nullptr) { - tox_event_conference_peer_list_changed_destruct((Tox_Event_Conference_Peer_List_Changed * _Nonnull)conference_peer_list_changed, mem); + tox_event_conference_peer_list_changed_destruct(conference_peer_list_changed, mem); } mem_delete(mem, conference_peer_list_changed); } @@ -147,7 +148,8 @@ static Tox_Event_Conference_Peer_List_Changed *tox_event_conference_peer_list_ch *****************************************************/ void tox_events_handle_conference_peer_list_changed( - Tox *tox, uint32_t conference_number, + Tox *tox, + uint32_t conference_number, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -159,3 +161,14 @@ void tox_events_handle_conference_peer_list_changed( tox_event_conference_peer_list_changed_set_conference_number(conference_peer_list_changed, conference_number); } + +void tox_events_handle_conference_peer_list_changed_dispatch(Tox *tox, const Tox_Event_Conference_Peer_List_Changed *event, void *user_data) +{ + if (tox->conference_peer_list_changed_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->conference_peer_list_changed_callback(tox, event->conference_number, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/conference_peer_name.c b/toxcore/events/conference_peer_name.c index c3f934cd..3880d7e3 100644 --- a/toxcore/events/conference_peer_name.c +++ b/toxcore/events/conference_peer_name.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -25,7 +26,7 @@ struct Tox_Event_Conference_Peer_Name { uint32_t conference_number; uint32_t peer_number; - uint8_t *name; + uint8_t *_Nullable name; uint32_t name_length; }; @@ -66,6 +67,12 @@ static bool tox_event_conference_peer_name_set_name(Tox_Event_Conference_Peer_Na return true; } + if (name_length == 0) { + conference_peer_name->name = nullptr; + conference_peer_name->name_length = 0; + return true; + } + uint8_t *name_copy = (uint8_t *)mem_balloc(mem, name_length); if (name_copy == nullptr) { @@ -147,7 +154,7 @@ Tox_Event_Conference_Peer_Name *tox_event_conference_peer_name_new(const Memory void tox_event_conference_peer_name_free(Tox_Event_Conference_Peer_Name *conference_peer_name, const Memory *mem) { if (conference_peer_name != nullptr) { - tox_event_conference_peer_name_destruct((Tox_Event_Conference_Peer_Name * _Nonnull)conference_peer_name, mem); + tox_event_conference_peer_name_destruct(conference_peer_name, mem); } mem_delete(mem, conference_peer_name); } @@ -208,7 +215,10 @@ static Tox_Event_Conference_Peer_Name *tox_event_conference_peer_name_alloc(Tox_ *****************************************************/ void tox_events_handle_conference_peer_name( - Tox *tox, uint32_t conference_number, uint32_t peer_number, const uint8_t *name, size_t length, + 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); @@ -220,5 +230,18 @@ void tox_events_handle_conference_peer_name( 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, state->mem, name, length); + if (!tox_event_conference_peer_name_set_name(conference_peer_name, state->mem, name, length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_conference_peer_name_dispatch(Tox *tox, const Tox_Event_Conference_Peer_Name *event, void *user_data) +{ + if (tox->conference_peer_name_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->conference_peer_name_callback(tox, event->conference_number, event->peer_number, event->name, event->name_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/conference_title.c b/toxcore/events/conference_title.c index 2196af42..797cba20 100644 --- a/toxcore/events/conference_title.c +++ b/toxcore/events/conference_title.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -25,7 +26,7 @@ struct Tox_Event_Conference_Title { uint32_t conference_number; uint32_t peer_number; - uint8_t *title; + uint8_t *_Nullable title; uint32_t title_length; }; @@ -66,6 +67,12 @@ static bool tox_event_conference_title_set_title(Tox_Event_Conference_Title *_No return true; } + if (title_length == 0) { + conference_title->title = nullptr; + conference_title->title_length = 0; + return true; + } + uint8_t *title_copy = (uint8_t *)mem_balloc(mem, title_length); if (title_copy == nullptr) { @@ -147,7 +154,7 @@ Tox_Event_Conference_Title *tox_event_conference_title_new(const Memory *mem) void tox_event_conference_title_free(Tox_Event_Conference_Title *conference_title, const Memory *mem) { if (conference_title != nullptr) { - tox_event_conference_title_destruct((Tox_Event_Conference_Title * _Nonnull)conference_title, mem); + tox_event_conference_title_destruct(conference_title, mem); } mem_delete(mem, conference_title); } @@ -208,7 +215,10 @@ static Tox_Event_Conference_Title *tox_event_conference_title_alloc(Tox_Events_S *****************************************************/ void tox_events_handle_conference_title( - Tox *tox, uint32_t conference_number, uint32_t peer_number, const uint8_t *title, size_t length, + 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); @@ -220,5 +230,18 @@ void tox_events_handle_conference_title( 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, state->mem, title, length); + if (!tox_event_conference_title_set_title(conference_title, state->mem, title, length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_conference_title_dispatch(Tox *tox, const Tox_Event_Conference_Title *event, void *user_data) +{ + if (tox->conference_title_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->conference_title_callback(tox, event->conference_number, event->peer_number, event->title, event->title_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/dht_nodes_response.c b/toxcore/events/dht_nodes_response.c index 6ddee228..d2dd5850 100644 --- a/toxcore/events/dht_nodes_response.c +++ b/toxcore/events/dht_nodes_response.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -24,7 +25,7 @@ struct Tox_Event_Dht_Nodes_Response { uint8_t public_key[TOX_PUBLIC_KEY_SIZE]; - uint8_t *ip; + char *_Nullable ip; uint32_t ip_length; uint16_t port; }; @@ -42,7 +43,7 @@ const uint8_t *tox_event_dht_nodes_response_get_public_key(const Tox_Event_Dht_N } static bool tox_event_dht_nodes_response_set_ip(Tox_Event_Dht_Nodes_Response *_Nonnull dht_nodes_response, - const Memory *_Nonnull mem, const uint8_t *_Nullable ip, uint32_t ip_length) + const Memory *_Nonnull mem, const char *_Nullable ip, uint32_t ip_length) { assert(dht_nodes_response != nullptr); if (dht_nodes_response->ip != nullptr) { @@ -56,7 +57,11 @@ static bool tox_event_dht_nodes_response_set_ip(Tox_Event_Dht_Nodes_Response *_N return true; } - uint8_t *ip_copy = (uint8_t *)mem_balloc(mem, ip_length + 1); + if (ip_length == UINT32_MAX) { + return false; + } + + char *ip_copy = (char *)mem_balloc(mem, ip_length + 1); if (ip_copy == nullptr) { return false; @@ -73,7 +78,7 @@ uint32_t tox_event_dht_nodes_response_get_ip_length(const Tox_Event_Dht_Nodes_Re assert(dht_nodes_response != nullptr); return dht_nodes_response->ip_length; } -const uint8_t *tox_event_dht_nodes_response_get_ip(const Tox_Event_Dht_Nodes_Response *dht_nodes_response) +const char *tox_event_dht_nodes_response_get_ip(const Tox_Event_Dht_Nodes_Response *dht_nodes_response) { assert(dht_nodes_response != nullptr); return dht_nodes_response->ip; @@ -108,7 +113,7 @@ bool tox_event_dht_nodes_response_pack( { return bin_pack_array(bp, 3) && bin_pack_bin(bp, event->public_key, TOX_PUBLIC_KEY_SIZE) - && bin_pack_bin(bp, event->ip, event->ip_length) + && bin_pack_str(bp, event->ip, event->ip_length) && bin_pack_u16(bp, event->port); } @@ -120,7 +125,7 @@ static bool tox_event_dht_nodes_response_unpack_into(Tox_Event_Dht_Nodes_Respons } return bin_unpack_bin_fixed(bu, event->public_key, TOX_PUBLIC_KEY_SIZE) - && bin_unpack_bin(bu, &event->ip, &event->ip_length) + && bin_unpack_str(bu, &event->ip, &event->ip_length) && bin_unpack_u16(bu, &event->port); } @@ -151,7 +156,7 @@ Tox_Event_Dht_Nodes_Response *tox_event_dht_nodes_response_new(const Memory *mem void tox_event_dht_nodes_response_free(Tox_Event_Dht_Nodes_Response *dht_nodes_response, const Memory *mem) { if (dht_nodes_response != nullptr) { - tox_event_dht_nodes_response_destruct((Tox_Event_Dht_Nodes_Response * _Nonnull)dht_nodes_response, mem); + tox_event_dht_nodes_response_destruct(dht_nodes_response, mem); } mem_delete(mem, dht_nodes_response); } @@ -212,7 +217,10 @@ static Tox_Event_Dht_Nodes_Response *tox_event_dht_nodes_response_alloc(Tox_Even *****************************************************/ void tox_events_handle_dht_nodes_response( - Tox *tox, const uint8_t *public_key, const char *ip, uint32_t ip_length, uint16_t port, + Tox *tox, + const uint8_t *public_key, + const char *ip, uint32_t ip_length, + uint16_t port, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -223,6 +231,19 @@ void tox_events_handle_dht_nodes_response( } tox_event_dht_nodes_response_set_public_key(dht_nodes_response, public_key); - tox_event_dht_nodes_response_set_ip(dht_nodes_response, state->mem, (const uint8_t *)ip, ip_length); + if (!tox_event_dht_nodes_response_set_ip(dht_nodes_response, state->mem, ip, ip_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } tox_event_dht_nodes_response_set_port(dht_nodes_response, port); } + +void tox_events_handle_dht_nodes_response_dispatch(Tox *tox, const Tox_Event_Dht_Nodes_Response *event, void *user_data) +{ + if (tox->dht_nodes_response_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->dht_nodes_response_callback(tox, event->public_key, (const char *)event->ip, event->ip_length, event->port, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/events_alloc.c b/toxcore/events/events_alloc.c index 18405ea9..a01ddb0c 100644 --- a/toxcore/events/events_alloc.c +++ b/toxcore/events/events_alloc.c @@ -61,7 +61,13 @@ bool tox_events_add(Tox_Events *events, const Tox_Event *event) } if (events->events_size == events->events_capacity) { - const uint32_t new_events_capacity = events->events_capacity * 2 + 1; + const uint64_t new_events_capacity_64 = (uint64_t)events->events_capacity * 2 + 1; + + if (new_events_capacity_64 > UINT32_MAX) { + return false; + } + + const uint32_t new_events_capacity = (uint32_t)new_events_capacity_64; Tox_Event *new_events = (Tox_Event *)mem_vrealloc( events->mem, events->events, new_events_capacity, sizeof(Tox_Event)); diff --git a/toxcore/events/events_alloc.h b/toxcore/events/events_alloc.h index b131002d..53efbe65 100644 --- a/toxcore/events/events_alloc.h +++ b/toxcore/events/events_alloc.h @@ -5,6 +5,9 @@ #ifndef C_TOXCORE_TOXCORE_EVENTS_EVENTS_ALLOC_H #define C_TOXCORE_TOXCORE_EVENTS_EVENTS_ALLOC_H +#include +#include + #include "../attributes.h" #include "../tox.h" #include "../tox_events.h" @@ -14,20 +17,20 @@ extern "C" { #endif -struct Tox_Memory; +struct Memory; struct Tox_Events { - Tox_Event *_Nonnull events; + Tox_Event *_Nullable events; uint32_t events_size; uint32_t events_capacity; - const struct Tox_Memory *_Nonnull mem; + const struct Memory *_Nonnull mem; }; typedef struct Tox_Events_State { Tox_Err_Events_Iterate error; - const struct Tox_Memory *_Nonnull mem; - Tox_Events *_Nonnull events; + const struct Memory *_Nonnull mem; + Tox_Events *_Nullable events; } Tox_Events_State; tox_conference_connected_cb tox_events_handle_conference_connected; diff --git a/toxcore/events/file_chunk_request.c b/toxcore/events/file_chunk_request.c index 96731dd1..4493a846 100644 --- a/toxcore/events/file_chunk_request.c +++ b/toxcore/events/file_chunk_request.c @@ -14,6 +14,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -133,7 +134,7 @@ Tox_Event_File_Chunk_Request *tox_event_file_chunk_request_new(const Memory *mem void tox_event_file_chunk_request_free(Tox_Event_File_Chunk_Request *file_chunk_request, const Memory *mem) { if (file_chunk_request != nullptr) { - tox_event_file_chunk_request_destruct((Tox_Event_File_Chunk_Request * _Nonnull)file_chunk_request, mem); + tox_event_file_chunk_request_destruct(file_chunk_request, mem); } mem_delete(mem, file_chunk_request); } @@ -194,7 +195,11 @@ static Tox_Event_File_Chunk_Request *tox_event_file_chunk_request_alloc(Tox_Even *****************************************************/ void tox_events_handle_file_chunk_request( - Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position, size_t length, + 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); @@ -209,3 +214,14 @@ void tox_events_handle_file_chunk_request( tox_event_file_chunk_request_set_position(file_chunk_request, position); tox_event_file_chunk_request_set_length(file_chunk_request, length); } + +void tox_events_handle_file_chunk_request_dispatch(Tox *tox, const Tox_Event_File_Chunk_Request *event, void *user_data) +{ + if (tox->file_chunk_request_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->file_chunk_request_callback(tox, event->friend_number, event->file_number, event->position, event->length, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/file_recv.c b/toxcore/events/file_recv.c index 0391ea3c..a51d3855 100644 --- a/toxcore/events/file_recv.c +++ b/toxcore/events/file_recv.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -27,7 +28,7 @@ struct Tox_Event_File_Recv { uint32_t file_number; uint32_t kind; uint64_t file_size; - uint8_t *filename; + uint8_t *_Nullable filename; uint32_t filename_length; }; @@ -90,6 +91,12 @@ static bool tox_event_file_recv_set_filename(Tox_Event_File_Recv *_Nonnull file_ return true; } + if (filename_length == 0) { + file_recv->filename = nullptr; + file_recv->filename_length = 0; + return true; + } + uint8_t *filename_copy = (uint8_t *)mem_balloc(mem, filename_length); if (filename_copy == nullptr) { @@ -175,7 +182,7 @@ Tox_Event_File_Recv *tox_event_file_recv_new(const Memory *mem) void tox_event_file_recv_free(Tox_Event_File_Recv *file_recv, const Memory *mem) { if (file_recv != nullptr) { - tox_event_file_recv_destruct((Tox_Event_File_Recv * _Nonnull)file_recv, mem); + tox_event_file_recv_destruct(file_recv, mem); } mem_delete(mem, file_recv); } @@ -236,7 +243,12 @@ static Tox_Event_File_Recv *tox_event_file_recv_alloc(Tox_Events_State *_Nonnull *****************************************************/ 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, + 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); @@ -250,5 +262,18 @@ void tox_events_handle_file_recv( 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, state->mem, filename, filename_length); + if (!tox_event_file_recv_set_filename(file_recv, state->mem, filename, filename_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_file_recv_dispatch(Tox *tox, const Tox_Event_File_Recv *event, void *user_data) +{ + if (tox->file_recv_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->file_recv_callback(tox, event->friend_number, event->file_number, event->kind, event->file_size, event->filename, event->filename_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/file_recv_chunk.c b/toxcore/events/file_recv_chunk.c index bfedf77b..b1c67e9f 100644 --- a/toxcore/events/file_recv_chunk.c +++ b/toxcore/events/file_recv_chunk.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -26,7 +27,7 @@ struct Tox_Event_File_Recv_Chunk { uint32_t friend_number; uint32_t file_number; uint64_t position; - uint8_t *data; + uint8_t *_Nullable data; uint32_t data_length; }; @@ -78,6 +79,12 @@ static bool tox_event_file_recv_chunk_set_data(Tox_Event_File_Recv_Chunk *_Nonnu return true; } + if (data_length == 0) { + file_recv_chunk->data = nullptr; + file_recv_chunk->data_length = 0; + return true; + } + uint8_t *data_copy = (uint8_t *)mem_balloc(mem, data_length); if (data_copy == nullptr) { @@ -161,7 +168,7 @@ Tox_Event_File_Recv_Chunk *tox_event_file_recv_chunk_new(const Memory *mem) void tox_event_file_recv_chunk_free(Tox_Event_File_Recv_Chunk *file_recv_chunk, const Memory *mem) { if (file_recv_chunk != nullptr) { - tox_event_file_recv_chunk_destruct((Tox_Event_File_Recv_Chunk * _Nonnull)file_recv_chunk, mem); + tox_event_file_recv_chunk_destruct(file_recv_chunk, mem); } mem_delete(mem, file_recv_chunk); } @@ -222,7 +229,11 @@ static Tox_Event_File_Recv_Chunk *tox_event_file_recv_chunk_alloc(Tox_Events_Sta *****************************************************/ 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, + 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); @@ -235,5 +246,18 @@ void tox_events_handle_file_recv_chunk( 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, state->mem, data, length); + if (!tox_event_file_recv_chunk_set_data(file_recv_chunk, state->mem, data, length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_file_recv_chunk_dispatch(Tox *tox, const Tox_Event_File_Recv_Chunk *event, void *user_data) +{ + if (tox->file_recv_chunk_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->file_recv_chunk_callback(tox, event->friend_number, event->file_number, event->position, event->data, event->data_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/file_recv_control.c b/toxcore/events/file_recv_control.c index 00c3c18e..5f5672ee 100644 --- a/toxcore/events/file_recv_control.c +++ b/toxcore/events/file_recv_control.c @@ -15,6 +15,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -121,7 +122,7 @@ Tox_Event_File_Recv_Control *tox_event_file_recv_control_new(const Memory *mem) void tox_event_file_recv_control_free(Tox_Event_File_Recv_Control *file_recv_control, const Memory *mem) { if (file_recv_control != nullptr) { - tox_event_file_recv_control_destruct((Tox_Event_File_Recv_Control * _Nonnull)file_recv_control, mem); + tox_event_file_recv_control_destruct(file_recv_control, mem); } mem_delete(mem, file_recv_control); } @@ -182,7 +183,10 @@ static Tox_Event_File_Recv_Control *tox_event_file_recv_control_alloc(Tox_Events *****************************************************/ void tox_events_handle_file_recv_control( - Tox *tox, uint32_t friend_number, uint32_t file_number, Tox_File_Control 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); @@ -196,3 +200,14 @@ void tox_events_handle_file_recv_control( tox_event_file_recv_control_set_file_number(file_recv_control, file_number); tox_event_file_recv_control_set_control(file_recv_control, control); } + +void tox_events_handle_file_recv_control_dispatch(Tox *tox, const Tox_Event_File_Recv_Control *event, void *user_data) +{ + if (tox->file_recv_control_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->file_recv_control_callback(tox, event->friend_number, event->file_number, event->control, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/friend_connection_status.c b/toxcore/events/friend_connection_status.c index eb3bff48..d67229d5 100644 --- a/toxcore/events/friend_connection_status.c +++ b/toxcore/events/friend_connection_status.c @@ -15,6 +15,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -107,7 +108,7 @@ Tox_Event_Friend_Connection_Status *tox_event_friend_connection_status_new(const void tox_event_friend_connection_status_free(Tox_Event_Friend_Connection_Status *friend_connection_status, const Memory *mem) { if (friend_connection_status != nullptr) { - tox_event_friend_connection_status_destruct((Tox_Event_Friend_Connection_Status * _Nonnull)friend_connection_status, mem); + tox_event_friend_connection_status_destruct(friend_connection_status, mem); } mem_delete(mem, friend_connection_status); } @@ -168,7 +169,9 @@ static Tox_Event_Friend_Connection_Status *tox_event_friend_connection_status_al *****************************************************/ void tox_events_handle_friend_connection_status( - Tox *tox, uint32_t friend_number, Tox_Connection connection_status, + Tox *tox, + uint32_t friend_number, + Tox_Connection connection_status, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -181,3 +184,14 @@ void tox_events_handle_friend_connection_status( 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); } + +void tox_events_handle_friend_connection_status_dispatch(Tox *tox, const Tox_Event_Friend_Connection_Status *event, void *user_data) +{ + if (tox->friend_connection_status_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->friend_connection_status_callback(tox, event->friend_number, event->connection_status, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/friend_lossless_packet.c b/toxcore/events/friend_lossless_packet.c index 0dba77b9..ed42e1ab 100644 --- a/toxcore/events/friend_lossless_packet.c +++ b/toxcore/events/friend_lossless_packet.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -24,7 +25,7 @@ struct Tox_Event_Friend_Lossless_Packet { uint32_t friend_number; - uint8_t *data; + uint8_t *_Nullable data; uint32_t data_length; }; @@ -54,6 +55,12 @@ static bool tox_event_friend_lossless_packet_set_data(Tox_Event_Friend_Lossless_ return true; } + if (data_length == 0) { + friend_lossless_packet->data = nullptr; + friend_lossless_packet->data_length = 0; + return true; + } + uint8_t *data_copy = (uint8_t *)mem_balloc(mem, data_length); if (data_copy == nullptr) { @@ -133,7 +140,7 @@ Tox_Event_Friend_Lossless_Packet *tox_event_friend_lossless_packet_new(const Mem void tox_event_friend_lossless_packet_free(Tox_Event_Friend_Lossless_Packet *friend_lossless_packet, const Memory *mem) { if (friend_lossless_packet != nullptr) { - tox_event_friend_lossless_packet_destruct((Tox_Event_Friend_Lossless_Packet * _Nonnull)friend_lossless_packet, mem); + tox_event_friend_lossless_packet_destruct(friend_lossless_packet, mem); } mem_delete(mem, friend_lossless_packet); } @@ -194,7 +201,9 @@ static Tox_Event_Friend_Lossless_Packet *tox_event_friend_lossless_packet_alloc( *****************************************************/ void tox_events_handle_friend_lossless_packet( - Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, + 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); @@ -205,5 +214,18 @@ void tox_events_handle_friend_lossless_packet( } tox_event_friend_lossless_packet_set_friend_number(friend_lossless_packet, friend_number); - tox_event_friend_lossless_packet_set_data(friend_lossless_packet, state->mem, data, length); + if (!tox_event_friend_lossless_packet_set_data(friend_lossless_packet, state->mem, data, length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_friend_lossless_packet_dispatch(Tox *tox, const Tox_Event_Friend_Lossless_Packet *event, void *user_data) +{ + if (event->data_length == 0 || tox->friend_lossless_packet_callback_per_pktid[event->data[0]] == nullptr) { + return; + } + + tox_unlock(tox); + tox->friend_lossless_packet_callback_per_pktid[event->data[0]](tox, event->friend_number, event->data, event->data_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/friend_lossy_packet.c b/toxcore/events/friend_lossy_packet.c index cb2bb0e7..a2ca953c 100644 --- a/toxcore/events/friend_lossy_packet.c +++ b/toxcore/events/friend_lossy_packet.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -24,7 +25,7 @@ struct Tox_Event_Friend_Lossy_Packet { uint32_t friend_number; - uint8_t *data; + uint8_t *_Nullable data; uint32_t data_length; }; @@ -54,6 +55,12 @@ static bool tox_event_friend_lossy_packet_set_data(Tox_Event_Friend_Lossy_Packet return true; } + if (data_length == 0) { + friend_lossy_packet->data = nullptr; + friend_lossy_packet->data_length = 0; + return true; + } + uint8_t *data_copy = (uint8_t *)mem_balloc(mem, data_length); if (data_copy == nullptr) { @@ -133,7 +140,7 @@ Tox_Event_Friend_Lossy_Packet *tox_event_friend_lossy_packet_new(const Memory *m void tox_event_friend_lossy_packet_free(Tox_Event_Friend_Lossy_Packet *friend_lossy_packet, const Memory *mem) { if (friend_lossy_packet != nullptr) { - tox_event_friend_lossy_packet_destruct((Tox_Event_Friend_Lossy_Packet * _Nonnull)friend_lossy_packet, mem); + tox_event_friend_lossy_packet_destruct(friend_lossy_packet, mem); } mem_delete(mem, friend_lossy_packet); } @@ -194,7 +201,9 @@ static Tox_Event_Friend_Lossy_Packet *tox_event_friend_lossy_packet_alloc(Tox_Ev *****************************************************/ void tox_events_handle_friend_lossy_packet( - Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, + 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); @@ -205,5 +214,18 @@ void tox_events_handle_friend_lossy_packet( } tox_event_friend_lossy_packet_set_friend_number(friend_lossy_packet, friend_number); - tox_event_friend_lossy_packet_set_data(friend_lossy_packet, state->mem, data, length); + if (!tox_event_friend_lossy_packet_set_data(friend_lossy_packet, state->mem, data, length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_friend_lossy_packet_dispatch(Tox *tox, const Tox_Event_Friend_Lossy_Packet *event, void *user_data) +{ + if (event->data_length == 0 || tox->friend_lossy_packet_callback_per_pktid[event->data[0]] == nullptr) { + return; + } + + tox_unlock(tox); + tox->friend_lossy_packet_callback_per_pktid[event->data[0]](tox, event->friend_number, event->data, event->data_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/friend_message.c b/toxcore/events/friend_message.c index b223a301..68f14cca 100644 --- a/toxcore/events/friend_message.c +++ b/toxcore/events/friend_message.c @@ -16,6 +16,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -27,7 +28,7 @@ struct Tox_Event_Friend_Message { uint32_t friend_number; Tox_Message_Type type; - uint8_t *message; + uint8_t *_Nullable message; uint32_t message_length; }; @@ -68,6 +69,12 @@ static bool tox_event_friend_message_set_message(Tox_Event_Friend_Message *_Nonn return true; } + if (message_length == 0) { + friend_message->message = nullptr; + friend_message->message_length = 0; + return true; + } + uint8_t *message_copy = (uint8_t *)mem_balloc(mem, message_length); if (message_copy == nullptr) { @@ -149,7 +156,7 @@ Tox_Event_Friend_Message *tox_event_friend_message_new(const Memory *mem) void tox_event_friend_message_free(Tox_Event_Friend_Message *friend_message, const Memory *mem) { if (friend_message != nullptr) { - tox_event_friend_message_destruct((Tox_Event_Friend_Message * _Nonnull)friend_message, mem); + tox_event_friend_message_destruct(friend_message, mem); } mem_delete(mem, friend_message); } @@ -210,7 +217,10 @@ static Tox_Event_Friend_Message *tox_event_friend_message_alloc(Tox_Events_State *****************************************************/ void tox_events_handle_friend_message( - Tox *tox, uint32_t friend_number, Tox_Message_Type type, const uint8_t *message, size_t length, + 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); @@ -222,5 +232,18 @@ void tox_events_handle_friend_message( 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, state->mem, message, length); + if (!tox_event_friend_message_set_message(friend_message, state->mem, message, length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_friend_message_dispatch(Tox *tox, const Tox_Event_Friend_Message *event, void *user_data) +{ + if (tox->friend_message_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->friend_message_callback(tox, event->friend_number, event->type, event->message, event->message_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/friend_name.c b/toxcore/events/friend_name.c index 5cc59051..2c77fc1e 100644 --- a/toxcore/events/friend_name.c +++ b/toxcore/events/friend_name.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -24,7 +25,7 @@ struct Tox_Event_Friend_Name { uint32_t friend_number; - uint8_t *name; + uint8_t *_Nullable name; uint32_t name_length; }; @@ -54,6 +55,12 @@ static bool tox_event_friend_name_set_name(Tox_Event_Friend_Name *_Nonnull frien return true; } + if (name_length == 0) { + friend_name->name = nullptr; + friend_name->name_length = 0; + return true; + } + uint8_t *name_copy = (uint8_t *)mem_balloc(mem, name_length); if (name_copy == nullptr) { @@ -133,7 +140,7 @@ Tox_Event_Friend_Name *tox_event_friend_name_new(const Memory *mem) void tox_event_friend_name_free(Tox_Event_Friend_Name *friend_name, const Memory *mem) { if (friend_name != nullptr) { - tox_event_friend_name_destruct((Tox_Event_Friend_Name * _Nonnull)friend_name, mem); + tox_event_friend_name_destruct(friend_name, mem); } mem_delete(mem, friend_name); } @@ -194,7 +201,9 @@ static Tox_Event_Friend_Name *tox_event_friend_name_alloc(Tox_Events_State *_Non *****************************************************/ void tox_events_handle_friend_name( - Tox *tox, uint32_t friend_number, const uint8_t *name, size_t length, + 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); @@ -205,5 +214,18 @@ void tox_events_handle_friend_name( } tox_event_friend_name_set_friend_number(friend_name, friend_number); - tox_event_friend_name_set_name(friend_name, state->mem, name, length); + if (!tox_event_friend_name_set_name(friend_name, state->mem, name, length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_friend_name_dispatch(Tox *tox, const Tox_Event_Friend_Name *event, void *user_data) +{ + if (tox->friend_name_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->friend_name_callback(tox, event->friend_number, event->name, event->name_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/friend_read_receipt.c b/toxcore/events/friend_read_receipt.c index f6bf6cf7..89c09573 100644 --- a/toxcore/events/friend_read_receipt.c +++ b/toxcore/events/friend_read_receipt.c @@ -14,6 +14,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -105,7 +106,7 @@ Tox_Event_Friend_Read_Receipt *tox_event_friend_read_receipt_new(const Memory *m void tox_event_friend_read_receipt_free(Tox_Event_Friend_Read_Receipt *friend_read_receipt, const Memory *mem) { if (friend_read_receipt != nullptr) { - tox_event_friend_read_receipt_destruct((Tox_Event_Friend_Read_Receipt * _Nonnull)friend_read_receipt, mem); + tox_event_friend_read_receipt_destruct(friend_read_receipt, mem); } mem_delete(mem, friend_read_receipt); } @@ -166,7 +167,9 @@ static Tox_Event_Friend_Read_Receipt *tox_event_friend_read_receipt_alloc(Tox_Ev *****************************************************/ void tox_events_handle_friend_read_receipt( - Tox *tox, uint32_t friend_number, uint32_t message_id, + Tox *tox, + uint32_t friend_number, + uint32_t message_id, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -179,3 +182,14 @@ void tox_events_handle_friend_read_receipt( 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); } + +void tox_events_handle_friend_read_receipt_dispatch(Tox *tox, const Tox_Event_Friend_Read_Receipt *event, void *user_data) +{ + if (tox->friend_read_receipt_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->friend_read_receipt_callback(tox, event->friend_number, event->message_id, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/friend_request.c b/toxcore/events/friend_request.c index 098470aa..5df42bd1 100644 --- a/toxcore/events/friend_request.c +++ b/toxcore/events/friend_request.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -24,7 +25,7 @@ struct Tox_Event_Friend_Request { uint8_t public_key[TOX_PUBLIC_KEY_SIZE]; - uint8_t *message; + uint8_t *_Nullable message; uint32_t message_length; }; @@ -55,6 +56,12 @@ static bool tox_event_friend_request_set_message(Tox_Event_Friend_Request *_Nonn return true; } + if (message_length == 0) { + friend_request->message = nullptr; + friend_request->message_length = 0; + return true; + } + uint8_t *message_copy = (uint8_t *)mem_balloc(mem, message_length); if (message_copy == nullptr) { @@ -136,7 +143,7 @@ Tox_Event_Friend_Request *tox_event_friend_request_new(const Memory *mem) void tox_event_friend_request_free(Tox_Event_Friend_Request *friend_request, const Memory *mem) { if (friend_request != nullptr) { - tox_event_friend_request_destruct((Tox_Event_Friend_Request * _Nonnull)friend_request, mem); + tox_event_friend_request_destruct(friend_request, mem); } mem_delete(mem, friend_request); } @@ -197,7 +204,9 @@ static Tox_Event_Friend_Request *tox_event_friend_request_alloc(Tox_Events_State *****************************************************/ void tox_events_handle_friend_request( - Tox *tox, const uint8_t *public_key, const uint8_t *message, size_t length, + 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); @@ -208,5 +217,18 @@ void tox_events_handle_friend_request( } tox_event_friend_request_set_public_key(friend_request, public_key); - tox_event_friend_request_set_message(friend_request, state->mem, message, length); + if (!tox_event_friend_request_set_message(friend_request, state->mem, message, length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_friend_request_dispatch(Tox *tox, const Tox_Event_Friend_Request *event, void *user_data) +{ + if (tox->friend_request_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->friend_request_callback(tox, event->public_key, event->message, event->message_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/friend_status.c b/toxcore/events/friend_status.c index ad3401dd..a60283fb 100644 --- a/toxcore/events/friend_status.c +++ b/toxcore/events/friend_status.c @@ -15,6 +15,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -107,7 +108,7 @@ Tox_Event_Friend_Status *tox_event_friend_status_new(const Memory *mem) void tox_event_friend_status_free(Tox_Event_Friend_Status *friend_status, const Memory *mem) { if (friend_status != nullptr) { - tox_event_friend_status_destruct((Tox_Event_Friend_Status * _Nonnull)friend_status, mem); + tox_event_friend_status_destruct(friend_status, mem); } mem_delete(mem, friend_status); } @@ -168,7 +169,9 @@ static Tox_Event_Friend_Status *tox_event_friend_status_alloc(Tox_Events_State * *****************************************************/ void tox_events_handle_friend_status( - Tox *tox, uint32_t friend_number, Tox_User_Status status, + Tox *tox, + uint32_t friend_number, + Tox_User_Status status, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -181,3 +184,14 @@ void tox_events_handle_friend_status( tox_event_friend_status_set_friend_number(friend_status, friend_number); tox_event_friend_status_set_status(friend_status, status); } + +void tox_events_handle_friend_status_dispatch(Tox *tox, const Tox_Event_Friend_Status *event, void *user_data) +{ + if (tox->friend_status_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->friend_status_callback(tox, event->friend_number, event->status, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/friend_status_message.c b/toxcore/events/friend_status_message.c index cf3e4a3e..aa590830 100644 --- a/toxcore/events/friend_status_message.c +++ b/toxcore/events/friend_status_message.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -24,7 +25,7 @@ struct Tox_Event_Friend_Status_Message { uint32_t friend_number; - uint8_t *message; + uint8_t *_Nullable message; uint32_t message_length; }; @@ -54,6 +55,12 @@ static bool tox_event_friend_status_message_set_message(Tox_Event_Friend_Status_ return true; } + if (message_length == 0) { + friend_status_message->message = nullptr; + friend_status_message->message_length = 0; + return true; + } + uint8_t *message_copy = (uint8_t *)mem_balloc(mem, message_length); if (message_copy == nullptr) { @@ -133,7 +140,7 @@ Tox_Event_Friend_Status_Message *tox_event_friend_status_message_new(const Memor void tox_event_friend_status_message_free(Tox_Event_Friend_Status_Message *friend_status_message, const Memory *mem) { if (friend_status_message != nullptr) { - tox_event_friend_status_message_destruct((Tox_Event_Friend_Status_Message * _Nonnull)friend_status_message, mem); + tox_event_friend_status_message_destruct(friend_status_message, mem); } mem_delete(mem, friend_status_message); } @@ -194,7 +201,9 @@ static Tox_Event_Friend_Status_Message *tox_event_friend_status_message_alloc(To *****************************************************/ void tox_events_handle_friend_status_message( - Tox *tox, uint32_t friend_number, const uint8_t *message, size_t length, + 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); @@ -205,5 +214,18 @@ void tox_events_handle_friend_status_message( } tox_event_friend_status_message_set_friend_number(friend_status_message, friend_number); - tox_event_friend_status_message_set_message(friend_status_message, state->mem, message, length); + if (!tox_event_friend_status_message_set_message(friend_status_message, state->mem, message, length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_friend_status_message_dispatch(Tox *tox, const Tox_Event_Friend_Status_Message *event, void *user_data) +{ + if (tox->friend_status_message_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->friend_status_message_callback(tox, event->friend_number, event->message, event->message_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/friend_typing.c b/toxcore/events/friend_typing.c index c21faab7..d64d624e 100644 --- a/toxcore/events/friend_typing.c +++ b/toxcore/events/friend_typing.c @@ -14,6 +14,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -105,7 +106,7 @@ Tox_Event_Friend_Typing *tox_event_friend_typing_new(const Memory *mem) void tox_event_friend_typing_free(Tox_Event_Friend_Typing *friend_typing, const Memory *mem) { if (friend_typing != nullptr) { - tox_event_friend_typing_destruct((Tox_Event_Friend_Typing * _Nonnull)friend_typing, mem); + tox_event_friend_typing_destruct(friend_typing, mem); } mem_delete(mem, friend_typing); } @@ -166,7 +167,9 @@ static Tox_Event_Friend_Typing *tox_event_friend_typing_alloc(Tox_Events_State * *****************************************************/ void tox_events_handle_friend_typing( - Tox *tox, uint32_t friend_number, bool typing, + Tox *tox, + uint32_t friend_number, + bool typing, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -179,3 +182,14 @@ void tox_events_handle_friend_typing( tox_event_friend_typing_set_friend_number(friend_typing, friend_number); tox_event_friend_typing_set_typing(friend_typing, typing); } + +void tox_events_handle_friend_typing_dispatch(Tox *tox, const Tox_Event_Friend_Typing *event, void *user_data) +{ + if (tox->friend_typing_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->friend_typing_callback(tox, event->friend_number, event->typing, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/group_custom_packet.c b/toxcore/events/group_custom_packet.c index 6d3cbae7..77e6723b 100644 --- a/toxcore/events/group_custom_packet.c +++ b/toxcore/events/group_custom_packet.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -25,7 +26,7 @@ struct Tox_Event_Group_Custom_Packet { uint32_t group_number; uint32_t peer_id; - uint8_t *data; + uint8_t *_Nullable data; uint32_t data_length; }; @@ -66,6 +67,12 @@ static bool tox_event_group_custom_packet_set_data(Tox_Event_Group_Custom_Packet return true; } + if (data_length == 0) { + group_custom_packet->data = nullptr; + group_custom_packet->data_length = 0; + return true; + } + uint8_t *data_copy = (uint8_t *)mem_balloc(mem, data_length); if (data_copy == nullptr) { @@ -147,7 +154,7 @@ Tox_Event_Group_Custom_Packet *tox_event_group_custom_packet_new(const Memory *m void tox_event_group_custom_packet_free(Tox_Event_Group_Custom_Packet *group_custom_packet, const Memory *mem) { if (group_custom_packet != nullptr) { - tox_event_group_custom_packet_destruct((Tox_Event_Group_Custom_Packet * _Nonnull)group_custom_packet, mem); + tox_event_group_custom_packet_destruct(group_custom_packet, mem); } mem_delete(mem, group_custom_packet); } @@ -208,7 +215,10 @@ static Tox_Event_Group_Custom_Packet *tox_event_group_custom_packet_alloc(Tox_Ev *****************************************************/ void tox_events_handle_group_custom_packet( - Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *data, size_t data_length, + Tox *tox, + uint32_t group_number, + uint32_t peer_id, + const uint8_t *data, size_t data_length, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -220,5 +230,18 @@ void tox_events_handle_group_custom_packet( tox_event_group_custom_packet_set_group_number(group_custom_packet, group_number); tox_event_group_custom_packet_set_peer_id(group_custom_packet, peer_id); - tox_event_group_custom_packet_set_data(group_custom_packet, state->mem, data, data_length); + if (!tox_event_group_custom_packet_set_data(group_custom_packet, state->mem, data, data_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_group_custom_packet_dispatch(Tox *tox, const Tox_Event_Group_Custom_Packet *event, void *user_data) +{ + if (tox->group_custom_packet_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_custom_packet_callback(tox, event->group_number, event->peer_id, event->data, event->data_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/group_custom_private_packet.c b/toxcore/events/group_custom_private_packet.c index fd415ac9..a7f8abd2 100644 --- a/toxcore/events/group_custom_private_packet.c +++ b/toxcore/events/group_custom_private_packet.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -25,7 +26,7 @@ struct Tox_Event_Group_Custom_Private_Packet { uint32_t group_number; uint32_t peer_id; - uint8_t *data; + uint8_t *_Nullable data; uint32_t data_length; }; @@ -66,6 +67,12 @@ static bool tox_event_group_custom_private_packet_set_data(Tox_Event_Group_Custo return true; } + if (data_length == 0) { + group_custom_private_packet->data = nullptr; + group_custom_private_packet->data_length = 0; + return true; + } + uint8_t *data_copy = (uint8_t *)mem_balloc(mem, data_length); if (data_copy == nullptr) { @@ -147,7 +154,7 @@ Tox_Event_Group_Custom_Private_Packet *tox_event_group_custom_private_packet_new void tox_event_group_custom_private_packet_free(Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet, const Memory *mem) { if (group_custom_private_packet != nullptr) { - tox_event_group_custom_private_packet_destruct((Tox_Event_Group_Custom_Private_Packet * _Nonnull)group_custom_private_packet, mem); + tox_event_group_custom_private_packet_destruct(group_custom_private_packet, mem); } mem_delete(mem, group_custom_private_packet); } @@ -208,7 +215,10 @@ static Tox_Event_Group_Custom_Private_Packet *tox_event_group_custom_private_pac *****************************************************/ void tox_events_handle_group_custom_private_packet( - Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *data, size_t data_length, + Tox *tox, + uint32_t group_number, + uint32_t peer_id, + const uint8_t *data, size_t data_length, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -220,5 +230,18 @@ void tox_events_handle_group_custom_private_packet( tox_event_group_custom_private_packet_set_group_number(group_custom_private_packet, group_number); tox_event_group_custom_private_packet_set_peer_id(group_custom_private_packet, peer_id); - tox_event_group_custom_private_packet_set_data(group_custom_private_packet, state->mem, data, data_length); + if (!tox_event_group_custom_private_packet_set_data(group_custom_private_packet, state->mem, data, data_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_group_custom_private_packet_dispatch(Tox *tox, const Tox_Event_Group_Custom_Private_Packet *event, void *user_data) +{ + if (tox->group_custom_private_packet_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_custom_private_packet_callback(tox, event->group_number, event->peer_id, event->data, event->data_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/group_invite.c b/toxcore/events/group_invite.c index 89e4d19d..f96501ad 100644 --- a/toxcore/events/group_invite.c +++ b/toxcore/events/group_invite.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -24,9 +25,9 @@ struct Tox_Event_Group_Invite { uint32_t friend_number; - uint8_t *invite_data; + uint8_t *_Nullable invite_data; uint32_t invite_data_length; - uint8_t *group_name; + uint8_t *_Nullable group_name; uint32_t group_name_length; }; @@ -56,6 +57,12 @@ static bool tox_event_group_invite_set_invite_data(Tox_Event_Group_Invite *_Nonn return true; } + if (invite_data_length == 0) { + group_invite->invite_data = nullptr; + group_invite->invite_data_length = 0; + return true; + } + uint8_t *invite_data_copy = (uint8_t *)mem_balloc(mem, invite_data_length); if (invite_data_copy == nullptr) { @@ -93,6 +100,12 @@ static bool tox_event_group_invite_set_group_name(Tox_Event_Group_Invite *_Nonnu return true; } + if (group_name_length == 0) { + group_invite->group_name = nullptr; + group_invite->group_name_length = 0; + return true; + } + uint8_t *group_name_copy = (uint8_t *)mem_balloc(mem, group_name_length); if (group_name_copy == nullptr) { @@ -175,7 +188,7 @@ Tox_Event_Group_Invite *tox_event_group_invite_new(const Memory *mem) void tox_event_group_invite_free(Tox_Event_Group_Invite *group_invite, const Memory *mem) { if (group_invite != nullptr) { - tox_event_group_invite_destruct((Tox_Event_Group_Invite * _Nonnull)group_invite, mem); + tox_event_group_invite_destruct(group_invite, mem); } mem_delete(mem, group_invite); } @@ -236,7 +249,10 @@ static Tox_Event_Group_Invite *tox_event_group_invite_alloc(Tox_Events_State *_N *****************************************************/ void tox_events_handle_group_invite( - Tox *tox, uint32_t friend_number, const uint8_t *invite_data, size_t invite_data_length, const uint8_t *group_name, size_t group_name_length, + Tox *tox, + uint32_t friend_number, + const uint8_t *invite_data, size_t invite_data_length, + const uint8_t *group_name, size_t group_name_length, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -247,6 +263,21 @@ void tox_events_handle_group_invite( } tox_event_group_invite_set_friend_number(group_invite, friend_number); - tox_event_group_invite_set_invite_data(group_invite, state->mem, invite_data, invite_data_length); - tox_event_group_invite_set_group_name(group_invite, state->mem, group_name, group_name_length); + if (!tox_event_group_invite_set_invite_data(group_invite, state->mem, invite_data, invite_data_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } + if (!tox_event_group_invite_set_group_name(group_invite, state->mem, group_name, group_name_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_group_invite_dispatch(Tox *tox, const Tox_Event_Group_Invite *event, void *user_data) +{ + if (tox->group_invite_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_invite_callback(tox, event->friend_number, event->invite_data, event->invite_data_length, event->group_name, event->group_name_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/group_join_fail.c b/toxcore/events/group_join_fail.c index 37294fa3..26aac3dd 100644 --- a/toxcore/events/group_join_fail.c +++ b/toxcore/events/group_join_fail.c @@ -15,6 +15,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -107,7 +108,7 @@ Tox_Event_Group_Join_Fail *tox_event_group_join_fail_new(const Memory *mem) void tox_event_group_join_fail_free(Tox_Event_Group_Join_Fail *group_join_fail, const Memory *mem) { if (group_join_fail != nullptr) { - tox_event_group_join_fail_destruct((Tox_Event_Group_Join_Fail * _Nonnull)group_join_fail, mem); + tox_event_group_join_fail_destruct(group_join_fail, mem); } mem_delete(mem, group_join_fail); } @@ -168,7 +169,9 @@ static Tox_Event_Group_Join_Fail *tox_event_group_join_fail_alloc(Tox_Events_Sta *****************************************************/ void tox_events_handle_group_join_fail( - Tox *tox, uint32_t group_number, Tox_Group_Join_Fail fail_type, + Tox *tox, + uint32_t group_number, + Tox_Group_Join_Fail fail_type, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -181,3 +184,14 @@ void tox_events_handle_group_join_fail( tox_event_group_join_fail_set_group_number(group_join_fail, group_number); tox_event_group_join_fail_set_fail_type(group_join_fail, fail_type); } + +void tox_events_handle_group_join_fail_dispatch(Tox *tox, const Tox_Event_Group_Join_Fail *event, void *user_data) +{ + if (tox->group_join_fail_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_join_fail_callback(tox, event->group_number, event->fail_type, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/group_message.c b/toxcore/events/group_message.c index d3d01fa7..cc6e6692 100644 --- a/toxcore/events/group_message.c +++ b/toxcore/events/group_message.c @@ -16,6 +16,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -28,7 +29,7 @@ struct Tox_Event_Group_Message { uint32_t group_number; uint32_t peer_id; Tox_Message_Type message_type; - uint8_t *message; + uint8_t *_Nullable message; uint32_t message_length; uint32_t message_id; }; @@ -81,6 +82,12 @@ static bool tox_event_group_message_set_message(Tox_Event_Group_Message *_Nonnul return true; } + if (message_length == 0) { + group_message->message = nullptr; + group_message->message_length = 0; + return true; + } + uint8_t *message_copy = (uint8_t *)mem_balloc(mem, message_length); if (message_copy == nullptr) { @@ -177,7 +184,7 @@ Tox_Event_Group_Message *tox_event_group_message_new(const Memory *mem) void tox_event_group_message_free(Tox_Event_Group_Message *group_message, const Memory *mem) { if (group_message != nullptr) { - tox_event_group_message_destruct((Tox_Event_Group_Message * _Nonnull)group_message, mem); + tox_event_group_message_destruct(group_message, mem); } mem_delete(mem, group_message); } @@ -238,7 +245,12 @@ static Tox_Event_Group_Message *tox_event_group_message_alloc(Tox_Events_State * *****************************************************/ void tox_events_handle_group_message( - Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Message_Type message_type, const uint8_t *message, size_t message_length, uint32_t message_id, + Tox *tox, + uint32_t group_number, + uint32_t peer_id, + Tox_Message_Type message_type, + const uint8_t *message, size_t message_length, + uint32_t message_id, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -251,6 +263,19 @@ void tox_events_handle_group_message( tox_event_group_message_set_group_number(group_message, group_number); tox_event_group_message_set_peer_id(group_message, peer_id); tox_event_group_message_set_message_type(group_message, message_type); - tox_event_group_message_set_message(group_message, state->mem, message, message_length); + if (!tox_event_group_message_set_message(group_message, state->mem, message, message_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } tox_event_group_message_set_message_id(group_message, message_id); } + +void tox_events_handle_group_message_dispatch(Tox *tox, const Tox_Event_Group_Message *event, void *user_data) +{ + if (tox->group_message_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_message_callback(tox, event->group_number, event->peer_id, event->message_type, event->message, event->message_length, event->message_id, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/group_moderation.c b/toxcore/events/group_moderation.c index df133e13..3532829c 100644 --- a/toxcore/events/group_moderation.c +++ b/toxcore/events/group_moderation.c @@ -15,6 +15,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -135,7 +136,7 @@ Tox_Event_Group_Moderation *tox_event_group_moderation_new(const Memory *mem) void tox_event_group_moderation_free(Tox_Event_Group_Moderation *group_moderation, const Memory *mem) { if (group_moderation != nullptr) { - tox_event_group_moderation_destruct((Tox_Event_Group_Moderation * _Nonnull)group_moderation, mem); + tox_event_group_moderation_destruct(group_moderation, mem); } mem_delete(mem, group_moderation); } @@ -196,7 +197,11 @@ static Tox_Event_Group_Moderation *tox_event_group_moderation_alloc(Tox_Events_S *****************************************************/ void tox_events_handle_group_moderation( - Tox *tox, uint32_t group_number, uint32_t source_peer_id, uint32_t target_peer_id, Tox_Group_Mod_Event mod_type, + Tox *tox, + uint32_t group_number, + uint32_t source_peer_id, + uint32_t target_peer_id, + Tox_Group_Mod_Event mod_type, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -211,3 +216,14 @@ void tox_events_handle_group_moderation( tox_event_group_moderation_set_target_peer_id(group_moderation, target_peer_id); tox_event_group_moderation_set_mod_type(group_moderation, mod_type); } + +void tox_events_handle_group_moderation_dispatch(Tox *tox, const Tox_Event_Group_Moderation *event, void *user_data) +{ + if (tox->group_moderation_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_moderation_callback(tox, event->group_number, event->source_peer_id, event->target_peer_id, event->mod_type, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/group_password.c b/toxcore/events/group_password.c index b1eac6ac..a1722ad8 100644 --- a/toxcore/events/group_password.c +++ b/toxcore/events/group_password.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -24,7 +25,7 @@ struct Tox_Event_Group_Password { uint32_t group_number; - uint8_t *password; + uint8_t *_Nullable password; uint32_t password_length; }; @@ -54,6 +55,12 @@ static bool tox_event_group_password_set_password(Tox_Event_Group_Password *_Non return true; } + if (password_length == 0) { + group_password->password = nullptr; + group_password->password_length = 0; + return true; + } + uint8_t *password_copy = (uint8_t *)mem_balloc(mem, password_length); if (password_copy == nullptr) { @@ -133,7 +140,7 @@ Tox_Event_Group_Password *tox_event_group_password_new(const Memory *mem) void tox_event_group_password_free(Tox_Event_Group_Password *group_password, const Memory *mem) { if (group_password != nullptr) { - tox_event_group_password_destruct((Tox_Event_Group_Password * _Nonnull)group_password, mem); + tox_event_group_password_destruct(group_password, mem); } mem_delete(mem, group_password); } @@ -194,7 +201,9 @@ static Tox_Event_Group_Password *tox_event_group_password_alloc(Tox_Events_State *****************************************************/ void tox_events_handle_group_password( - Tox *tox, uint32_t group_number, const uint8_t *password, size_t password_length, + Tox *tox, + uint32_t group_number, + const uint8_t *password, size_t password_length, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -205,5 +214,18 @@ void tox_events_handle_group_password( } tox_event_group_password_set_group_number(group_password, group_number); - tox_event_group_password_set_password(group_password, state->mem, password, password_length); + if (!tox_event_group_password_set_password(group_password, state->mem, password, password_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_group_password_dispatch(Tox *tox, const Tox_Event_Group_Password *event, void *user_data) +{ + if (tox->group_password_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_password_callback(tox, event->group_number, event->password, event->password_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/group_peer_exit.c b/toxcore/events/group_peer_exit.c index 457888e6..c574e88c 100644 --- a/toxcore/events/group_peer_exit.c +++ b/toxcore/events/group_peer_exit.c @@ -16,6 +16,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -28,9 +29,9 @@ struct Tox_Event_Group_Peer_Exit { uint32_t group_number; uint32_t peer_id; Tox_Group_Exit_Type exit_type; - uint8_t *name; + uint8_t *_Nullable name; uint32_t name_length; - uint8_t *part_message; + uint8_t *_Nullable part_message; uint32_t part_message_length; }; @@ -82,6 +83,12 @@ static bool tox_event_group_peer_exit_set_name(Tox_Event_Group_Peer_Exit *_Nonnu return true; } + if (name_length == 0) { + group_peer_exit->name = nullptr; + group_peer_exit->name_length = 0; + return true; + } + uint8_t *name_copy = (uint8_t *)mem_balloc(mem, name_length); if (name_copy == nullptr) { @@ -119,6 +126,12 @@ static bool tox_event_group_peer_exit_set_part_message(Tox_Event_Group_Peer_Exit return true; } + if (part_message_length == 0) { + group_peer_exit->part_message = nullptr; + group_peer_exit->part_message_length = 0; + return true; + } + uint8_t *part_message_copy = (uint8_t *)mem_balloc(mem, part_message_length); if (part_message_copy == nullptr) { @@ -205,7 +218,7 @@ Tox_Event_Group_Peer_Exit *tox_event_group_peer_exit_new(const Memory *mem) void tox_event_group_peer_exit_free(Tox_Event_Group_Peer_Exit *group_peer_exit, const Memory *mem) { if (group_peer_exit != nullptr) { - tox_event_group_peer_exit_destruct((Tox_Event_Group_Peer_Exit * _Nonnull)group_peer_exit, mem); + tox_event_group_peer_exit_destruct(group_peer_exit, mem); } mem_delete(mem, group_peer_exit); } @@ -266,7 +279,12 @@ static Tox_Event_Group_Peer_Exit *tox_event_group_peer_exit_alloc(Tox_Events_Sta *****************************************************/ void tox_events_handle_group_peer_exit( - Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Group_Exit_Type exit_type, const uint8_t *name, size_t name_length, const uint8_t *part_message, size_t part_message_length, + Tox *tox, + uint32_t group_number, + uint32_t peer_id, + Tox_Group_Exit_Type exit_type, + const uint8_t *name, size_t name_length, + const uint8_t *part_message, size_t part_message_length, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -279,6 +297,21 @@ void tox_events_handle_group_peer_exit( tox_event_group_peer_exit_set_group_number(group_peer_exit, group_number); tox_event_group_peer_exit_set_peer_id(group_peer_exit, peer_id); tox_event_group_peer_exit_set_exit_type(group_peer_exit, exit_type); - tox_event_group_peer_exit_set_name(group_peer_exit, state->mem, name, name_length); - tox_event_group_peer_exit_set_part_message(group_peer_exit, state->mem, part_message, part_message_length); + if (!tox_event_group_peer_exit_set_name(group_peer_exit, state->mem, name, name_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } + if (!tox_event_group_peer_exit_set_part_message(group_peer_exit, state->mem, part_message, part_message_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_group_peer_exit_dispatch(Tox *tox, const Tox_Event_Group_Peer_Exit *event, void *user_data) +{ + if (tox->group_peer_exit_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_peer_exit_callback(tox, event->group_number, event->peer_id, event->exit_type, event->name, event->name_length, event->part_message, event->part_message_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/group_peer_join.c b/toxcore/events/group_peer_join.c index a3a672a0..ae4e4782 100644 --- a/toxcore/events/group_peer_join.c +++ b/toxcore/events/group_peer_join.c @@ -14,6 +14,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -105,7 +106,7 @@ Tox_Event_Group_Peer_Join *tox_event_group_peer_join_new(const Memory *mem) void tox_event_group_peer_join_free(Tox_Event_Group_Peer_Join *group_peer_join, const Memory *mem) { if (group_peer_join != nullptr) { - tox_event_group_peer_join_destruct((Tox_Event_Group_Peer_Join * _Nonnull)group_peer_join, mem); + tox_event_group_peer_join_destruct(group_peer_join, mem); } mem_delete(mem, group_peer_join); } @@ -166,7 +167,9 @@ static Tox_Event_Group_Peer_Join *tox_event_group_peer_join_alloc(Tox_Events_Sta *****************************************************/ void tox_events_handle_group_peer_join( - Tox *tox, uint32_t group_number, uint32_t peer_id, + Tox *tox, + uint32_t group_number, + uint32_t peer_id, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -179,3 +182,14 @@ void tox_events_handle_group_peer_join( tox_event_group_peer_join_set_group_number(group_peer_join, group_number); tox_event_group_peer_join_set_peer_id(group_peer_join, peer_id); } + +void tox_events_handle_group_peer_join_dispatch(Tox *tox, const Tox_Event_Group_Peer_Join *event, void *user_data) +{ + if (tox->group_peer_join_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_peer_join_callback(tox, event->group_number, event->peer_id, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/group_peer_limit.c b/toxcore/events/group_peer_limit.c index d53c2c5a..a1078bc1 100644 --- a/toxcore/events/group_peer_limit.c +++ b/toxcore/events/group_peer_limit.c @@ -14,6 +14,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -105,7 +106,7 @@ Tox_Event_Group_Peer_Limit *tox_event_group_peer_limit_new(const Memory *mem) void tox_event_group_peer_limit_free(Tox_Event_Group_Peer_Limit *group_peer_limit, const Memory *mem) { if (group_peer_limit != nullptr) { - tox_event_group_peer_limit_destruct((Tox_Event_Group_Peer_Limit * _Nonnull)group_peer_limit, mem); + tox_event_group_peer_limit_destruct(group_peer_limit, mem); } mem_delete(mem, group_peer_limit); } @@ -166,7 +167,9 @@ static Tox_Event_Group_Peer_Limit *tox_event_group_peer_limit_alloc(Tox_Events_S *****************************************************/ void tox_events_handle_group_peer_limit( - Tox *tox, uint32_t group_number, uint32_t peer_limit, + Tox *tox, + uint32_t group_number, + uint32_t peer_limit, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -179,3 +182,14 @@ void tox_events_handle_group_peer_limit( tox_event_group_peer_limit_set_group_number(group_peer_limit, group_number); tox_event_group_peer_limit_set_peer_limit(group_peer_limit, peer_limit); } + +void tox_events_handle_group_peer_limit_dispatch(Tox *tox, const Tox_Event_Group_Peer_Limit *event, void *user_data) +{ + if (tox->group_peer_limit_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_peer_limit_callback(tox, event->group_number, event->peer_limit, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/group_peer_name.c b/toxcore/events/group_peer_name.c index 2870aa85..2c0a4894 100644 --- a/toxcore/events/group_peer_name.c +++ b/toxcore/events/group_peer_name.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -25,7 +26,7 @@ struct Tox_Event_Group_Peer_Name { uint32_t group_number; uint32_t peer_id; - uint8_t *name; + uint8_t *_Nullable name; uint32_t name_length; }; @@ -66,6 +67,12 @@ static bool tox_event_group_peer_name_set_name(Tox_Event_Group_Peer_Name *_Nonnu return true; } + if (name_length == 0) { + group_peer_name->name = nullptr; + group_peer_name->name_length = 0; + return true; + } + uint8_t *name_copy = (uint8_t *)mem_balloc(mem, name_length); if (name_copy == nullptr) { @@ -147,7 +154,7 @@ Tox_Event_Group_Peer_Name *tox_event_group_peer_name_new(const Memory *mem) void tox_event_group_peer_name_free(Tox_Event_Group_Peer_Name *group_peer_name, const Memory *mem) { if (group_peer_name != nullptr) { - tox_event_group_peer_name_destruct((Tox_Event_Group_Peer_Name * _Nonnull)group_peer_name, mem); + tox_event_group_peer_name_destruct(group_peer_name, mem); } mem_delete(mem, group_peer_name); } @@ -208,7 +215,10 @@ static Tox_Event_Group_Peer_Name *tox_event_group_peer_name_alloc(Tox_Events_Sta *****************************************************/ void tox_events_handle_group_peer_name( - Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *name, size_t name_length, + Tox *tox, + uint32_t group_number, + uint32_t peer_id, + const uint8_t *name, size_t name_length, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -220,5 +230,18 @@ void tox_events_handle_group_peer_name( tox_event_group_peer_name_set_group_number(group_peer_name, group_number); tox_event_group_peer_name_set_peer_id(group_peer_name, peer_id); - tox_event_group_peer_name_set_name(group_peer_name, state->mem, name, name_length); + if (!tox_event_group_peer_name_set_name(group_peer_name, state->mem, name, name_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_group_peer_name_dispatch(Tox *tox, const Tox_Event_Group_Peer_Name *event, void *user_data) +{ + if (tox->group_peer_name_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_peer_name_callback(tox, event->group_number, event->peer_id, event->name, event->name_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/group_peer_status.c b/toxcore/events/group_peer_status.c index 16e754cc..7e78eb6c 100644 --- a/toxcore/events/group_peer_status.c +++ b/toxcore/events/group_peer_status.c @@ -15,6 +15,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -121,7 +122,7 @@ Tox_Event_Group_Peer_Status *tox_event_group_peer_status_new(const Memory *mem) void tox_event_group_peer_status_free(Tox_Event_Group_Peer_Status *group_peer_status, const Memory *mem) { if (group_peer_status != nullptr) { - tox_event_group_peer_status_destruct((Tox_Event_Group_Peer_Status * _Nonnull)group_peer_status, mem); + tox_event_group_peer_status_destruct(group_peer_status, mem); } mem_delete(mem, group_peer_status); } @@ -182,7 +183,10 @@ static Tox_Event_Group_Peer_Status *tox_event_group_peer_status_alloc(Tox_Events *****************************************************/ void tox_events_handle_group_peer_status( - Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_User_Status status, + Tox *tox, + uint32_t group_number, + uint32_t peer_id, + Tox_User_Status status, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -196,3 +200,14 @@ void tox_events_handle_group_peer_status( tox_event_group_peer_status_set_peer_id(group_peer_status, peer_id); tox_event_group_peer_status_set_status(group_peer_status, status); } + +void tox_events_handle_group_peer_status_dispatch(Tox *tox, const Tox_Event_Group_Peer_Status *event, void *user_data) +{ + if (tox->group_peer_status_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_peer_status_callback(tox, event->group_number, event->peer_id, event->status, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/group_privacy_state.c b/toxcore/events/group_privacy_state.c index 27573add..84d60260 100644 --- a/toxcore/events/group_privacy_state.c +++ b/toxcore/events/group_privacy_state.c @@ -15,6 +15,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -107,7 +108,7 @@ Tox_Event_Group_Privacy_State *tox_event_group_privacy_state_new(const Memory *m void tox_event_group_privacy_state_free(Tox_Event_Group_Privacy_State *group_privacy_state, const Memory *mem) { if (group_privacy_state != nullptr) { - tox_event_group_privacy_state_destruct((Tox_Event_Group_Privacy_State * _Nonnull)group_privacy_state, mem); + tox_event_group_privacy_state_destruct(group_privacy_state, mem); } mem_delete(mem, group_privacy_state); } @@ -168,7 +169,9 @@ static Tox_Event_Group_Privacy_State *tox_event_group_privacy_state_alloc(Tox_Ev *****************************************************/ void tox_events_handle_group_privacy_state( - Tox *tox, uint32_t group_number, Tox_Group_Privacy_State privacy_state, + Tox *tox, + uint32_t group_number, + Tox_Group_Privacy_State privacy_state, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -181,3 +184,14 @@ void tox_events_handle_group_privacy_state( tox_event_group_privacy_state_set_group_number(group_privacy_state, group_number); tox_event_group_privacy_state_set_privacy_state(group_privacy_state, privacy_state); } + +void tox_events_handle_group_privacy_state_dispatch(Tox *tox, const Tox_Event_Group_Privacy_State *event, void *user_data) +{ + if (tox->group_privacy_state_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_privacy_state_callback(tox, event->group_number, event->privacy_state, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/group_private_message.c b/toxcore/events/group_private_message.c index 4386b3ac..dba5cd1f 100644 --- a/toxcore/events/group_private_message.c +++ b/toxcore/events/group_private_message.c @@ -16,6 +16,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -28,7 +29,7 @@ struct Tox_Event_Group_Private_Message { uint32_t group_number; uint32_t peer_id; Tox_Message_Type message_type; - uint8_t *message; + uint8_t *_Nullable message; uint32_t message_length; uint32_t message_id; }; @@ -81,6 +82,12 @@ static bool tox_event_group_private_message_set_message(Tox_Event_Group_Private_ return true; } + if (message_length == 0) { + group_private_message->message = nullptr; + group_private_message->message_length = 0; + return true; + } + uint8_t *message_copy = (uint8_t *)mem_balloc(mem, message_length); if (message_copy == nullptr) { @@ -177,7 +184,7 @@ Tox_Event_Group_Private_Message *tox_event_group_private_message_new(const Memor void tox_event_group_private_message_free(Tox_Event_Group_Private_Message *group_private_message, const Memory *mem) { if (group_private_message != nullptr) { - tox_event_group_private_message_destruct((Tox_Event_Group_Private_Message * _Nonnull)group_private_message, mem); + tox_event_group_private_message_destruct(group_private_message, mem); } mem_delete(mem, group_private_message); } @@ -238,7 +245,12 @@ static Tox_Event_Group_Private_Message *tox_event_group_private_message_alloc(To *****************************************************/ void tox_events_handle_group_private_message( - Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Message_Type message_type, const uint8_t *message, size_t message_length, uint32_t message_id, + Tox *tox, + uint32_t group_number, + uint32_t peer_id, + Tox_Message_Type message_type, + const uint8_t *message, size_t message_length, + uint32_t message_id, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -251,6 +263,19 @@ void tox_events_handle_group_private_message( tox_event_group_private_message_set_group_number(group_private_message, group_number); tox_event_group_private_message_set_peer_id(group_private_message, peer_id); tox_event_group_private_message_set_message_type(group_private_message, message_type); - tox_event_group_private_message_set_message(group_private_message, state->mem, message, message_length); + if (!tox_event_group_private_message_set_message(group_private_message, state->mem, message, message_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } tox_event_group_private_message_set_message_id(group_private_message, message_id); } + +void tox_events_handle_group_private_message_dispatch(Tox *tox, const Tox_Event_Group_Private_Message *event, void *user_data) +{ + if (tox->group_private_message_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_private_message_callback(tox, event->group_number, event->peer_id, event->message_type, event->message, event->message_length, event->message_id, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/group_self_join.c b/toxcore/events/group_self_join.c index 06e1d417..29cdf649 100644 --- a/toxcore/events/group_self_join.c +++ b/toxcore/events/group_self_join.c @@ -14,6 +14,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -86,7 +87,7 @@ Tox_Event_Group_Self_Join *tox_event_group_self_join_new(const Memory *mem) void tox_event_group_self_join_free(Tox_Event_Group_Self_Join *group_self_join, const Memory *mem) { if (group_self_join != nullptr) { - tox_event_group_self_join_destruct((Tox_Event_Group_Self_Join * _Nonnull)group_self_join, mem); + tox_event_group_self_join_destruct(group_self_join, mem); } mem_delete(mem, group_self_join); } @@ -147,7 +148,8 @@ static Tox_Event_Group_Self_Join *tox_event_group_self_join_alloc(Tox_Events_Sta *****************************************************/ void tox_events_handle_group_self_join( - Tox *tox, uint32_t group_number, + Tox *tox, + uint32_t group_number, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -159,3 +161,14 @@ void tox_events_handle_group_self_join( tox_event_group_self_join_set_group_number(group_self_join, group_number); } + +void tox_events_handle_group_self_join_dispatch(Tox *tox, const Tox_Event_Group_Self_Join *event, void *user_data) +{ + if (tox->group_self_join_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_self_join_callback(tox, event->group_number, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/group_topic.c b/toxcore/events/group_topic.c index 156b89c3..e6a57b19 100644 --- a/toxcore/events/group_topic.c +++ b/toxcore/events/group_topic.c @@ -15,6 +15,7 @@ #include "../tox.h" #include "../tox_event.h" #include "../tox_events.h" +#include "../tox_struct.h" /***************************************************** * @@ -25,7 +26,7 @@ struct Tox_Event_Group_Topic { uint32_t group_number; uint32_t peer_id; - uint8_t *topic; + uint8_t *_Nullable topic; uint32_t topic_length; }; @@ -66,6 +67,12 @@ static bool tox_event_group_topic_set_topic(Tox_Event_Group_Topic *_Nonnull grou return true; } + if (topic_length == 0) { + group_topic->topic = nullptr; + group_topic->topic_length = 0; + return true; + } + uint8_t *topic_copy = (uint8_t *)mem_balloc(mem, topic_length); if (topic_copy == nullptr) { @@ -147,7 +154,7 @@ Tox_Event_Group_Topic *tox_event_group_topic_new(const Memory *mem) void tox_event_group_topic_free(Tox_Event_Group_Topic *group_topic, const Memory *mem) { if (group_topic != nullptr) { - tox_event_group_topic_destruct((Tox_Event_Group_Topic * _Nonnull)group_topic, mem); + tox_event_group_topic_destruct(group_topic, mem); } mem_delete(mem, group_topic); } @@ -208,7 +215,10 @@ static Tox_Event_Group_Topic *tox_event_group_topic_alloc(Tox_Events_State *_Non *****************************************************/ void tox_events_handle_group_topic( - Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *topic, size_t topic_length, + Tox *tox, + uint32_t group_number, + uint32_t peer_id, + const uint8_t *topic, size_t topic_length, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -220,5 +230,18 @@ void tox_events_handle_group_topic( tox_event_group_topic_set_group_number(group_topic, group_number); tox_event_group_topic_set_peer_id(group_topic, peer_id); - tox_event_group_topic_set_topic(group_topic, state->mem, topic, topic_length); + if (!tox_event_group_topic_set_topic(group_topic, state->mem, topic, topic_length)) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } +} + +void tox_events_handle_group_topic_dispatch(Tox *tox, const Tox_Event_Group_Topic *event, void *user_data) +{ + if (tox->group_topic_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_topic_callback(tox, event->group_number, event->peer_id, event->topic, event->topic_length, user_data); + tox_lock(tox); } diff --git a/toxcore/events/group_topic_lock.c b/toxcore/events/group_topic_lock.c index cc1cf8ef..7f828307 100644 --- a/toxcore/events/group_topic_lock.c +++ b/toxcore/events/group_topic_lock.c @@ -15,6 +15,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -107,7 +108,7 @@ Tox_Event_Group_Topic_Lock *tox_event_group_topic_lock_new(const Memory *mem) void tox_event_group_topic_lock_free(Tox_Event_Group_Topic_Lock *group_topic_lock, const Memory *mem) { if (group_topic_lock != nullptr) { - tox_event_group_topic_lock_destruct((Tox_Event_Group_Topic_Lock * _Nonnull)group_topic_lock, mem); + tox_event_group_topic_lock_destruct(group_topic_lock, mem); } mem_delete(mem, group_topic_lock); } @@ -168,7 +169,9 @@ static Tox_Event_Group_Topic_Lock *tox_event_group_topic_lock_alloc(Tox_Events_S *****************************************************/ void tox_events_handle_group_topic_lock( - Tox *tox, uint32_t group_number, Tox_Group_Topic_Lock topic_lock, + Tox *tox, + uint32_t group_number, + Tox_Group_Topic_Lock topic_lock, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -181,3 +184,14 @@ void tox_events_handle_group_topic_lock( tox_event_group_topic_lock_set_group_number(group_topic_lock, group_number); tox_event_group_topic_lock_set_topic_lock(group_topic_lock, topic_lock); } + +void tox_events_handle_group_topic_lock_dispatch(Tox *tox, const Tox_Event_Group_Topic_Lock *event, void *user_data) +{ + if (tox->group_topic_lock_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_topic_lock_callback(tox, event->group_number, event->topic_lock, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/group_voice_state.c b/toxcore/events/group_voice_state.c index 1d0e51d5..aedf507f 100644 --- a/toxcore/events/group_voice_state.c +++ b/toxcore/events/group_voice_state.c @@ -15,6 +15,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -107,7 +108,7 @@ Tox_Event_Group_Voice_State *tox_event_group_voice_state_new(const Memory *mem) void tox_event_group_voice_state_free(Tox_Event_Group_Voice_State *group_voice_state, const Memory *mem) { if (group_voice_state != nullptr) { - tox_event_group_voice_state_destruct((Tox_Event_Group_Voice_State * _Nonnull)group_voice_state, mem); + tox_event_group_voice_state_destruct(group_voice_state, mem); } mem_delete(mem, group_voice_state); } @@ -168,7 +169,9 @@ static Tox_Event_Group_Voice_State *tox_event_group_voice_state_alloc(Tox_Events *****************************************************/ void tox_events_handle_group_voice_state( - Tox *tox, uint32_t group_number, Tox_Group_Voice_State voice_state, + Tox *tox, + uint32_t group_number, + Tox_Group_Voice_State voice_state, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -181,3 +184,14 @@ void tox_events_handle_group_voice_state( tox_event_group_voice_state_set_group_number(group_voice_state, group_number); tox_event_group_voice_state_set_voice_state(group_voice_state, voice_state); } + +void tox_events_handle_group_voice_state_dispatch(Tox *tox, const Tox_Event_Group_Voice_State *event, void *user_data) +{ + if (tox->group_voice_state_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->group_voice_state_callback(tox, event->group_number, event->voice_state, user_data); + tox_lock(tox); +} diff --git a/toxcore/events/self_connection_status.c b/toxcore/events/self_connection_status.c index 692c5938..40c89e2a 100644 --- a/toxcore/events/self_connection_status.c +++ b/toxcore/events/self_connection_status.c @@ -15,6 +15,7 @@ #include "../tox_event.h" #include "../tox_events.h" #include "../tox_pack.h" +#include "../tox_struct.h" #include "../tox_unpack.h" /***************************************************** @@ -88,7 +89,7 @@ Tox_Event_Self_Connection_Status *tox_event_self_connection_status_new(const Mem void tox_event_self_connection_status_free(Tox_Event_Self_Connection_Status *self_connection_status, const Memory *mem) { if (self_connection_status != nullptr) { - tox_event_self_connection_status_destruct((Tox_Event_Self_Connection_Status * _Nonnull)self_connection_status, mem); + tox_event_self_connection_status_destruct(self_connection_status, mem); } mem_delete(mem, self_connection_status); } @@ -149,7 +150,8 @@ static Tox_Event_Self_Connection_Status *tox_event_self_connection_status_alloc( *****************************************************/ void tox_events_handle_self_connection_status( - Tox *tox, Tox_Connection connection_status, + Tox *tox, + Tox_Connection connection_status, void *user_data) { Tox_Events_State *state = tox_events_alloc(user_data); @@ -161,3 +163,14 @@ void tox_events_handle_self_connection_status( tox_event_self_connection_status_set_connection_status(self_connection_status, connection_status); } + +void tox_events_handle_self_connection_status_dispatch(Tox *tox, const Tox_Event_Self_Connection_Status *event, void *user_data) +{ + if (tox->self_connection_status_callback == nullptr) { + return; + } + + tox_unlock(tox); + tox->self_connection_status_callback(tox, event->connection_status, user_data); + tox_lock(tox); +} diff --git a/toxcore/forwarding.h b/toxcore/forwarding.h index 8cef884a..3f8f4b0e 100644 --- a/toxcore/forwarding.h +++ b/toxcore/forwarding.h @@ -5,13 +5,18 @@ #ifndef C_TOXCORE_TOXCORE_FORWARDING_H #define C_TOXCORE_TOXCORE_FORWARDING_H +#include +#include + #include "DHT.h" #include "attributes.h" #include "crypto_core.h" #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "network.h" +#include "rng.h" #ifdef __cplusplus extern "C" { diff --git a/toxcore/forwarding_fuzz_test.cc b/toxcore/forwarding_fuzz_test.cc index e37b8aad..9bf481e8 100644 --- a/toxcore/forwarding_fuzz_test.cc +++ b/toxcore/forwarding_fuzz_test.cc @@ -15,37 +15,39 @@ using tox::test::configure_fuzz_memory_source; using tox::test::Fuzz_Data; using tox::test::SimulatedEnvironment; -constexpr uint16_t SIZE_IP_PORT = SIZE_IP6 + sizeof(uint16_t); +constexpr std::uint16_t SIZE_IP_PORT = SIZE_IP6 + sizeof(std::uint16_t); template using Ptr = std::unique_ptr; -std::optional> prepare(Fuzz_Data &input) +std::optional> prepare( + Fuzz_Data &input) { - CONSUME_OR_RETURN_VAL(const uint8_t *ipp_packed, input, SIZE_IP_PORT, std::nullopt); + CONSUME_OR_RETURN_VAL(const std::uint8_t *ipp_packed, input, SIZE_IP_PORT, std::nullopt); IP_Port ipp{}; unpack_ip_port(&ipp, ipp_packed, SIZE_IP6, true); - CONSUME_OR_RETURN_VAL(const uint8_t *forwarder_packed, input, SIZE_IP_PORT, std::nullopt); + CONSUME_OR_RETURN_VAL(const std::uint8_t *forwarder_packed, input, SIZE_IP_PORT, std::nullopt); IP_Port forwarder{}; unpack_ip_port(&forwarder, forwarder_packed, SIZE_IP6, true); // 2 bytes: size of the request - CONSUME_OR_RETURN_VAL(const uint8_t *data_size_bytes, input, sizeof(uint16_t), std::nullopt); - uint16_t data_size; - std::memcpy(&data_size, data_size_bytes, sizeof(uint16_t)); + CONSUME_OR_RETURN_VAL( + const std::uint8_t *data_size_bytes, input, sizeof(std::uint16_t), std::nullopt); + std::uint16_t data_size; + std::memcpy(&data_size, data_size_bytes, sizeof(std::uint16_t)); // data bytes (max 64K) - CONSUME_OR_RETURN_VAL(const uint8_t *data, input, data_size, std::nullopt); + CONSUME_OR_RETURN_VAL(const std::uint8_t *data, input, data_size, std::nullopt); return {{ipp, forwarder, data, data_size}}; } void TestSendForwardRequest(Fuzz_Data &input) { - CONSUME1_OR_RETURN(const uint16_t, chain_length, input); - const uint16_t chain_keys_size = chain_length * CRYPTO_PUBLIC_KEY_SIZE; - CONSUME_OR_RETURN(const uint8_t *chain_keys, input, chain_keys_size); + CONSUME1_OR_RETURN(const std::uint16_t, chain_length, input); + const std::uint16_t chain_keys_size = chain_length * CRYPTO_PUBLIC_KEY_SIZE; + CONSUME_OR_RETURN(const std::uint8_t *chain_keys, input, chain_keys_size); const auto prep = prepare(input); if (!prep.has_value()) { @@ -75,8 +77,8 @@ void TestSendForwardRequest(Fuzz_Data &input) void TestForwardReply(Fuzz_Data &input) { - CONSUME1_OR_RETURN(const uint16_t, sendback_length, input); - CONSUME_OR_RETURN(const uint8_t *sendback, input, sendback_length); + CONSUME1_OR_RETURN(const std::uint16_t, sendback_length, input); + CONSUME_OR_RETURN(const std::uint8_t *sendback, input, sendback_length); const auto prep = prepare(input); if (!prep.has_value()) { @@ -106,8 +108,8 @@ void TestForwardReply(Fuzz_Data &input) } // namespace -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size); +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { tox::test::fuzz_select_target(data, size); return 0; diff --git a/toxcore/friend_connection.h b/toxcore/friend_connection.h index 9298f251..da11cf80 100644 --- a/toxcore/friend_connection.h +++ b/toxcore/friend_connection.h @@ -9,6 +9,7 @@ #ifndef C_TOXCORE_TOXCORE_FRIEND_CONNECTION_H #define C_TOXCORE_TOXCORE_FRIEND_CONNECTION_H +#include #include #include "DHT.h" @@ -16,6 +17,7 @@ #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "net_crypto.h" #include "network.h" #include "onion_client.h" diff --git a/toxcore/friend_connection_test.cc b/toxcore/friend_connection_test.cc index fe1114b8..19d04fd8 100644 --- a/toxcore/friend_connection_test.cc +++ b/toxcore/friend_connection_test.cc @@ -13,6 +13,7 @@ #include "../testing/support/public/simulated_environment.hh" #include "DHT_test_util.hh" +#include "attributes.h" #include "crypto_core.h" #include "logger.h" #include "mono_time.h" @@ -20,6 +21,7 @@ #include "net_profile.h" #include "network.h" #include "onion_client.h" +#include "test_util.hh" namespace { @@ -30,7 +32,7 @@ using namespace tox::test; template class FriendConnTestNode { public: - FriendConnTestNode(SimulatedEnvironment &env, uint16_t port) + FriendConnTestNode(SimulatedEnvironment &env, std::uint16_t port) : dht_wrapper_(env, port) , net_profile_(netprof_new(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory), [mem = &dht_wrapper_.node().c_memory](Net_Profile *p) { netprof_kill(mem, p); }) @@ -40,8 +42,9 @@ public: { logger_callback_log( dht_wrapper_.logger(), - [](void *context, Logger_Level level, const char *file, uint32_t line, const char *func, - const char *message, void *userdata) { + [](void *_Nullable context, Logger_Level level, const char *_Nonnull file, + std::uint32_t line, const char *_Nonnull func, const char *_Nonnull message, + void *_Nullable userdata) { fprintf(stderr, "[%d] %s:%u: %s: %s\n", level, file, line, func, message); }, nullptr, nullptr); @@ -67,12 +70,18 @@ public: dht_wrapper_.get_dht(), net_crypto_.get(), dht_wrapper_.networking(), true)); } - Friend_Connections *get_friend_connections() { return friend_connections_.get(); } - Onion_Client *get_onion_client() { return onion_client_.get(); } - Net_Crypto *get_net_crypto() { return net_crypto_.get(); } - DHT *get_dht() { return dht_wrapper_.get_dht(); } - const uint8_t *dht_public_key() const { return dht_wrapper_.dht_public_key(); } - const uint8_t *real_public_key() const { return nc_get_self_public_key(net_crypto_.get()); } + Friend_Connections *_Nonnull get_friend_connections() + { + return REQUIRE_NOT_NULL(friend_connections_.get()); + } + Onion_Client *_Nonnull get_onion_client() { return REQUIRE_NOT_NULL(onion_client_.get()); } + Net_Crypto *_Nonnull get_net_crypto() { return REQUIRE_NOT_NULL(net_crypto_.get()); } + DHT *_Nonnull get_dht() { return dht_wrapper_.get_dht(); } + const std::uint8_t *dht_public_key() const { return dht_wrapper_.dht_public_key(); } + const std::uint8_t *real_public_key() const + { + return nc_get_self_public_key(net_crypto_.get()); + } const Random *get_random() { return &dht_wrapper_.node().c_random; } IP_Port get_ip_port() const { return dht_wrapper_.get_ip_port(); } @@ -114,8 +123,8 @@ TEST_F(FriendConnectionTest, CreationAndDestruction) TEST_F(FriendConnectionTest, AddKillConnection) { FriendConnNode alice(env, 33445); - uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t friend_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t friend_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(alice.get_random(), friend_pk, friend_sk); // Add Connection diff --git a/toxcore/friend_requests.h b/toxcore/friend_requests.h index b97640ac..34470c0b 100644 --- a/toxcore/friend_requests.h +++ b/toxcore/friend_requests.h @@ -10,6 +10,7 @@ #define C_TOXCORE_TOXCORE_FRIEND_REQUESTS_H #include +#include #include "attributes.h" #include "friend_connection.h" diff --git a/toxcore/group.h b/toxcore/group.h index a9ef0329..f5c3e0ca 100644 --- a/toxcore/group.h +++ b/toxcore/group.h @@ -9,6 +9,7 @@ #ifndef C_TOXCORE_TOXCORE_GROUP_H #define C_TOXCORE_TOXCORE_GROUP_H +#include #include #include diff --git a/toxcore/group_announce.h b/toxcore/group_announce.h index 29238bc6..94e420d3 100644 --- a/toxcore/group_announce.h +++ b/toxcore/group_announce.h @@ -18,6 +18,7 @@ #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "network.h" #ifdef __cplusplus diff --git a/toxcore/group_announce_fuzz_test.cc b/toxcore/group_announce_fuzz_test.cc index 7ed78227..1d087063 100644 --- a/toxcore/group_announce_fuzz_test.cc +++ b/toxcore/group_announce_fuzz_test.cc @@ -7,6 +7,7 @@ #include "../testing/support/public/fuzz_data.hh" #include "../testing/support/public/simulated_environment.hh" +#include "attributes.h" namespace { @@ -16,21 +17,21 @@ using tox::test::SimulatedEnvironment; void TestUnpackAnnouncesList(Fuzz_Data &input) { - CONSUME1_OR_RETURN(const uint8_t, max_count, input); + CONSUME1_OR_RETURN(const std::uint8_t, max_count, input); // Always allocate at least something to avoid passing nullptr to functions below. std::vector announces(max_count + 1); // TODO(iphydf): How do we know the packed size? - CONSUME1_OR_RETURN(const uint16_t, packed_size, input); + CONSUME1_OR_RETURN(const std::uint16_t, packed_size, input); SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Logger *logger = logger_new(&c_mem); if (gca_unpack_announces_list(logger, input.data(), input.size(), announces.data(), max_count) != -1) { // Always allocate at least something to avoid passing nullptr to functions below. - std::vector packed(packed_size + 1); - size_t processed; + std::vector packed(packed_size + 1); + std::size_t processed; gca_pack_announces_list( logger, packed.data(), packed_size, announces.data(), max_count, &processed); } @@ -42,14 +43,14 @@ void TestUnpackPublicAnnounce(Fuzz_Data &input) GC_Public_Announce public_announce; // TODO(iphydf): How do we know the packed size? - CONSUME1_OR_RETURN(const uint16_t, packed_size, input); + CONSUME1_OR_RETURN(const std::uint16_t, packed_size, input); SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Logger *logger = logger_new(&c_mem); if (gca_unpack_public_announce(logger, input.data(), input.size(), &public_announce) != -1) { // Always allocate at least something to avoid passing nullptr to functions below. - std::vector packed(packed_size + 1); + std::vector packed(packed_size + 1); gca_pack_public_announce(logger, packed.data(), packed_size, &public_announce); } logger_kill(logger); @@ -58,13 +59,13 @@ void TestUnpackPublicAnnounce(Fuzz_Data &input) void TestDoGca(Fuzz_Data &input) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); std::unique_ptr logger(logger_new(&c_mem), logger_kill); std::unique_ptr> mono_time( mono_time_new( &c_mem, - [](void *user_data) -> uint64_t { + [](void *_Nullable user_data) -> std::uint64_t { return static_cast(user_data)->current_time_ms(); }, &env.fake_clock()), @@ -76,12 +77,12 @@ void TestDoGca(Fuzz_Data &input) assert(gca != nullptr); while (!input.empty()) { - CONSUME1_OR_RETURN(const uint8_t, choice, input); + CONSUME1_OR_RETURN(const std::uint8_t, choice, input); switch (choice) { case 0: { // Add an announce. - CONSUME1_OR_RETURN(const uint16_t, length, input); - CONSUME_OR_RETURN(const uint8_t *data, input, length); + CONSUME1_OR_RETURN(const std::uint16_t, length, input); + CONSUME_OR_RETURN(const std::uint8_t *data, input, length); GC_Public_Announce public_announce; if (gca_unpack_public_announce(logger.get(), data, length, &public_announce) != -1) { gca_add_announce(&c_mem, mono_time.get(), gca.get(), &public_announce); @@ -90,7 +91,7 @@ void TestDoGca(Fuzz_Data &input) } case 1: { // Advance the time by a number of tox_iteration_intervals. - CONSUME1_OR_RETURN(const uint8_t, iterations, input); + CONSUME1_OR_RETURN(const std::uint8_t, iterations, input); env.fake_clock().advance(iterations * 20); // Do an iteration. do_gca(mono_time.get(), gca.get()); @@ -98,18 +99,18 @@ void TestDoGca(Fuzz_Data &input) } case 2: { // Get announces. - CONSUME1_OR_RETURN(const uint8_t, max_nodes, input); + CONSUME1_OR_RETURN(const std::uint8_t, max_nodes, input); // Always allocate at least something to avoid passing nullptr to functions below. std::vector gc_announces(max_nodes + 1); - CONSUME_OR_RETURN(const uint8_t *chat_id, input, CHAT_ID_SIZE); - CONSUME_OR_RETURN(const uint8_t *except_public_key, input, ENC_PUBLIC_KEY_SIZE); + CONSUME_OR_RETURN(const std::uint8_t *chat_id, input, CHAT_ID_SIZE); + CONSUME_OR_RETURN(const std::uint8_t *except_public_key, input, ENC_PUBLIC_KEY_SIZE); gca_get_announces( gca.get(), gc_announces.data(), max_nodes, chat_id, except_public_key); break; } case 3: { // Remove a chat. - CONSUME_OR_RETURN(const uint8_t *chat_id, input, CHAT_ID_SIZE); + CONSUME_OR_RETURN(const std::uint8_t *chat_id, input, CHAT_ID_SIZE); cleanup_gca(gca.get(), chat_id); break; } @@ -119,8 +120,8 @@ void TestDoGca(Fuzz_Data &input) } // namespace -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size); +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { tox::test::fuzz_select_target( data, size); diff --git a/toxcore/group_announce_test.cc b/toxcore/group_announce_test.cc index c9ce9498..3d881902 100644 --- a/toxcore/group_announce_test.cc +++ b/toxcore/group_announce_test.cc @@ -6,6 +6,7 @@ #include #include "DHT.h" +#include "attributes.h" #include "crypto_core.h" #include "logger.h" #include "mono_time.h" @@ -20,15 +21,15 @@ using tox::test::SimulatedEnvironment; struct Announces : ::testing::Test { protected: SimulatedEnvironment env; - Tox_Memory c_mem_; - Mono_Time *mono_time_ = nullptr; - GC_Announces_List *gca_ = nullptr; + Memory c_mem_; + Mono_Time *_Nullable mono_time_ = nullptr; + GC_Announces_List *_Nullable gca_ = nullptr; GC_Announce _ann1; GC_Announce _ann2; void SetUp() override { - c_mem_ = env.fake_memory().get_c_memory(); + c_mem_ = env.fake_memory().c_memory(); mono_time_ = mono_time_new(&c_mem_, nullptr, nullptr); ASSERT_NE(mono_time_, nullptr); setup_fake_clock(mono_time_, env.fake_clock()); @@ -43,7 +44,7 @@ protected: mono_time_free(&c_mem_, mono_time_); } - void advance_clock(uint64_t increment) + void advance_clock(std::uint64_t increment) { env.fake_clock().advance(increment); mono_time_update(mono_time_); @@ -105,7 +106,7 @@ TEST_F(Announces, AnnouncesGetAndCleanup) ASSERT_NE(gca_add_announce(&c_mem_, mono_time_, gca_, &ann2), nullptr); ASSERT_NE(gca_add_announce(&c_mem_, mono_time_, gca_, &ann2), nullptr); - uint8_t empty_pk[ENC_PUBLIC_KEY_SIZE] = {0}; + std::uint8_t empty_pk[ENC_PUBLIC_KEY_SIZE] = {0}; GC_Announce announces; ASSERT_EQ(gca_get_announces(gca_, &announces, 1, ann1.chat_public_key, empty_pk), 1); @@ -124,13 +125,13 @@ TEST_F(Announces, AnnouncesGetAndCleanup) struct AnnouncesPack : ::testing::Test { protected: SimulatedEnvironment env; - Tox_Memory c_mem_; + Memory c_mem_; std::vector announces_; Logger *logger_ = nullptr; void SetUp() override { - c_mem_ = env.fake_memory().get_c_memory(); + c_mem_ = env.fake_memory().c_memory(); logger_ = logger_new(&c_mem_); ASSERT_NE(logger_, nullptr); @@ -168,7 +169,7 @@ TEST_F(AnnouncesPack, PublicAnnounceCanBePackedAndUnpacked) ann.chat_public_key[0] = 0x88; ann.base_announce = announces_[0]; - std::vector packed(GCA_PUBLIC_ANNOUNCE_MAX_SIZE); + std::vector packed(GCA_PUBLIC_ANNOUNCE_MAX_SIZE); const int packed_size = gca_pack_public_announce(logger_, packed.data(), packed.size(), &ann); EXPECT_GT(packed_size, 0); @@ -182,7 +183,7 @@ TEST_F(AnnouncesPack, UnpackEmptyPublicAnnounce) { #ifndef __clang__ GC_Public_Announce ann{}; - std::vector packed(GCA_PUBLIC_ANNOUNCE_MAX_SIZE); + std::vector packed(GCA_PUBLIC_ANNOUNCE_MAX_SIZE); EXPECT_EQ(gca_unpack_public_announce(logger_, nullptr, 0, &ann), -1); EXPECT_EQ(gca_unpack_public_announce(logger_, packed.data(), packed.size(), nullptr), -1); @@ -193,7 +194,7 @@ TEST_F(AnnouncesPack, PackEmptyPublicAnnounce) { #ifndef __clang__ GC_Public_Announce ann{}; - std::vector packed(GCA_PUBLIC_ANNOUNCE_MAX_SIZE); + std::vector packed(GCA_PUBLIC_ANNOUNCE_MAX_SIZE); EXPECT_EQ(gca_pack_public_announce(logger_, packed.data(), packed.size(), nullptr), -1); EXPECT_EQ(gca_pack_public_announce(logger_, nullptr, 0, &ann), -1); #endif @@ -202,13 +203,13 @@ TEST_F(AnnouncesPack, PackEmptyPublicAnnounce) TEST_F(AnnouncesPack, PublicAnnouncePackNull) { GC_Public_Announce ann{}; - std::vector packed(GCA_PUBLIC_ANNOUNCE_MAX_SIZE); + std::vector packed(GCA_PUBLIC_ANNOUNCE_MAX_SIZE); EXPECT_EQ(gca_pack_public_announce(logger_, packed.data(), packed.size(), &ann), -1); ann.chat_public_key[0] = 0x88; ann.base_announce = announces_[0]; - std::vector packedTooSmall(GCA_PUBLIC_ANNOUNCE_MAX_SIZE - 1); + std::vector packedTooSmall(GCA_PUBLIC_ANNOUNCE_MAX_SIZE - 1); EXPECT_EQ( gca_pack_public_announce(logger_, packedTooSmall.data(), packedTooSmall.size(), &ann), -1); @@ -235,9 +236,9 @@ TEST_F(AnnouncesPack, AnnouncesValidationCheck) TEST_F(AnnouncesPack, UnpackIncompleteAnnouncesList) { - const uint8_t data[] = {0x00, 0x24, 0x3d, 0x00, 0x3d, 0xff, 0xff, 0x5b, 0x04, 0x20, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}; + const std::uint8_t data[] = {0x00, 0x24, 0x3d, 0x00, 0x3d, 0xff, 0xff, 0x5b, 0x04, 0x20, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}; GC_Announce announce; EXPECT_EQ(gca_unpack_announces_list(logger_, data, sizeof(data), &announce, 1), -1); @@ -249,10 +250,10 @@ TEST_F(AnnouncesPack, UnpackIncompleteAnnouncesList) TEST_F(AnnouncesPack, PackedAnnouncesListCanBeUnpacked) { - const uint16_t size = gca_pack_announces_list_size(announces_.size()); - std::vector packed(size); + const std::uint16_t size = gca_pack_announces_list_size(announces_.size()); + std::vector packed(size); - size_t processed = 0; + std::size_t processed = 0; EXPECT_GT(gca_pack_announces_list(logger_, packed.data(), packed.size(), announces_.data(), announces_.size(), &processed), @@ -269,7 +270,7 @@ TEST_F(AnnouncesPack, PackedAnnouncesListCanBeUnpacked) TEST_F(AnnouncesPack, PackingEmptyAnnounceFails) { GC_Announce announce{}; // all zeroes - std::vector packed(gca_pack_announces_list_size(1)); + std::vector packed(gca_pack_announces_list_size(1)); EXPECT_EQ( gca_pack_announces_list(logger_, packed.data(), packed.size(), &announce, 1, nullptr), -1); #ifndef __clang__ @@ -282,7 +283,7 @@ TEST_F(AnnouncesPack, PackingEmptyAnnounceFails) TEST_F(AnnouncesPack, PackAnnounceNull) { #ifndef __clang__ - std::vector data(GCA_ANNOUNCE_MAX_SIZE); + std::vector data(GCA_ANNOUNCE_MAX_SIZE); GC_Announce announce; ASSERT_EQ(gca_pack_announce(logger_, nullptr, 0, &announce), -1); ASSERT_EQ(gca_pack_announce(logger_, data.data(), data.size(), nullptr), -1); diff --git a/toxcore/group_chats.c b/toxcore/group_chats.c index 323b0ffa..5ca65a5d 100644 --- a/toxcore/group_chats.c +++ b/toxcore/group_chats.c @@ -1967,7 +1967,7 @@ static bool sync_response_send_state(GC_Chat *_Nonnull chat, GC_Connection *_Non * Return -3 if we fail to send a response packet. * Return -4 if `peer_number` does not designate a valid peer. */ -static int handle_gc_sync_request(GC_Chat *_Nonnull chat, uint32_t peer_number, const uint8_t *_Nonnull data, uint16_t length) +static int handle_gc_sync_request(GC_Chat *_Nonnull chat, uint32_t peer_number, const uint8_t *_Nullable data, uint16_t length) { GC_Connection *gconn = get_gc_connection(chat, peer_number); @@ -2067,7 +2067,7 @@ static bool send_gc_tcp_relays(const GC_Chat *_Nonnull chat, GC_Connection *_Non * Return -1 if packet has invalid size. * Return -2 if packet contains invalid data. */ -static int handle_gc_tcp_relays(GC_Chat *_Nonnull chat, GC_Connection *_Nonnull gconn, const uint8_t *_Nonnull data, uint16_t length) +static int handle_gc_tcp_relays(GC_Chat *_Nonnull chat, GC_Connection *_Nonnull gconn, const uint8_t *_Nullable data, uint16_t length) { if (length == 0) { return -1; @@ -2203,7 +2203,7 @@ static bool send_gc_invite_response_reject(const GC_Chat *_Nonnull chat, GC_Conn * Return -3 if we fail to send an invite response. * Return -4 if peer_number does not designate a valid peer. */ -static int handle_gc_invite_request(GC_Chat *_Nonnull chat, uint32_t peer_number, const uint8_t *_Nonnull data, uint16_t length) +static int handle_gc_invite_request(GC_Chat *_Nonnull chat, uint32_t peer_number, const uint8_t *_Nullable data, uint16_t length) { if (chat->shared_state.version == 0) { // we aren't synced yet; ignore request return 0; @@ -2682,7 +2682,7 @@ static bool send_gc_peer_exchange(const GC_Chat *chat, GC_Connection *gconn) * Return -8 if memory allocation fails. */ static int handle_gc_peer_info_response(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, uint32_t peer_number, - const uint8_t *_Nonnull data, uint16_t length, void *_Nullable userdata) + const uint8_t *_Nullable data, uint16_t length, void *_Nullable userdata) { if (length < PACKED_GC_PEER_SIZE) { return -1; @@ -2918,7 +2918,7 @@ static int handle_gc_shared_state_error(GC_Chat *_Nonnull chat, GC_Connection *_ * Return 0 if packet is successfully handled. * Return -1 if packet is invalid and this is not successfully handled. */ -static int handle_gc_shared_state(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, GC_Connection *_Nonnull gconn, const uint8_t *_Nonnull data, +static int handle_gc_shared_state(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, GC_Connection *_Nonnull gconn, const uint8_t *_Nullable data, uint16_t length, void *_Nullable userdata) { if (length < GC_SHARED_STATE_ENC_PACKET_SIZE) { @@ -3015,7 +3015,7 @@ static int validate_unpack_mod_list(GC_Chat *_Nonnull chat, const uint8_t *_Nonn * Return -1 if packet has invalid size. * Return -2 if packet contained invalid data or validation failed. */ -static int handle_gc_mod_list(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, const uint8_t *_Nonnull data, uint16_t length, void *_Nullable userdata) +static int handle_gc_mod_list(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, const uint8_t *_Nullable data, uint16_t length, void *_Nullable userdata) { if (length < sizeof(uint16_t)) { return -1; @@ -3094,7 +3094,7 @@ static int handle_gc_sanctions_list_error(GC_Chat *_Nonnull chat) * Return -1 if we failed to gracefully handle a sanctions list error. * Return -2 if packet has invalid size. */ -static int handle_gc_sanctions_list(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, const uint8_t *_Nonnull data, uint16_t length, +static int handle_gc_sanctions_list(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, const uint8_t *_Nullable data, uint16_t length, void *_Nullable userdata) { if (length < sizeof(uint16_t)) { @@ -3869,7 +3869,7 @@ static bool handle_gc_topic_validate(const GC_Chat *_Nonnull chat, const GC_Peer * Return 0 if packet is correctly handled. * Return -1 if packet has invalid size. */ -static int handle_gc_topic(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, const GC_Peer *_Nonnull peer, const uint8_t *_Nonnull data, +static int handle_gc_topic(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, const GC_Peer *_Nonnull peer, const uint8_t *_Nullable data, uint16_t length, void *_Nullable userdata) { if (length < SIGNATURE_SIZE + GC_MIN_PACKED_TOPIC_INFO_SIZE) { @@ -3931,7 +3931,7 @@ static int handle_gc_topic(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, * Return -2 if we fail to create a new session keypair. * Return -3 if response packet fails to send. */ -static int handle_gc_key_exchange(const GC_Chat *_Nonnull chat, GC_Connection *_Nonnull gconn, const uint8_t *_Nonnull data, uint16_t length) +static int handle_gc_key_exchange(const GC_Chat *_Nonnull chat, GC_Connection *_Nonnull gconn, const uint8_t *_Nullable data, uint16_t length) { if (length < 1 + ENC_PUBLIC_KEY_SIZE) { return -1; @@ -4995,7 +4995,7 @@ int gc_send_custom_private_packet(const GC_Chat *chat, bool lossless, GC_Peer_Id * @retval -1 if packet has invalid size. */ static int handle_gc_custom_private_packet(const GC_Session *_Nonnull c, const GC_Chat *_Nonnull chat, const GC_Peer *_Nonnull peer, - const uint8_t *_Nonnull data, uint16_t length, bool lossless, void *_Nullable userdata) + const uint8_t *_Nullable data, uint16_t length, bool lossless, void *_Nullable userdata) { if (!custom_gc_packet_length_is_valid(length, lossless)) { return -1; @@ -5038,7 +5038,7 @@ int gc_send_custom_packet(const GC_Chat *chat, bool lossless, const uint8_t *dat * Return 0 if packet is handled correctly. * Return -1 if packet has invalid size. */ -static int handle_gc_custom_packet(const GC_Session *_Nonnull c, const GC_Chat *_Nonnull chat, const GC_Peer *_Nonnull peer, const uint8_t *_Nonnull data, +static int handle_gc_custom_packet(const GC_Session *_Nonnull c, const GC_Chat *_Nonnull chat, const GC_Peer *_Nonnull peer, const uint8_t *_Nullable data, uint16_t length, bool lossless, void *_Nullable userdata) { if (!custom_gc_packet_length_is_valid(length, lossless)) { @@ -5301,7 +5301,7 @@ int gc_set_ignore(const GC_Chat *chat, GC_Peer_Id peer_id, bool ignore) * Returns 0 if packet is handled correctly. * Returns -1 on failure. */ -static int handle_gc_broadcast(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, uint32_t peer_number, const uint8_t *_Nonnull data, +static int handle_gc_broadcast(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, uint32_t peer_number, const uint8_t *_Nullable data, uint16_t length, void *_Nullable userdata) { if (length < GC_BROADCAST_ENC_HEADER_SIZE) { @@ -5483,7 +5483,7 @@ static int make_gc_handshake_packet(const GC_Chat *_Nonnull chat, const GC_Conne uint8_t data[GC_MIN_HS_PACKET_PAYLOAD_SIZE + sizeof(Node_format)]; - uint16_t length = sizeof(uint8_t); + uint32_t length = sizeof(uint8_t); data[0] = handshake_type; memcpy(data + length, gconn->session_public_key, ENC_PUBLIC_KEY_SIZE); @@ -5505,7 +5505,7 @@ static int make_gc_handshake_packet(const GC_Chat *_Nonnull chat, const GC_Conne const int enc_len = wrap_group_handshake_packet( chat->log, chat->mem, chat->rng, chat->self_public_key.enc, chat->self_secret_key.enc, - gconn->addr.public_key.enc, packet, (uint16_t)packet_size, data, length); + gconn->addr.public_key.enc, packet, (uint16_t)packet_size, data, (uint16_t)length); if (enc_len != GC_MIN_ENCRYPTED_HS_PAYLOAD_SIZE + nodes_size) { LOGGER_WARNING(chat->log, "Failed to wrap handshake packet: %d", enc_len); @@ -7766,6 +7766,11 @@ int gc_invite_friend(const GC_Session *c, GC_Chat *chat, int32_t friend_number, mem_delete(chat->mem, packet); + if (chat->saved_invites[chat->saved_invites_index] != -1) { + LOGGER_WARNING(chat->log, "Overwriting pending group invite for friend %d due to buffer overflow", + chat->saved_invites[chat->saved_invites_index]); + } + chat->saved_invites[chat->saved_invites_index] = friend_number; chat->saved_invites_index = (chat->saved_invites_index + 1) % MAX_GC_SAVED_INVITES; @@ -7992,6 +7997,8 @@ bool handle_gc_invite_accepted_packet(const GC_Session *c, int friend_number, co const int peer_number = peer_add(chat, nullptr, invite_chat_pk); if (!friend_was_invited(m, chat, friend_number)) { + LOGGER_WARNING(chat->log, "Group invite acceptance from friend %d rejected (not invited or invite buffer overflowed)", + friend_number); return false; } diff --git a/toxcore/group_chats.h b/toxcore/group_chats.h index 83692934..c83e892a 100644 --- a/toxcore/group_chats.h +++ b/toxcore/group_chats.h @@ -680,7 +680,7 @@ bool gc_send_message_ack(const GC_Chat *_Nonnull chat, GC_Connection *_Nonnull g * * @retval true if packet is successfully handled. */ -bool handle_gc_lossless_helper(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, uint32_t peer_number, const uint8_t *_Nonnull data, +bool handle_gc_lossless_helper(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, uint32_t peer_number, const uint8_t *_Nullable data, uint16_t length, uint8_t packet_type, void *_Nullable userdata); /** @brief Handles an invite accept packet. * diff --git a/toxcore/group_common.h b/toxcore/group_common.h index cee126df..2b5bb3bf 100644 --- a/toxcore/group_common.h +++ b/toxcore/group_common.h @@ -20,6 +20,7 @@ #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "net_profile.h" #include "network.h" diff --git a/toxcore/group_connection.c b/toxcore/group_connection.c index be94784e..658e5258 100644 --- a/toxcore/group_connection.c +++ b/toxcore/group_connection.c @@ -512,14 +512,7 @@ static bool process_recv_array_entry(const GC_Session *_Nonnull c, GC_Chat *_Non uint8_t sender_pk[ENC_PUBLIC_KEY_SIZE]; memcpy(sender_pk, get_enc_key(&gconn->addr.public_key), ENC_PUBLIC_KEY_SIZE); - const uint8_t *data = array_entry->data; - // TODO(iphydf): This is ugly. We should probably change all the handlers to accept nullable. - const uint8_t empty_data[1] = { 0 }; - if (data == nullptr) { - data = empty_data; - } - - const bool ret = handle_gc_lossless_helper(c, chat, peer_number, data, array_entry->data_length, + const bool ret = handle_gc_lossless_helper(c, chat, peer_number, array_entry->data, array_entry->data_length, array_entry->packet_type, userdata); /* peer number can change from peer add operations in packet handlers */ diff --git a/toxcore/group_connection.h b/toxcore/group_connection.h index 010b51e5..05b36867 100644 --- a/toxcore/group_connection.h +++ b/toxcore/group_connection.h @@ -10,6 +10,9 @@ #ifndef C_TOXCORE_TOXCORE_GROUP_CONNECTION_H #define C_TOXCORE_TOXCORE_GROUP_CONNECTION_H +#include +#include + #include "DHT.h" #include "TCP_connection.h" #include "attributes.h" @@ -18,6 +21,7 @@ #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "network.h" /* Max number of TCP relays we share with a peer on handshake */ diff --git a/toxcore/group_moderation_fuzz_test.cc b/toxcore/group_moderation_fuzz_test.cc index e0943534..6d568fb9 100644 --- a/toxcore/group_moderation_fuzz_test.cc +++ b/toxcore/group_moderation_fuzz_test.cc @@ -10,9 +10,9 @@ using tox::test::SimulatedEnvironment; void TestModListUnpack(Fuzz_Data &input) { - CONSUME1_OR_RETURN(const uint16_t, num_mods, input); + CONSUME1_OR_RETURN(const std::uint16_t, num_mods, input); SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; mod_list_unpack(&mods, input.data(), input.size(), num_mods); mod_list_cleanup(&mods); @@ -22,21 +22,21 @@ void TestSanctionsListUnpack(Fuzz_Data &input) { Mod_Sanction sanctions[10]; Mod_Sanction_Creds creds; - uint16_t processed_data_len; + std::uint16_t processed_data_len; sanctions_list_unpack(sanctions, &creds, 10, input.data(), input.size(), &processed_data_len); } void TestSanctionCredsUnpack(Fuzz_Data &input) { - CONSUME_OR_RETURN(const uint8_t *data, input, MOD_SANCTIONS_CREDS_SIZE); + CONSUME_OR_RETURN(const std::uint8_t *data, input, MOD_SANCTIONS_CREDS_SIZE); Mod_Sanction_Creds creds; sanctions_creds_unpack(&creds, data); } } // namespace -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size); +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { tox::test::fuzz_select_target(data, size); diff --git a/toxcore/group_moderation_test.cc b/toxcore/group_moderation_test.cc index 633b7085..e78e4753 100644 --- a/toxcore/group_moderation_test.cc +++ b/toxcore/group_moderation_test.cc @@ -7,6 +7,8 @@ #include #include +#include +#include #include #include "DHT.h" @@ -18,16 +20,16 @@ namespace { using tox::test::SimulatedEnvironment; -using ModerationHash = std::array; +using ModerationHash = std::array; TEST(ModList, PackedSizeOfEmptyModListIsZero) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; EXPECT_EQ(mod_list_packed_size(&mods), 0); - uint8_t byte = 1; + std::uint8_t byte = 1; mod_list_pack(&mods, &byte); EXPECT_EQ(byte, 1); } @@ -35,19 +37,19 @@ TEST(ModList, PackedSizeOfEmptyModListIsZero) TEST(ModList, UnpackingZeroSizeArrayIsNoop) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; - const uint8_t byte = 1; + const std::uint8_t byte = 1; EXPECT_EQ(mod_list_unpack(&mods, &byte, 0, 0), 0); } TEST(ModList, AddRemoveMultipleMods) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; - uint8_t sig_pk1[32] = {1}; - uint8_t sig_pk2[32] = {2}; + std::uint8_t sig_pk1[32] = {1}; + std::uint8_t sig_pk2[32] = {2}; EXPECT_TRUE(mod_list_add_entry(&mods, sig_pk1)); EXPECT_TRUE(mod_list_add_entry(&mods, sig_pk2)); EXPECT_TRUE(mod_list_remove_entry(&mods, sig_pk1)); @@ -56,13 +58,13 @@ TEST(ModList, AddRemoveMultipleMods) TEST(ModList, PackingAndUnpackingList) { - using ModListEntry = std::array; + using ModListEntry = std::array; SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; EXPECT_TRUE(mod_list_add_entry(&mods, ModListEntry{}.data())); - std::vector packed(mod_list_packed_size(&mods)); + std::vector packed(mod_list_packed_size(&mods)); mod_list_pack(&mods, packed.data()); EXPECT_TRUE(mod_list_remove_entry(&mods, ModListEntry{}.data())); @@ -74,13 +76,13 @@ TEST(ModList, PackingAndUnpackingList) TEST(ModList, UnpackingTooManyModsFails) { - using ModListEntry = std::array; + using ModListEntry = std::array; SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; EXPECT_TRUE(mod_list_add_entry(&mods, ModListEntry{}.data())); - std::vector packed(mod_list_packed_size(&mods)); + std::vector packed(mod_list_packed_size(&mods)); mod_list_pack(&mods, packed.data()); Moderation mods2{&c_mem}; @@ -90,10 +92,10 @@ TEST(ModList, UnpackingTooManyModsFails) TEST(ModList, UnpackingFromEmptyBufferFails) { - std::vector packed(1); + std::vector packed(1); SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; EXPECT_EQ(mod_list_unpack(&mods, packed.data(), 0, 1), -1); } @@ -101,8 +103,8 @@ TEST(ModList, UnpackingFromEmptyBufferFails) TEST(ModList, HashOfEmptyModListZeroesOutBuffer) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); - auto c_rng = env.fake_random().get_c_random(); + auto c_mem = env.fake_memory().c_memory(); + auto c_rng = env.fake_random().c_random(); Moderation mods{&c_mem}; @@ -116,7 +118,7 @@ TEST(ModList, HashOfEmptyModListZeroesOutBuffer) TEST(ModList, RemoveIndexFromEmptyModListFails) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; EXPECT_FALSE(mod_list_remove_index(&mods, 0)); EXPECT_FALSE(mod_list_remove_index(&mods, UINT16_MAX)); @@ -125,18 +127,18 @@ TEST(ModList, RemoveIndexFromEmptyModListFails) TEST(ModList, RemoveEntryFromEmptyModListFails) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; - uint8_t sig_pk[32] = {0}; + std::uint8_t sig_pk[32] = {0}; EXPECT_FALSE(mod_list_remove_entry(&mods, sig_pk)); } TEST(ModList, ModListRemoveIndex) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; - uint8_t sig_pk[32] = {1}; + std::uint8_t sig_pk[32] = {1}; EXPECT_TRUE(mod_list_add_entry(&mods, sig_pk)); EXPECT_TRUE(mod_list_remove_index(&mods, 0)); } @@ -144,7 +146,7 @@ TEST(ModList, ModListRemoveIndex) TEST(ModList, CleanupOnEmptyModsIsNoop) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; mod_list_cleanup(&mods); } @@ -152,18 +154,18 @@ TEST(ModList, CleanupOnEmptyModsIsNoop) TEST(ModList, EmptyModListCannotVerifyAnySigPk) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; - uint8_t sig_pk[32] = {1}; + std::uint8_t sig_pk[32] = {1}; EXPECT_FALSE(mod_list_verify_sig_pk(&mods, sig_pk)); } TEST(ModList, ModListAddVerifyRemoveSigPK) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods{&c_mem}; - uint8_t sig_pk[32] = {1}; + std::uint8_t sig_pk[32] = {1}; EXPECT_TRUE(mod_list_add_entry(&mods, sig_pk)); EXPECT_TRUE(mod_list_verify_sig_pk(&mods, sig_pk)); EXPECT_TRUE(mod_list_remove_entry(&mods, sig_pk)); @@ -173,10 +175,10 @@ TEST(ModList, ModListAddVerifyRemoveSigPK) TEST(ModList, ModListHashCheck) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mods1{&c_mem}; - uint8_t sig_pk1[32] = {1}; - std::array hash1; + std::uint8_t sig_pk1[32] = {1}; + std::array hash1; EXPECT_TRUE(mod_list_add_entry(&mods1, sig_pk1)); EXPECT_TRUE(mod_list_make_hash(&mods1, hash1.data())); @@ -186,20 +188,20 @@ TEST(ModList, ModListHashCheck) TEST(SanctionsList, PackingIntoUndersizedBufferFails) { Mod_Sanction sanctions[1] = {}; - std::array packed; + std::array packed; EXPECT_EQ(sanctions_list_pack(packed.data(), packed.size(), sanctions, 1, nullptr), -1); - uint16_t length = sanctions_list_packed_size(1) - 1; - std::vector packed2(length); + std::uint16_t length = sanctions_list_packed_size(1) - 1; + std::vector packed2(length); EXPECT_EQ(sanctions_list_pack(packed2.data(), packed2.size(), sanctions, 1, nullptr), -1); } TEST(SanctionsList, PackUnpackSanctionsCreds) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Moderation mod{&c_mem}; - std::array packed; + std::array packed; EXPECT_EQ(sanctions_creds_pack(&mod.sanctions_creds, packed.data()), MOD_SANCTIONS_CREDS_SIZE); EXPECT_EQ( sanctions_creds_unpack(&mod.sanctions_creds, packed.data()), MOD_SANCTIONS_CREDS_SIZE); @@ -208,8 +210,8 @@ TEST(SanctionsList, PackUnpackSanctionsCreds) struct SanctionsListMod : ::testing::Test { protected: SimulatedEnvironment env; - Tox_Memory c_mem_; - Tox_Random c_rng_; + Memory c_mem_; + Random c_rng_; Extended_Public_Key pk; Extended_Secret_Key sk; @@ -217,12 +219,12 @@ protected: Moderation mod; Mod_Sanction sanctions[2] = {}; - const uint8_t sanctioned_pk1[32] = {1}; - const uint8_t sanctioned_pk2[32] = {2}; + const std::uint8_t sanctioned_pk1[32] = {1}; + const std::uint8_t sanctioned_pk2[32] = {2}; SanctionsListMod() - : c_mem_(env.fake_memory().get_c_memory()) - , c_rng_(env.fake_random().get_c_random()) + : c_mem_(env.fake_memory().c_memory()) + , c_rng_(env.fake_random().c_random()) , mod{&c_mem_} { } @@ -234,8 +236,8 @@ protected: mod.log = log; - memcpy(mod.self_public_sig_key, get_sig_pk(&pk), SIG_PUBLIC_KEY_SIZE); - memcpy(mod.self_secret_sig_key, get_sig_sk(&sk), SIG_SECRET_KEY_SIZE); + std::memcpy(mod.self_public_sig_key, get_sig_pk(&pk), SIG_PUBLIC_KEY_SIZE); + std::memcpy(mod.self_secret_sig_key, get_sig_sk(&sk), SIG_SECRET_KEY_SIZE); ASSERT_TRUE(mod_list_add_entry(&mod, get_sig_pk(&pk))); @@ -267,13 +269,13 @@ protected: // TODO(JFreegman): Split this up into smaller subtests TEST_F(SanctionsListMod, PackUnpackSanction) { - std::vector packed(sanctions_list_packed_size(2)); + std::vector packed(sanctions_list_packed_size(2)); EXPECT_EQ( sanctions_list_pack(packed.data(), packed.size(), sanctions, 2, nullptr), packed.size()); Mod_Sanction unpacked_sanctions[2] = {}; - uint16_t processed_data_len = 0; + std::uint16_t processed_data_len = 0; EXPECT_EQ(sanctions_list_unpack(unpacked_sanctions, &mod.sanctions_creds, 2, packed.data(), packed.size(), &processed_data_len), diff --git a/toxcore/group_onion_announce.h b/toxcore/group_onion_announce.h index 4ebd702c..67a5fbf9 100644 --- a/toxcore/group_onion_announce.h +++ b/toxcore/group_onion_announce.h @@ -6,6 +6,8 @@ #ifndef C_TOXCORE_TOXCORE_GROUP_ONION_ANNOUNCE_H #define C_TOXCORE_TOXCORE_GROUP_ONION_ANNOUNCE_H +#include + #include "attributes.h" #include "crypto_core.h" #include "group_announce.h" diff --git a/toxcore/group_pack.h b/toxcore/group_pack.h index cc0e5b8c..d9f53299 100644 --- a/toxcore/group_pack.h +++ b/toxcore/group_pack.h @@ -11,6 +11,7 @@ #define C_TOXCORE_TOXCORE_GROUP_PACK_H #include +#include #include "attributes.h" #include "bin_pack.h" diff --git a/toxcore/list_test.cc b/toxcore/list_test.cc index 7cf3efe1..a873f1c4 100644 --- a/toxcore/list_test.cc +++ b/toxcore/list_test.cc @@ -2,6 +2,9 @@ #include +#include +#include + #include "mem.h" #include "os_memory.h" @@ -11,7 +14,7 @@ TEST(List, CreateAndDestroyWithNonZeroSize) { const Memory *mem = os_memory(); BS_List list; - bs_list_init(&list, mem, sizeof(int), 10, memcmp); + bs_list_init(&list, mem, sizeof(int), 10, std::memcmp); bs_list_free(&list); } @@ -19,7 +22,7 @@ TEST(List, CreateAndDestroyWithZeroSize) { const Memory *mem = os_memory(); BS_List list; - bs_list_init(&list, mem, sizeof(int), 0, memcmp); + bs_list_init(&list, mem, sizeof(int), 0, std::memcmp); bs_list_free(&list); } @@ -27,8 +30,8 @@ TEST(List, DeleteFromEmptyList) { const Memory *mem = os_memory(); BS_List list; - bs_list_init(&list, mem, sizeof(int), 0, memcmp); - const uint8_t data[sizeof(int)] = {0}; + bs_list_init(&list, mem, sizeof(int), 0, std::memcmp); + const std::uint8_t data[sizeof(int)] = {0}; bs_list_remove(&list, data, 0); bs_list_free(&list); } diff --git a/toxcore/logger.c b/toxcore/logger.c index 89d4af6b..5e73de40 100644 --- a/toxcore/logger.c +++ b/toxcore/logger.c @@ -74,7 +74,9 @@ void logger_write(const Logger *log, Logger_Level level, const char *file, uint3 // The full path may contain PII of the person compiling toxcore (their // username and directory layout). const char *filename = strrchr(file, '/'); - file = filename != nullptr ? filename + 1 : file; + if (filename != nullptr) { + file = &filename[1]; + } #if defined(_WIN32) || defined(__CYGWIN__) // On Windows, the path separator *may* be a backslash, so we look for that // one too. diff --git a/toxcore/mem.c b/toxcore/mem.c index ce33fb4a..dcd5ff52 100644 --- a/toxcore/mem.c +++ b/toxcore/mem.c @@ -8,23 +8,22 @@ #include #include "ccompat.h" -#include "tox_memory.h" void *mem_balloc(const Memory *mem, uint32_t size) { - void *const ptr = tox_memory_malloc(mem, size); + void *const ptr = mem->funcs->malloc_callback(mem->user_data, size); return ptr; } void *mem_brealloc(const Memory *mem, void *ptr, uint32_t size) { - void *const new_ptr = tox_memory_realloc(mem, ptr, size); + void *const new_ptr = mem->funcs->realloc_callback(mem->user_data, ptr, size); return new_ptr; } void *mem_alloc(const Memory *mem, uint32_t size) { - void *const ptr = tox_memory_malloc(mem, size); + void *const ptr = mem_balloc(mem, size); if (ptr != nullptr) { memset(ptr, 0, size); } @@ -39,10 +38,7 @@ void *mem_valloc(const Memory *mem, uint32_t nmemb, uint32_t size) return nullptr; } - void *const ptr = tox_memory_malloc(mem, bytes); - if (ptr != nullptr) { - memset(ptr, 0, bytes); - } + void *const ptr = mem_alloc(mem, bytes); return ptr; } @@ -54,11 +50,12 @@ void *mem_vrealloc(const Memory *mem, void *ptr, uint32_t nmemb, uint32_t size) return nullptr; } - void *const new_ptr = tox_memory_realloc(mem, ptr, bytes); + void *const new_ptr = mem_brealloc(mem, ptr, bytes); return new_ptr; } void mem_delete(const Memory *mem, void *ptr) { - tox_memory_dealloc(mem, ptr); + mem->funcs->dealloc_callback(mem->user_data, ptr); } + diff --git a/toxcore/mem.h b/toxcore/mem.h index 6784525b..dafe8f3f 100644 --- a/toxcore/mem.h +++ b/toxcore/mem.h @@ -12,13 +12,31 @@ #include // uint*_t #include "attributes.h" -#include "tox_memory.h" #ifdef __cplusplus extern "C" { #endif -typedef Tox_Memory Memory; +/** @brief Allocate a byte array, similar to malloc. */ +typedef void *_Nullable memory_malloc_cb(void *_Nullable self, uint32_t size); +/** @brief Reallocate a byte array, similar to realloc. */ +typedef void *_Nullable memory_realloc_cb(void *_Nullable self, void *_Nullable ptr, uint32_t size); +/** + * @brief Deallocate a byte or object array, similar to free. + */ +typedef void memory_dealloc_cb(void *_Nullable self, void *_Nullable ptr); + +/** @brief Functions wrapping standard C memory allocation functions. */ +typedef struct Memory_Funcs { + memory_malloc_cb *_Nonnull malloc_callback; + memory_realloc_cb *_Nonnull realloc_callback; + memory_dealloc_cb *_Nonnull dealloc_callback; +} Memory_Funcs; + +typedef struct Memory { + const Memory_Funcs *_Nonnull funcs; + void *_Nullable user_data; +} Memory; /** * @brief Allocate an array of a given size for built-in types. @@ -37,9 +55,12 @@ void *_Nullable mem_balloc(const Memory *_Nonnull mem, uint32_t size); void *_Nullable mem_brealloc(const Memory *_Nonnull mem, void *_Nullable ptr, uint32_t size); /** - * @brief Allocate a single object. + * @brief Allocate a single zero-initialised object. * * Always use as `(T *)mem_alloc(mem, sizeof(T))`. + * + * @param mem The memory allocator. + * @param size Size in bytes of each element. */ void *_Nullable mem_alloc(const Memory *_Nonnull mem, uint32_t size); @@ -75,3 +96,4 @@ void mem_delete(const Memory *_Nonnull mem, void *_Nullable ptr); #endif #endif /* C_TOXCORE_TOXCORE_MEM_H */ + diff --git a/toxcore/mem_test.cc b/toxcore/mem_test.cc index c0e88831..87490606 100644 --- a/toxcore/mem_test.cc +++ b/toxcore/mem_test.cc @@ -2,6 +2,7 @@ #include +#include "attributes.h" #include "os_memory.h" namespace { @@ -9,9 +10,9 @@ namespace { TEST(Mem, AllocLarge) { // Mebi prefix: https://en.wikipedia.org/wiki/Binary_prefix. - constexpr uint32_t MI = 1024 * 1024; + constexpr std::uint32_t MI = 1024 * 1024; - const Memory *mem = os_memory(); + const Memory *_Nonnull mem = os_memory(); void *ptr = mem_valloc(mem, 4, MI); EXPECT_NE(ptr, nullptr); @@ -22,9 +23,9 @@ TEST(Mem, AllocLarge) TEST(Mem, AllocOverflow) { // Gibi prefix. - constexpr uint32_t GI = 1024 * 1024 * 1024; + constexpr std::uint32_t GI = 1024 * 1024 * 1024; - const Memory *mem = os_memory(); + const Memory *_Nonnull mem = os_memory(); // 1 gibi-elements of 100 bytes each. void *ptr = mem_valloc(mem, GI, 100); diff --git a/toxcore/mono_time_test.cc b/toxcore/mono_time_test.cc index 8844f78b..cc3a3d59 100644 --- a/toxcore/mono_time_test.cc +++ b/toxcore/mono_time_test.cc @@ -5,6 +5,7 @@ #include +#include "attributes.h" #include "mono_time_test_util.hh" namespace { @@ -15,19 +16,19 @@ using tox::test::SimulatedEnvironment; TEST(MonoTime, TimeIncreasesWhenAdvanced) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Mono_Time *mono_time = mono_time_new(&c_mem, nullptr, nullptr); ASSERT_NE(mono_time, nullptr); setup_fake_clock(mono_time, env.fake_clock()); mono_time_update(mono_time); - uint64_t const start = mono_time_get(mono_time); + std::uint64_t const start = mono_time_get(mono_time); // Advance 10 seconds to ensure we definitely cross second boundaries and see an increase env.fake_clock().advance(10000); mono_time_update(mono_time); - uint64_t const end = mono_time_get(mono_time); + std::uint64_t const end = mono_time_get(mono_time); EXPECT_GT(end, start); EXPECT_EQ(end, start + 10); @@ -37,13 +38,13 @@ TEST(MonoTime, TimeIncreasesWhenAdvanced) TEST(MonoTime, IsTimeout) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Mono_Time *mono_time = mono_time_new(&c_mem, nullptr, nullptr); ASSERT_NE(mono_time, nullptr); setup_fake_clock(mono_time, env.fake_clock()); mono_time_update(mono_time); // Ensure start is consistent with fake clock - uint64_t const start = mono_time_get(mono_time); + std::uint64_t const start = mono_time_get(mono_time); EXPECT_FALSE(mono_time_is_timeout(mono_time, start, 1)); env.fake_clock().advance(2000); // 2 seconds @@ -57,13 +58,13 @@ TEST(MonoTime, IsTimeout) TEST(MonoTime, IsTimeoutWithIntermediateUpdates) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Mono_Time *mono_time = mono_time_new(&c_mem, nullptr, nullptr); ASSERT_NE(mono_time, nullptr); setup_fake_clock(mono_time, env.fake_clock()); mono_time_update(mono_time); - uint64_t const start = mono_time_get(mono_time); + std::uint64_t const start = mono_time_get(mono_time); EXPECT_FALSE(mono_time_is_timeout(mono_time, start, 5)); env.fake_clock().advance(100); @@ -82,18 +83,20 @@ TEST(MonoTime, IsTimeoutWithIntermediateUpdates) TEST(MonoTime, CustomTime) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Mono_Time *mono_time = mono_time_new(&c_mem, nullptr, nullptr); ASSERT_NE(mono_time, nullptr); - uint64_t test_time = 123456; + std::uint64_t test_time = 123456; mono_time_set_current_time_callback( - mono_time, [](void *user_data) { return *static_cast(user_data); }, &test_time); + mono_time, + [](void *_Nullable user_data) { return *static_cast(user_data); }, + &test_time); mono_time_update(mono_time); EXPECT_EQ(current_time_monotonic(mono_time), test_time); - uint64_t const start = mono_time_get(mono_time); + std::uint64_t const start = mono_time_get(mono_time); test_time += 7000; mono_time_update(mono_time); diff --git a/toxcore/mono_time_test_util.cc b/toxcore/mono_time_test_util.cc index b46d386b..77e47e6e 100644 --- a/toxcore/mono_time_test_util.cc +++ b/toxcore/mono_time_test_util.cc @@ -2,11 +2,11 @@ using tox::test::FakeClock; -void setup_fake_clock(Mono_Time *mono_time, FakeClock &clock) +void setup_fake_clock(Mono_Time *_Nonnull mono_time, FakeClock &clock) { mono_time_set_current_time_callback( mono_time, - [](void *user_data) -> uint64_t { + [](void *_Nullable user_data) -> std::uint64_t { return static_cast(user_data)->current_time_ms(); }, &clock); diff --git a/toxcore/mono_time_test_util.hh b/toxcore/mono_time_test_util.hh index 357b345a..bee44093 100644 --- a/toxcore/mono_time_test_util.hh +++ b/toxcore/mono_time_test_util.hh @@ -2,8 +2,9 @@ #define C_TOXCORE_TOXCORE_MONO_TIME_TEST_UTIL_HH #include "../testing/support/doubles/fake_clock.hh" +#include "attributes.h" #include "mono_time.h" -void setup_fake_clock(Mono_Time *mono_time, tox::test::FakeClock &clock); +void setup_fake_clock(Mono_Time *_Nonnull mono_time, tox::test::FakeClock &clock); #endif // C_TOXCORE_TOXCORE_MONO_TIME_TEST_UTIL_HH diff --git a/toxcore/net.c b/toxcore/net.c new file mode 100644 index 00000000..da142bc4 --- /dev/null +++ b/toxcore/net.c @@ -0,0 +1,303 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2016-2026 The TokTok team. + * Copyright © 2013 Tox project. + */ + +#include "net.h" + +int net_socket_to_native(Socket sock) +{ + return (force int)sock.value; +} + +Socket net_socket_from_native(int sock) +{ + const Socket res = {(force Socket_Value)sock}; + return res; +} + +int ns_close(const Network *ns, Socket sock) +{ + return ns->funcs->close(ns->obj, sock); +} + +Socket ns_accept(const Network *ns, Socket sock) +{ + return ns->funcs->accept(ns->obj, sock); +} + +int ns_bind(const Network *ns, Socket sock, const IP_Port *addr) +{ + return ns->funcs->bind(ns->obj, sock, addr); +} + +int ns_listen(const Network *ns, Socket sock, int backlog) +{ + return ns->funcs->listen(ns->obj, sock, backlog); +} + +int ns_connect(const Network *ns, Socket sock, const IP_Port *addr) +{ + return ns->funcs->connect(ns->obj, sock, addr); +} + +int ns_recvbuf(const Network *ns, Socket sock) +{ + return ns->funcs->recvbuf(ns->obj, sock); +} + +int ns_recv(const Network *ns, Socket sock, uint8_t *buf, size_t len) +{ + return ns->funcs->recv(ns->obj, sock, buf, len); +} + +int ns_recvfrom(const Network *ns, Socket sock, uint8_t *buf, size_t len, IP_Port *addr) +{ + return ns->funcs->recvfrom(ns->obj, sock, buf, len, addr); +} + +int ns_send(const Network *ns, Socket sock, const uint8_t *buf, size_t len) +{ + return ns->funcs->send(ns->obj, sock, buf, len); +} + +int ns_sendto(const Network *ns, Socket sock, const uint8_t *buf, size_t len, const IP_Port *addr) +{ + return ns->funcs->sendto(ns->obj, sock, buf, len, addr); +} + +Socket ns_socket(const Network *ns, int domain, int type, int proto) +{ + return ns->funcs->socket(ns->obj, domain, type, proto); +} + +int ns_socket_nonblock(const Network *ns, Socket sock, bool nonblock) +{ + return ns->funcs->socket_nonblock(ns->obj, sock, nonblock); +} + +int ns_getsockopt(const Network *ns, Socket sock, int level, int optname, void *optval, size_t *optlen) +{ + return ns->funcs->getsockopt(ns->obj, sock, level, optname, optval, optlen); +} + +int ns_setsockopt(const Network *ns, Socket sock, int level, int optname, const void *optval, size_t optlen) +{ + return ns->funcs->setsockopt(ns->obj, sock, level, optname, optval, optlen); +} + +int ns_getaddrinfo(const Network *ns, const Memory *mem, const char *address, int family, int protocol, IP_Port **addrs) +{ + return ns->funcs->getaddrinfo(ns->obj, mem, address, family, protocol, addrs); +} + +int ns_freeaddrinfo(const Network *ns, const Memory *mem, IP_Port *addrs) +{ + return ns->funcs->freeaddrinfo(ns->obj, mem, addrs); +} + +size_t net_pack_bool(uint8_t *bytes, bool v) +{ + bytes[0] = v ? 1 : 0; + return 1; +} + +size_t net_pack_u16(uint8_t *bytes, uint16_t v) +{ + bytes[0] = (v >> 8) & 0xff; + bytes[1] = v & 0xff; + return sizeof(v); +} + +size_t net_pack_u32(uint8_t *bytes, uint32_t v) +{ + uint8_t *p = bytes; + p += net_pack_u16(p, (v >> 16) & 0xffff); + p += net_pack_u16(p, v & 0xffff); + return p - bytes; +} + +size_t net_pack_u64(uint8_t *bytes, uint64_t v) +{ + uint8_t *p = bytes; + p += net_pack_u32(p, (v >> 32) & 0xffffffff); + p += net_pack_u32(p, v & 0xffffffff); + return p - bytes; +} + +size_t net_unpack_bool(const uint8_t *bytes, bool *v) +{ + *v = bytes[0] != 0; + return 1; +} + +size_t net_unpack_u16(const uint8_t *bytes, uint16_t *v) +{ + const uint8_t hi = bytes[0]; + const uint8_t lo = bytes[1]; + *v = ((uint16_t)hi << 8) | lo; + return sizeof(*v); +} + +size_t net_unpack_u32(const uint8_t *bytes, uint32_t *v) +{ + const uint8_t *p = bytes; + uint16_t hi; + uint16_t lo; + p += net_unpack_u16(p, &hi); + p += net_unpack_u16(p, &lo); + *v = ((uint32_t)hi << 16) | lo; + return p - bytes; +} + +size_t net_unpack_u64(const uint8_t *bytes, uint64_t *v) +{ + const uint8_t *p = bytes; + uint32_t hi; + uint32_t lo; + p += net_unpack_u32(p, &hi); + p += net_unpack_u32(p, &lo); + *v = ((uint64_t)hi << 32) | lo; + return p - bytes; +} + +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}; + +Family net_family_unspec(void) +{ + return family_unspec; +} + +Family net_family_ipv4(void) +{ + return family_ipv4; +} + +Family net_family_ipv6(void) +{ + return family_ipv6; +} + +Family net_family_tcp_server(void) +{ + return family_tcp_server; +} + +Family net_family_tcp_client(void) +{ + return family_tcp_client; +} + +Family net_family_tcp_ipv4(void) +{ + return family_tcp_ipv4; +} + +Family net_family_tcp_ipv6(void) +{ + return family_tcp_ipv6; +} + +Family net_family_tox_tcp_ipv4(void) +{ + return family_tox_tcp_ipv4; +} + +Family net_family_tox_tcp_ipv6(void) +{ + return family_tox_tcp_ipv6; +} + +bool net_family_is_unspec(Family family) +{ + return family.value == family_unspec.value; +} + +bool net_family_is_ipv4(Family family) +{ + return family.value == family_ipv4.value; +} + +bool net_family_is_ipv6(Family family) +{ + return family.value == family_ipv6.value; +} + +bool net_family_is_tcp_server(Family family) +{ + return family.value == family_tcp_server.value; +} + +bool net_family_is_tcp_client(Family family) +{ + return family.value == family_tcp_client.value; +} + +bool net_family_is_tcp_ipv4(Family family) +{ + return family.value == family_tcp_ipv4.value; +} + +bool net_family_is_tcp_ipv6(Family family) +{ + return family.value == family_tcp_ipv6.value; +} + +bool net_family_is_tox_tcp_ipv4(Family family) +{ + return family.value == family_tox_tcp_ipv4.value; +} + +bool net_family_is_tox_tcp_ipv6(Family family) +{ + return family.value == family_tox_tcp_ipv6.value; +} + +const char *net_family_to_string(Family family) +{ + if (net_family_is_unspec(family)) { + return "TOX_AF_UNSPEC"; + } + + if (net_family_is_ipv4(family)) { + return "TOX_AF_INET"; + } + + if (net_family_is_ipv6(family)) { + return "TOX_AF_INET6"; + } + + if (net_family_is_tcp_server(family)) { + return "TCP_SERVER_FAMILY"; + } + + if (net_family_is_tcp_client(family)) { + return "TCP_CLIENT_FAMILY"; + } + + if (net_family_is_tcp_ipv4(family)) { + return "TCP_INET"; + } + + if (net_family_is_tcp_ipv6(family)) { + return "TCP_INET6"; + } + + if (net_family_is_tox_tcp_ipv4(family)) { + return "TOX_TCP_INET"; + } + + if (net_family_is_tox_tcp_ipv6(family)) { + return "TOX_TCP_INET6"; + } + + return ""; +} diff --git a/toxcore/net.h b/toxcore/net.h new file mode 100644 index 00000000..f4dbc2e6 --- /dev/null +++ b/toxcore/net.h @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2016-2026 The TokTok team. + * Copyright © 2013 Tox project. + */ + +#ifndef C_TOXCORE_TOXCORE_NET_H +#define C_TOXCORE_TOXCORE_NET_H + +#include +#include +#include + +#include "attributes.h" +#include "mem.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef bitwise int Socket_Value; +typedef struct Socket { + Socket_Value value; +} Socket; + +#define SIZE_IP4 4 +#define SIZE_IP6 16 +#define SIZE_IP (1 + SIZE_IP6) +#define SIZE_PORT 2 +#define SIZE_IPPORT (SIZE_IP + SIZE_PORT) + +typedef struct Family { + uint8_t value; +} Family; + +typedef union IP4 { + uint32_t uint32; + uint16_t uint16[2]; + uint8_t uint8[4]; +} IP4; + +typedef union IP6 { + uint8_t uint8[16]; + uint16_t uint16[8]; + uint32_t uint32[4]; + uint64_t uint64[2]; +} IP6; + +typedef union IP_Union { + IP4 v4; + IP6 v6; +} IP_Union; + +typedef struct IP { + Family family; + IP_Union ip; +} IP; + +typedef struct IP_Port { + IP ip; + uint16_t port; +} IP_Port; + +/** Redefinitions of variables for safe transfer over wire. */ +#define TOX_AF_UNSPEC 0 +#define TOX_AF_INET 2 +#define TOX_AF_INET6 10 +#define TOX_TCP_INET 130 +#define TOX_TCP_INET6 138 + +#define TOX_SOCK_STREAM 1 +#define TOX_SOCK_DGRAM 2 + +#define TOX_PROTO_TCP 1 +#define TOX_PROTO_UDP 2 + +/** TCP related */ +#define TCP_CLIENT_FAMILY (TOX_AF_INET6 + 1) +#define TCP_INET (TOX_AF_INET6 + 2) +#define TCP_INET6 (TOX_AF_INET6 + 3) +#define TCP_SERVER_FAMILY (TOX_AF_INET6 + 4) + +int net_socket_to_native(Socket sock); +Socket net_socket_from_native(int sock); +Socket net_invalid_socket(void); + +typedef int net_close_cb(void *_Nullable obj, Socket sock); +typedef Socket net_accept_cb(void *_Nullable obj, Socket sock); +typedef int net_bind_cb(void *_Nullable obj, Socket sock, const IP_Port *_Nonnull addr); +typedef int net_listen_cb(void *_Nullable obj, Socket sock, int backlog); +typedef int net_connect_cb(void *_Nullable obj, Socket sock, const IP_Port *_Nonnull addr); +typedef int net_recvbuf_cb(void *_Nullable obj, Socket sock); +typedef int net_recv_cb(void *_Nullable obj, Socket sock, uint8_t *_Nonnull buf, size_t len); +typedef int net_recvfrom_cb(void *_Nullable obj, Socket sock, uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr); +typedef int net_send_cb(void *_Nullable obj, Socket sock, const uint8_t *_Nonnull buf, size_t len); +typedef int net_sendto_cb(void *_Nullable obj, Socket sock, const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr); +typedef Socket net_socket_cb(void *_Nullable obj, int domain, int type, int proto); +typedef int net_socket_nonblock_cb(void *_Nullable obj, Socket sock, bool nonblock); +typedef int net_getsockopt_cb(void *_Nullable obj, Socket sock, int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen); +typedef int net_setsockopt_cb(void *_Nullable obj, Socket sock, int level, int optname, const void *_Nonnull optval, size_t optlen); +typedef int net_getaddrinfo_cb(void *_Nullable obj, const Memory *_Nonnull mem, const char *_Nonnull address, int family, int protocol, IP_Port *_Nullable *_Nonnull addrs); +typedef int net_freeaddrinfo_cb(void *_Nullable obj, const Memory *_Nonnull mem, IP_Port *_Nullable addrs); + +typedef struct Network_Funcs { + net_close_cb *_Nullable close; + net_accept_cb *_Nullable accept; + net_bind_cb *_Nullable bind; + net_listen_cb *_Nullable listen; + net_connect_cb *_Nullable connect; + net_recvbuf_cb *_Nullable recvbuf; + net_recv_cb *_Nullable recv; + net_recvfrom_cb *_Nullable recvfrom; + net_send_cb *_Nullable send; + net_sendto_cb *_Nullable sendto; + net_socket_cb *_Nullable socket; + net_socket_nonblock_cb *_Nullable socket_nonblock; + net_getsockopt_cb *_Nullable getsockopt; + net_setsockopt_cb *_Nullable setsockopt; + net_getaddrinfo_cb *_Nullable getaddrinfo; + net_freeaddrinfo_cb *_Nullable freeaddrinfo; +} Network_Funcs; + +typedef struct Network { + const Network_Funcs *_Nullable funcs; + void *_Nullable obj; +} Network; + +int ns_close(const Network *_Nonnull ns, Socket sock); +Socket ns_accept(const Network *_Nonnull ns, Socket sock); +int ns_bind(const Network *_Nonnull ns, Socket sock, const IP_Port *_Nonnull addr); +int ns_listen(const Network *_Nonnull ns, Socket sock, int backlog); +int ns_connect(const Network *_Nonnull ns, Socket sock, const IP_Port *_Nonnull addr); +int ns_recvbuf(const Network *_Nonnull ns, Socket sock); +int ns_recv(const Network *_Nonnull ns, Socket sock, uint8_t *_Nonnull buf, size_t len); +int ns_recvfrom(const Network *_Nonnull ns, Socket sock, uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr); +int ns_send(const Network *_Nonnull ns, Socket sock, const uint8_t *_Nonnull buf, size_t len); +int ns_sendto(const Network *_Nonnull ns, Socket sock, const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr); +Socket ns_socket(const Network *_Nonnull ns, int domain, int type, int proto); +int ns_socket_nonblock(const Network *_Nonnull ns, Socket sock, bool nonblock); +int ns_getsockopt(const Network *_Nonnull ns, Socket sock, int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen); +int ns_setsockopt(const Network *_Nonnull ns, Socket sock, int level, int optname, const void *_Nonnull optval, size_t optlen); +int ns_getaddrinfo(const Network *_Nonnull ns, const Memory *_Nonnull mem, const char *_Nonnull address, int family, int protocol, IP_Port *_Nullable *_Nonnull addrs); +int ns_freeaddrinfo(const Network *_Nonnull ns, const Memory *_Nonnull mem, IP_Port *_Nullable addrs); + +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_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); + +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); + +const char *_Nonnull net_family_to_string(Family family); + +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); + +const char *_Nullable net_inet_ntop4(const IP4 *_Nonnull addr, char *_Nonnull buf, size_t bufsize); +const char *_Nullable net_inet_ntop6(const IP6 *_Nonnull addr, char *_Nonnull buf, size_t bufsize); +int net_inet_pton4(const char *_Nonnull addr_string, IP4 *_Nonnull addrbuf); +int net_inet_pton6(const char *_Nonnull addr_string, IP6 *_Nonnull addrbuf); + +bool net_should_ignore_recv_error(int err); +bool net_should_ignore_connect_error(int err); + +size_t net_pack_bool(uint8_t *_Nonnull bytes, bool v); +size_t net_pack_u16(uint8_t *_Nonnull bytes, uint16_t v); +size_t net_pack_u32(uint8_t *_Nonnull bytes, uint32_t v); +size_t net_pack_u64(uint8_t *_Nonnull bytes, uint64_t v); + +size_t net_unpack_bool(const uint8_t *_Nonnull bytes, bool *_Nonnull v); +size_t net_unpack_u16(const uint8_t *_Nonnull bytes, uint16_t *_Nonnull v); +size_t net_unpack_u32(const uint8_t *_Nonnull bytes, uint32_t *_Nonnull v); +size_t net_unpack_u64(const uint8_t *_Nonnull bytes, uint64_t *_Nonnull v); + +int net_error(void); +void net_clear_error(void); + +#define NET_STRERROR_SIZE 256 + +/** @brief Contains a null terminated formatted error message. + * + * This struct should not contain more than at most the 2 fields. + */ +typedef struct Net_Strerror { + char data[NET_STRERROR_SIZE]; + uint16_t size; +} Net_Strerror; + +/** @brief Get a text explanation for the error code from `net_error()`. + * + * @param error The error code to get a string for. + * @param buf The struct to store the error message in (usually on stack). + * + * @return pointer to a NULL-terminated string describing the error code. + */ +char *_Nonnull net_strerror(int error, Net_Strerror *_Nonnull buf); + +IP4 net_get_ip4_loopback(void); +IP4 net_get_ip4_broadcast(void); + +IP6 net_get_ip6_loopback(void); +IP6 net_get_ip6_broadcast(void); + +bool net_join_multicast(const Network *_Nonnull ns, Socket sock, Family family); + +bool net_set_socket_nonblock(const Network *_Nonnull ns, Socket sock); +bool net_set_socket_nosigpipe(const Network *_Nonnull ns, Socket sock); +bool net_set_socket_reuseaddr(const Network *_Nonnull ns, Socket sock); +bool net_set_socket_dualstack(const Network *_Nonnull ns, Socket sock); +bool net_set_socket_buffer_size(const Network *_Nonnull ns, Socket sock, int size); +bool net_set_socket_broadcast(const Network *_Nonnull ns, Socket sock); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* C_TOXCORE_TOXCORE_NET_H */ diff --git a/toxcore/net_crypto.h b/toxcore/net_crypto.h index 1a462660..244e1841 100644 --- a/toxcore/net_crypto.h +++ b/toxcore/net_crypto.h @@ -9,6 +9,9 @@ #ifndef C_TOXCORE_TOXCORE_NET_CRYPTO_H #define C_TOXCORE_TOXCORE_NET_CRYPTO_H +#include +#include + #include "DHT.h" // Node_format #include "TCP_client.h" #include "TCP_connection.h" @@ -17,8 +20,10 @@ #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "net_profile.h" #include "network.h" +#include "rng.h" #ifdef __cplusplus extern "C" { diff --git a/toxcore/net_crypto_fuzz_test.cc b/toxcore/net_crypto_fuzz_test.cc index 73451ae5..bfea1e45 100644 --- a/toxcore/net_crypto_fuzz_test.cc +++ b/toxcore/net_crypto_fuzz_test.cc @@ -11,6 +11,7 @@ #include "../testing/support/public/simulated_environment.hh" #include "DHT.h" #include "TCP_client.h" +#include "attributes.h" #include "net_profile.h" #include "network.h" @@ -24,24 +25,24 @@ using tox::test::SimulatedEnvironment; template using Ptr = std::unique_ptr; -std::optional> prepare(Fuzz_Data &input) +std::optional> prepare(Fuzz_Data &input) { IP_Port ipp; ip_init(&ipp.ip, true); ipp.port = net_htons(33445); - CONSUME_OR_RETURN_VAL(const uint8_t *iterations_packed, input, 1, std::nullopt); - uint8_t iterations = *iterations_packed; + CONSUME_OR_RETURN_VAL(const std::uint8_t *iterations_packed, input, 1, std::nullopt); + std::uint8_t iterations = *iterations_packed; return {{ipp, iterations}}; } static constexpr Net_Crypto_DHT_Funcs dht_funcs = { - [](void *dht, const uint8_t *public_key) { + [](void *_Nonnull dht, const std::uint8_t *_Nonnull public_key) { return dht_get_shared_key_sent(static_cast(dht), public_key); }, - [](const void *dht) { return dht_get_self_public_key(static_cast(dht)); }, - [](const void *dht) { return dht_get_self_secret_key(static_cast(dht)); }, + [](const void *_Nonnull dht) { return dht_get_self_public_key(static_cast(dht)); }, + [](const void *_Nonnull dht) { return dht_get_self_secret_key(static_cast(dht)); }, }; void TestNetCrypto(Fuzz_Data &input) @@ -73,7 +74,9 @@ void TestNetCrypto(Fuzz_Data &input) const std::unique_ptr> mono_time( mono_time_new( &node->c_memory, - [](void *user_data) { return static_cast(user_data)->current_time_ms(); }, + [](void *_Nullable user_data) { + return static_cast(user_data)->current_time_ms(); + }, &env.fake_clock()), [&node](Mono_Time *ptr) { mono_time_free(&node->c_memory, ptr); }); @@ -105,7 +108,7 @@ void TestNetCrypto(Fuzz_Data &input) return; } - for (uint8_t i = 0; i < iterations; ++i) { + for (std::uint8_t i = 0; i < iterations; ++i) { networking_poll(net.get(), nullptr); do_dht(dht.get()); do_net_crypto(net_crypto.get(), nullptr); @@ -118,8 +121,8 @@ void TestNetCrypto(Fuzz_Data &input) } // namespace -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size); +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { tox::test::fuzz_select_target(data, size); return 0; diff --git a/toxcore/net_crypto_test.cc b/toxcore/net_crypto_test.cc index c28e11e2..ea57cfb3 100644 --- a/toxcore/net_crypto_test.cc +++ b/toxcore/net_crypto_test.cc @@ -14,11 +14,13 @@ #include "../testing/support/public/simulated_environment.hh" #include "DHT_test_util.hh" +#include "attributes.h" #include "crypto_core.h" #include "logger.h" #include "mono_time.h" #include "net_profile.h" #include "network.h" +#include "test_util.hh" namespace { @@ -29,7 +31,7 @@ using namespace tox::test; template class TestNode { public: - TestNode(SimulatedEnvironment &env, uint16_t port, bool enable_trace = false) + TestNode(SimulatedEnvironment &env, std::uint16_t port, bool enable_trace = false) : dht_wrapper_(env, port) , net_profile_(netprof_new(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory), [mem = &dht_wrapper_.node().c_memory](Net_Profile *p) { netprof_kill(mem, p); }) @@ -39,9 +41,10 @@ public: // Setup Logger to stderr logger_callback_log( dht_wrapper_.logger(), - [](void *context, Logger_Level level, const char *file, uint32_t line, const char *func, - const char *message, void *) { - auto *self = static_cast(context); + [](void *_Nullable context, Logger_Level level, const char *_Nonnull file, + std::uint32_t line, const char *_Nonnull func, const char *_Nonnull message, + void *_Nullable) { + auto *self = static_cast(REQUIRE_NOT_NULL(context)); if (self->trace_enabled_ || level >= LOGGER_LEVEL_DEBUG) { fprintf(stderr, "[%d] %s:%u %s: %s\n", level, file, line, func, message); } @@ -59,11 +62,14 @@ public: new_connection_handler(net_crypto_.get(), &TestNode::static_new_connection_cb, this); } - Net_Crypto *get_net_crypto() { return net_crypto_.get(); } - const uint8_t *dht_public_key() const { return dht_wrapper_.dht_public_key(); } - const uint8_t *real_public_key() const { return nc_get_self_public_key(net_crypto_.get()); } + Net_Crypto *_Nonnull get_net_crypto() { return REQUIRE_NOT_NULL(net_crypto_.get()); } + const std::uint8_t *_Nonnull dht_public_key() const { return dht_wrapper_.dht_public_key(); } + const std::uint8_t *_Nonnull real_public_key() const + { + return nc_get_self_public_key(net_crypto_.get()); + } int dht_computation_count() const { return dht_wrapper_.dht_computation_count(); } - const Memory *get_memory() const { return &dht_wrapper_.node().c_memory; } + const Memory *_Nonnull get_memory() const { return &dht_wrapper_.node().c_memory; } IP_Port get_ip_port() const { return dht_wrapper_.get_ip_port(); } @@ -94,7 +100,7 @@ public: } // Sends data to the connected peer (assuming only 1 for simplicity or last connected) - bool send_data(int conn_id, const std::vector &data) + bool send_data(int conn_id, const std::vector &data) { if (data.empty()) return false; @@ -102,7 +108,7 @@ public: return write_cryptpacket(net_crypto_.get(), conn_id, data.data(), data.size(), false) != -1; } - void send_direct_packet(const IP_Port &dest, const std::vector &data) + void send_direct_packet(const IP_Port &dest, const std::vector &data) { if (data.empty()) return; @@ -118,7 +124,7 @@ public: return connections_[conn_id].connected; } - const std::vector &get_last_received_data(int conn_id) const + const std::vector &get_last_received_data(int conn_id) const { if (conn_id < 0 || conn_id >= static_cast(connections_.size())) return empty_vector_; @@ -126,7 +132,7 @@ public: } // Helper to get the ID assigned to a peer by Public Key (for the acceptor side) - int get_connection_id_by_pk(const uint8_t *pk) { return last_accepted_id_; } + int get_connection_id_by_pk(const std::uint8_t *pk) { return last_accepted_id_; } ~TestNode(); @@ -135,13 +141,13 @@ private: struct ConnectionState { bool connected = false; - std::vector received_data; + std::vector received_data; }; // We map connection IDs to state. connection IDs are small ints. std::vector connections_{128}; int last_accepted_id_ = -1; - std::vector empty_vector_; + std::vector empty_vector_; void setup_connection_callbacks(int id) { @@ -156,7 +162,7 @@ private: // -- Static Callbacks -- - static int static_new_connection_cb(void *object, const New_Connection *n_c) + static int static_new_connection_cb(void *_Nonnull object, const New_Connection *_Nonnull n_c) { auto *self = static_cast(object); int id = accept_crypto_connection(self->net_crypto_.get(), n_c); @@ -167,7 +173,8 @@ private: return id; // Return ID on success } - static int static_connection_status_cb(void *object, int id, bool status, void *userdata) + static int static_connection_status_cb( + void *_Nonnull object, int id, bool status, void *_Nullable userdata) { auto *self = static_cast(object); if (id < static_cast(self->connections_.size())) { @@ -176,8 +183,8 @@ private: return 0; } - static int static_connection_data_cb( - void *object, int id, const uint8_t *data, uint16_t length, void *userdata) + static int static_connection_data_cb(void *_Nonnull object, int id, + const std::uint8_t *_Nonnull data, std::uint16_t length, void *_Nullable userdata) { auto *self = static_cast(object); if (id < static_cast(self->connections_.size())) { @@ -234,7 +241,7 @@ TEST_F(NetCryptoTest, EndToEndDataExchange) // 3. Exchange Data // Packet ID must be in custom range (160+) - std::vector message = {160, 'H', 'e', 'l', 'l', 'o'}; + std::vector message = {160, 'H', 'e', 'l', 'l', 'o'}; EXPECT_TRUE(alice.send_data(alice_conn_id, message)); @@ -334,7 +341,7 @@ TEST_F(NetCryptoTest, DataLossAndRetransmission) return true; }); - std::vector message = {161, 'R', 'e', 't', 'r', 'y'}; + std::vector message = {161, 'R', 'e', 't', 'r', 'y'}; alice.send_data(alice_conn_id, message); // Alice needs to detect packet loss and retransmit. @@ -365,19 +372,19 @@ TEST_F(NetCryptoTest, CookieRequestCPUExhaustion) // Cookie Request Packet Length // From net_crypto.c: - // #define COOKIE_REQUEST_LENGTH (uint16_t)(1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + - // COOKIE_REQUEST_PLAIN_LENGTH + CRYPTO_MAC_SIZE) 1 + 32 + 24 + (32 * 2 + 8) + 16 = 145 + // #define COOKIE_REQUEST_LENGTH (std::uint16_t)(1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + // + COOKIE_REQUEST_PLAIN_LENGTH + CRYPTO_MAC_SIZE) 1 + 32 + 24 + (32 * 2 + 8) + 16 = 145 const int TEST_COOKIE_REQUEST_LENGTH = 145; // Send enough packets to trigger rate limiting const int NUM_PACKETS = 50; std::minstd_rand rng(42); - std::uniform_int_distribution dist; - auto gen = [&]() { return dist(rng); }; + std::uniform_int_distribution dist(0, 255); + auto gen = [&]() { return static_cast(dist(rng)); }; for (int i = 0; i < NUM_PACKETS; ++i) { - std::vector packet(TEST_COOKIE_REQUEST_LENGTH); + std::vector packet(TEST_COOKIE_REQUEST_LENGTH); packet[0] = NET_PACKET_COOKIE_REQUEST; // Random public key at offset 1 (size 32) @@ -408,11 +415,11 @@ TEST_F(NetCryptoTest, CookieRequestRateLimiting) const int TEST_COOKIE_REQUEST_LENGTH = 145; std::minstd_rand rng(42); - std::uniform_int_distribution dist; - auto gen = [&]() { return dist(rng); }; + std::uniform_int_distribution dist(0, 255); + auto gen = [&]() { return static_cast(dist(rng)); }; auto send_packet = [&]() { - std::vector packet(TEST_COOKIE_REQUEST_LENGTH); + std::vector packet(TEST_COOKIE_REQUEST_LENGTH); packet[0] = NET_PACKET_COOKIE_REQUEST; std::generate(packet.begin() + 1, packet.begin() + 1 + CRYPTO_PUBLIC_KEY_SIZE, gen); std::generate(packet.begin() + 1 + CRYPTO_PUBLIC_KEY_SIZE, packet.end(), gen); @@ -475,7 +482,7 @@ TEST_F(NetCryptoTest, HandleRequestPacketOOB) ASSERT_TRUE(connected); // 2. Alice sends many packets to populate her send_array. - std::vector dummy_data(50, 'A'); + std::vector dummy_data(50, 'A'); for (int i = 0; i < 300; ++i) { dummy_data[0] = 160 + (i % 30); // Valid packet ID range alice.send_data(alice_conn_id, dummy_data); @@ -483,35 +490,36 @@ TEST_F(NetCryptoTest, HandleRequestPacketOOB) alice.poll(); // Process sends // 3. Construct the malicious packet. - uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; - uint8_t alice_sent_nonce[CRYPTO_NONCE_SIZE]; - uint8_t alice_recv_nonce[CRYPTO_NONCE_SIZE]; + std::uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; + std::uint8_t alice_sent_nonce[CRYPTO_NONCE_SIZE]; + std::uint8_t alice_recv_nonce[CRYPTO_NONCE_SIZE]; // Retrieve secrets nc_testonly_get_secrets( alice.get_net_crypto(), alice_conn_id, shared_key, alice_sent_nonce, alice_recv_nonce); // Use Alice's recv_nonce (which Bob uses to encrypt) - uint8_t nonce[CRYPTO_NONCE_SIZE]; - memcpy(nonce, alice_recv_nonce, CRYPTO_NONCE_SIZE); + std::uint8_t nonce[CRYPTO_NONCE_SIZE]; + std::memcpy(nonce, alice_recv_nonce, CRYPTO_NONCE_SIZE); // Payload: [PACKET_ID_REQUEST (1), 255] // The length of 2 will trigger the OOB read when n wraps around. - uint8_t plaintext[] = {PACKET_ID_REQUEST, 255}; - uint16_t plaintext_len = sizeof(plaintext); + std::uint8_t plaintext[] = {PACKET_ID_REQUEST, 255}; + std::uint16_t plaintext_len = sizeof(plaintext); - uint16_t packet_size = 1 + sizeof(uint16_t) + plaintext_len + CRYPTO_MAC_SIZE; - std::vector malicious_packet(packet_size); + std::uint16_t packet_size = 1 + sizeof(std::uint16_t) + plaintext_len + CRYPTO_MAC_SIZE; + std::vector malicious_packet(packet_size); malicious_packet[0] = NET_PACKET_CRYPTO_DATA; - memcpy(&malicious_packet[1], nonce + (CRYPTO_NONCE_SIZE - sizeof(uint16_t)), sizeof(uint16_t)); + std::memcpy(&malicious_packet[1], nonce + (CRYPTO_NONCE_SIZE - sizeof(std::uint16_t)), + sizeof(std::uint16_t)); int len = encrypt_data_symmetric( alice.get_memory(), shared_key, nonce, plaintext, plaintext_len, &malicious_packet[3]); ASSERT_EQ(len, plaintext_len + CRYPTO_MAC_SIZE); // 4. Inject the packet - tox::test::Packet p; + tox::test::Packet p{}; p.to = alice.get_ip_port(); p.data = malicious_packet; p.from = bob.get_ip_port(); @@ -554,7 +562,7 @@ TEST_F(NetCryptoTest, EndToEndDataExchange_RealDHT) // 3. Exchange Data // Packet ID must be in custom range (160+) - std::vector message = {160, 'H', 'e', 'l', 'l', 'o'}; + std::vector message = {160, 'H', 'e', 'l', 'l', 'o'}; EXPECT_TRUE(alice.send_data(alice_conn_id, message)); diff --git a/toxcore/net_log.h b/toxcore/net_log.h index 6aec3df6..3bbcf04b 100644 --- a/toxcore/net_log.h +++ b/toxcore/net_log.h @@ -11,6 +11,7 @@ #include "attributes.h" #include "logger.h" +#include "net.h" #include "network.h" #ifdef __cplusplus diff --git a/toxcore/network.c b/toxcore/network.c index ac91dc23..4bb23dbc 100644 --- a/toxcore/network.c +++ b/toxcore/network.c @@ -7,74 +7,8 @@ * Functions for the core networking. */ -#ifdef __APPLE__ -#define _DARWIN_C_SOURCE -#endif /* __APPLE__ */ - -// For Solaris. -#ifdef __sun -#define __EXTENSIONS__ 1 -#endif /* __sun */ - -// For Linux (and some BSDs). -#ifndef _XOPEN_SOURCE -#define _XOPEN_SOURCE 700 -#endif /* _XOPEN_SOURCE */ - -#if defined(_WIN32) && defined(_WIN32_WINNT) && defined(_WIN32_WINNT_WINXP) && _WIN32_WINNT >= _WIN32_WINNT_WINXP -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x501 -#endif /* defined(_WIN32) && defined(_WIN32_WINNT) && defined(_WIN32_WINNT_WINXP) && _WIN32_WINNT >= _WIN32_WINNT_WINXP */ - -#if !defined(OS_WIN32) && (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) -#define OS_WIN32 -#endif /* !defined(OS_WIN32) && (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) */ - -#if defined(OS_WIN32) && !defined(WINVER) -// Windows XP -#define WINVER 0x0501 -#endif /* defined(OS_WIN32) && !defined(WINVER) */ - #include "network.h" -#ifdef OS_WIN32 // Put win32 includes here -// The mingw32/64 Windows library warns about including winsock2.h after -// windows.h even though with the above it's a valid thing to do. So, to make -// mingw32 headers happy, we include winsock2.h first. -#include -// Comment line here to avoid reordering by source code formatters. -#include -#include -#endif /* OS_WIN32 */ - -#ifdef __APPLE__ -#include -#include -#endif /* __APPLE__ */ - -#if !defined(OS_WIN32) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __sun -#include -#include -#endif /* __sun */ - -#else -#ifndef IPV6_V6ONLY -#define IPV6_V6ONLY 27 -#endif /* IPV6_V6ONLY */ -#endif /* !defined(OS_WIN32) */ - #include #include #include @@ -86,386 +20,36 @@ #include "ccompat.h" #include "logger.h" #include "mem.h" +#include "net.h" #include "net_log.h" #include "net_profile.h" +#include "os_network.h" #include "util.h" -// Disable MSG_NOSIGNAL on systems not supporting it, e.g. Windows, FreeBSD -#if !defined(MSG_NOSIGNAL) -#define MSG_NOSIGNAL 0 -#endif /* !defined(MSG_NOSIGNAL) */ - -#ifndef IPV6_ADD_MEMBERSHIP -#ifdef IPV6_JOIN_GROUP -#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP -#endif /* IPV6_JOIN_GROUP */ -#endif /* IPV6_ADD_MEMBERSHIP */ - 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) -{ - return err == EWOULDBLOCK; -} - -static bool should_ignore_connect_error(int err) -{ - return err == EWOULDBLOCK || err == EINPROGRESS; -} - -static const char *inet_ntop4(const struct in_addr *_Nonnull addr, char *_Nonnull buf, size_t bufsize) -{ - return inet_ntop(AF_INET, addr, buf, bufsize); -} - -static const char *inet_ntop6(const struct in6_addr *_Nonnull addr, char *_Nonnull buf, size_t bufsize) -{ - return inet_ntop(AF_INET6, addr, buf, bufsize); -} - -static int inet_pton4(const char *_Nonnull addr_string, struct in_addr *_Nonnull addrbuf) -{ - return inet_pton(AF_INET, addr_string, addrbuf); -} - -static int inet_pton6(const char *_Nonnull addr_string, struct in6_addr *_Nonnull addrbuf) -{ - return inet_pton(AF_INET6, addr_string, addrbuf); -} - -#else -#ifndef IPV6_V6ONLY -#define IPV6_V6ONLY 27 -#endif /* IPV6_V6ONLY */ - -static bool should_ignore_recv_error(int err) -{ - // We ignore WSAECONNRESET as Windows helpfully* sends that error if a - // previously sent UDP packet wasn't delivered. - return err == WSAEWOULDBLOCK || err == WSAECONNRESET; -} - -static bool should_ignore_connect_error(int err) -{ - return err == WSAEWOULDBLOCK || err == WSAEINPROGRESS; -} - -static const char *inet_ntop4(const struct in_addr *_Nonnull addr, char *_Nonnull buf, size_t bufsize) -{ - struct sockaddr_in saddr = {0}; - - saddr.sin_family = AF_INET; - saddr.sin_addr = *addr; - - DWORD len = bufsize; - - if (WSAAddressToString((LPSOCKADDR)&saddr, sizeof(saddr), nullptr, buf, &len)) { - return nullptr; - } - - return buf; -} - -static const char *inet_ntop6(const struct in6_addr *_Nonnull addr, char *_Nonnull buf, size_t bufsize) -{ - struct sockaddr_in6 saddr = {0}; - - saddr.sin6_family = AF_INET6; - saddr.sin6_addr = *addr; - - DWORD len = bufsize; - - if (WSAAddressToString((LPSOCKADDR)&saddr, sizeof(saddr), nullptr, buf, &len)) { - return nullptr; - } - - return buf; -} - -static int inet_pton4(const char *_Nonnull addrString, struct in_addr *_Nonnull addrbuf) -{ - struct sockaddr_in saddr = {0}; - - INT len = sizeof(saddr); - - if (WSAStringToAddress((LPTSTR)addrString, AF_INET, nullptr, (LPSOCKADDR)&saddr, &len)) { - return 0; - } - - *addrbuf = saddr.sin_addr; - - return 1; -} - -static int inet_pton6(const char *_Nonnull addrString, struct in6_addr *_Nonnull addrbuf) -{ - struct sockaddr_in6 saddr = {0}; - - INT len = sizeof(saddr); - - if (WSAStringToAddress((LPTSTR)addrString, AF_INET6, nullptr, (LPSOCKADDR)&saddr, &len)) { - return 0; - } - - *addrbuf = saddr.sin6_addr; - - return 1; -} - -#endif /* !defined(OS_WIN32) */ - -static_assert(TOX_INET6_ADDRSTRLEN >= INET6_ADDRSTRLEN, - "TOX_INET6_ADDRSTRLEN should be greater or equal to INET6_ADDRSTRLEN (#INET6_ADDRSTRLEN)"); -static_assert(TOX_INET_ADDRSTRLEN >= INET_ADDRSTRLEN, - "TOX_INET_ADDRSTRLEN should be greater or equal to INET_ADDRSTRLEN (#INET_ADDRSTRLEN)"); - -static int make_proto(int proto) -{ - switch (proto) { - case TOX_PROTO_TCP: - return IPPROTO_TCP; - - case TOX_PROTO_UDP: - return IPPROTO_UDP; - - default: - return proto; - } -} - -static int make_socktype(int type) -{ - switch (type) { - case TOX_SOCK_STREAM: - return SOCK_STREAM; - - case TOX_SOCK_DGRAM: - return SOCK_DGRAM; - - default: - return type; - } -} - -static int make_family(Family tox_family) -{ - switch (tox_family.value) { - case TOX_AF_INET: - case TCP_INET: - return AF_INET; - - case TOX_AF_INET6: - case TCP_INET6: - return AF_INET6; - - case TOX_AF_UNSPEC: - return AF_UNSPEC; - - default: - return tox_family.value; - } -} - -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}; - -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -static const Family *make_tox_family(int family) -{ - switch (family) { - case AF_INET: - return &family_ipv4; - - case AF_INET6: - return &family_ipv6; - - case AF_UNSPEC: - return &family_unspec; - - default: - return nullptr; - } -} -#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ - -static void get_ip4(IP4 *_Nonnull result, const struct in_addr *_Nonnull 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; -} - -static void get_ip6(IP6 *_Nonnull result, const struct in6_addr *_Nonnull 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)); -} - -static void fill_addr4(const IP4 *_Nonnull ip, struct in_addr *_Nonnull addr) -{ - addr->s_addr = ip->uint32; -} - -static void fill_addr6(const IP6 *_Nonnull ip, struct in6_addr *_Nonnull addr) -{ - memcpy(addr->s6_addr, ip->uint8, sizeof(ip->uint8)); -} - -#if !defined(INADDR_LOOPBACK) -#define INADDR_LOOPBACK 0x7f000001 -#endif /* !defined(INADDR_LOOPBACK) */ - IP4 get_ip4_broadcast(void) { - const IP4 ip4_broadcast = { INADDR_BROADCAST }; - return ip4_broadcast; + return net_get_ip4_broadcast(); } IP6 get_ip6_broadcast(void) { - const IP6 ip6_broadcast = { - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } - }; - return ip6_broadcast; + return net_get_ip6_broadcast(); } IP4 get_ip4_loopback(void) { - IP4 loopback; - loopback.uint32 = htonl(INADDR_LOOPBACK); - return loopback; + return net_get_ip4_loopback(); } IP6 get_ip6_loopback(void) { - /* in6addr_loopback isn't available everywhere, so we do it ourselves. */ - IP6 loopback = {{0}}; - loopback.uint8[15] = 1; - return loopback; -} - -#ifndef OS_WIN32 -#define INVALID_SOCKET (-1) -#endif /* OS_WIN32 */ - -int net_socket_to_native(Socket sock) -{ - return (force int)sock.value; -} - -Socket net_socket_from_native(int sock) -{ - const Socket res = {(force Socket_Value)sock}; - return res; -} - -Socket net_invalid_socket(void) -{ - return net_socket_from_native(INVALID_SOCKET); -} - -Family net_family_unspec(void) -{ - return family_unspec; -} - -Family net_family_ipv4(void) -{ - return family_ipv4; -} - -Family net_family_ipv6(void) -{ - return family_ipv6; -} - -Family net_family_tcp_server(void) -{ - return family_tcp_server; -} - -Family net_family_tcp_client(void) -{ - return family_tcp_client; -} - -Family net_family_tcp_ipv4(void) -{ - return family_tcp_ipv4; -} - -Family net_family_tcp_ipv6(void) -{ - return family_tcp_ipv6; -} - -Family net_family_tox_tcp_ipv4(void) -{ - return family_tox_tcp_ipv4; -} - -Family net_family_tox_tcp_ipv6(void) -{ - return family_tox_tcp_ipv6; -} - -bool net_family_is_unspec(Family family) -{ - return family.value == family_unspec.value; -} - -bool net_family_is_ipv4(Family family) -{ - return family.value == family_ipv4.value; -} - -bool net_family_is_ipv6(Family family) -{ - return family.value == family_ipv6.value; -} - -bool net_family_is_tcp_server(Family family) -{ - return family.value == family_tcp_server.value; -} - -bool net_family_is_tcp_client(Family family) -{ - return family.value == family_tcp_client.value; -} - -bool net_family_is_tcp_ipv4(Family family) -{ - return family.value == family_tcp_ipv4.value; -} - -bool net_family_is_tcp_ipv6(Family family) -{ - return family.value == family_tcp_ipv6.value; -} - -bool net_family_is_tox_tcp_ipv4(Family family) -{ - return family.value == family_tox_tcp_ipv4.value; -} - -bool net_family_is_tox_tcp_ipv6(Family family) -{ - return family.value == family_tox_tcp_ipv6.value; + return net_get_ip6_loopback(); } bool sock_valid(Socket sock) @@ -474,309 +58,10 @@ bool sock_valid(Socket sock) return sock.value != invalid_socket.value; } -typedef struct Network_Addr { - struct sockaddr_storage addr; - size_t size; -} Network_Addr; - -static void ip_port_to_network_addr(const IP_Port *ip_port, Network_Addr *addr) -{ - addr->size = 0; - if (net_family_is_ipv4(ip_port->ip.family)) { - struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr->addr; - addr->size = sizeof(struct sockaddr_in); - addr4->sin_family = AF_INET; - fill_addr4(&ip_port->ip.ip.v4, &addr4->sin_addr); - addr4->sin_port = ip_port->port; - } else if (net_family_is_ipv6(ip_port->ip.family)) { - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr->addr; - addr->size = sizeof(struct sockaddr_in6); - addr6->sin6_family = AF_INET6; - fill_addr6(&ip_port->ip.ip.v6, &addr6->sin6_addr); - addr6->sin6_port = ip_port->port; - addr6->sin6_flowinfo = 0; - addr6->sin6_scope_id = 0; - } -} - -static bool network_addr_to_ip_port(const Network_Addr *addr, IP_Port *ip_port) -{ - if (addr->addr.ss_family == AF_INET) { - const struct sockaddr_in *addr_in = (const struct sockaddr_in *)&addr->addr; - ip_port->ip.family = net_family_ipv4(); - get_ip4(&ip_port->ip.ip.v4, &addr_in->sin_addr); - ip_port->port = addr_in->sin_port; - return true; - } else if (addr->addr.ss_family == AF_INET6) { - const struct sockaddr_in6 *addr_in6 = (const struct sockaddr_in6 *)&addr->addr; - ip_port->ip.family = net_family_ipv6(); - get_ip6(&ip_port->ip.ip.v6, &addr_in6->sin6_addr); - ip_port->port = addr_in6->sin6_port; - return true; - } - return false; -} - -static int sys_close(void *_Nonnull obj, Socket sock) -{ -#if defined(OS_WIN32) - return closesocket(net_socket_to_native(sock)); -#else // !OS_WIN32 - return close(net_socket_to_native(sock)); -#endif /* OS_WIN32 */ -} - -static Socket sys_accept(void *_Nonnull obj, Socket sock) -{ - return net_socket_from_native(accept(net_socket_to_native(sock), nullptr, nullptr)); -} - -static int sys_bind(void *_Nonnull obj, Socket sock, const IP_Port *_Nonnull addr) -{ - Network_Addr naddr; - ip_port_to_network_addr(addr, &naddr); - if (naddr.size == 0) { - return -1; - } - return bind(net_socket_to_native(sock), (const struct sockaddr *)&naddr.addr, naddr.size); -} - -static int sys_listen(void *_Nonnull obj, Socket sock, int backlog) -{ - return listen(net_socket_to_native(sock), backlog); -} - -static int sys_connect(void *_Nonnull obj, Socket sock, const IP_Port *_Nonnull addr) -{ - Network_Addr naddr; - ip_port_to_network_addr(addr, &naddr); - if (naddr.size == 0) { - return -1; - } - return connect(net_socket_to_native(sock), (const struct sockaddr *)&naddr.addr, naddr.size); -} - -static int sys_recvbuf(void *_Nonnull obj, Socket sock) -{ -#ifdef OS_WIN32 - u_long count = 0; - ioctlsocket(net_socket_to_native(sock), FIONREAD, &count); -#else - int count = 0; - ioctl(net_socket_to_native(sock), FIONREAD, &count); -#endif /* OS_WIN32 */ - - return count; -} - -static int sys_recv(void *_Nonnull obj, Socket sock, uint8_t *_Nonnull buf, size_t len) -{ - return recv(net_socket_to_native(sock), (char *)buf, len, MSG_NOSIGNAL); -} - -static int sys_send(void *_Nonnull obj, Socket sock, const uint8_t *_Nonnull buf, size_t len) -{ - return send(net_socket_to_native(sock), (const char *)buf, len, MSG_NOSIGNAL); -} - -static int sys_sendto(void *_Nonnull obj, Socket sock, const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr) -{ - Network_Addr naddr; - ip_port_to_network_addr(addr, &naddr); - if (naddr.size == 0) { - return -1; - } - return sendto(net_socket_to_native(sock), (const char *)buf, len, 0, (const struct sockaddr *)&naddr.addr, naddr.size); -} - -static int sys_recvfrom(void *_Nonnull obj, Socket sock, uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr) -{ - Network_Addr naddr = {{0}}; - socklen_t addrlen = sizeof(naddr.addr); - const int ret = recvfrom(net_socket_to_native(sock), (char *)buf, len, 0, (struct sockaddr *)&naddr.addr, &addrlen); - naddr.size = addrlen; - if (ret >= 0) { - if (!network_addr_to_ip_port(&naddr, addr)) { - // Ignore packets from unknown families - return -1; - } - } - return ret; -} - -static Socket sys_socket(void *_Nonnull obj, int domain, int type, int proto) -{ - return net_socket_from_native(socket(domain, type, proto)); -} - -static int sys_socket_nonblock(void *_Nonnull obj, Socket sock, bool nonblock) -{ -#ifdef OS_WIN32 - u_long mode = nonblock ? 1 : 0; - return ioctlsocket(net_socket_to_native(sock), FIONBIO, &mode); -#else - return fcntl(net_socket_to_native(sock), F_SETFL, O_NONBLOCK, nonblock ? 1 : 0); -#endif /* OS_WIN32 */ -} - -static int sys_getsockopt(void *_Nonnull obj, Socket sock, int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen) -{ - socklen_t len = *optlen; - const int ret = getsockopt(net_socket_to_native(sock), level, optname, (char *)optval, &len); - *optlen = len; - return ret; -} - -static int sys_setsockopt(void *_Nonnull obj, Socket sock, int level, int optname, const void *_Nonnull optval, size_t optlen) -{ -#ifdef EMSCRIPTEN - return 0; -#else - return setsockopt(net_socket_to_native(sock), level, optname, (const char *)optval, optlen); -#endif /* EMSCRIPTEN */ -} - -// sets and fills an array of addrs for address -// returns the number of entries in addrs -static int sys_getaddrinfo(void *_Nonnull obj, const Memory *_Nonnull mem, const char *_Nonnull address, int family, int sock_type, IP_Port *_Nullable *_Nonnull addrs) -{ - assert(addrs != nullptr); - - struct addrinfo hints = {0}; - hints.ai_family = family; - - // different platforms favour a different field - // hints.ai_socktype = SOCK_DGRAM; // type of socket Tox uses. - hints.ai_socktype = sock_type; - // hints.ai_protocol = protocol; - - struct addrinfo *infos = nullptr; - - const int rc = getaddrinfo(address, nullptr, &hints, &infos); - - // Lookup failed. - if (rc != 0) { - // TODO(Green-Sky): log error - return 0; - } - - const int32_t max_count = INT32_MAX / sizeof(IP_Port); - - // we count number of "valid" results - int result = 0; - for (struct addrinfo *walker = infos; walker != nullptr && result < max_count; walker = walker->ai_next) { - if (walker->ai_family == family || family == AF_UNSPEC) { - ++result; - } - - // do we need to check socktype/protocol? - } - - assert(max_count >= result); - - IP_Port *tmp_addrs = (IP_Port *)mem_valloc(mem, result, sizeof(IP_Port)); - if (tmp_addrs == nullptr) { - freeaddrinfo(infos); - return 0; - } - - // now we fill in - int i = 0; - for (struct addrinfo *walker = infos; walker != nullptr; walker = walker->ai_next) { - if (walker->ai_family == family || family == AF_UNSPEC) { - Network_Addr naddr; - naddr.size = walker->ai_addrlen; - memcpy(&naddr.addr, walker->ai_addr, walker->ai_addrlen); - - if (network_addr_to_ip_port(&naddr, &tmp_addrs[i])) { - ++i; - } - } - } - - // Correct the count if conversion failed for some reason - result = i; - - freeaddrinfo(infos); - - *addrs = tmp_addrs; - - // number of entries in addrs - return result; -} - -static int sys_freeaddrinfo(void *_Nonnull obj, const Memory *_Nonnull mem, IP_Port *_Nonnull addrs) -{ - if (addrs == nullptr) { - return 0; - } - - mem_delete(mem, addrs); - - return 0; -} - -static const Network_Funcs os_network_funcs = { - sys_close, - sys_accept, - sys_bind, - sys_listen, - sys_connect, - sys_recvbuf, - sys_recv, - sys_recvfrom, - sys_send, - sys_sendto, - sys_socket, - sys_socket_nonblock, - sys_getsockopt, - sys_setsockopt, - sys_getaddrinfo, - sys_freeaddrinfo, -}; -static const Network os_network_obj = {&os_network_funcs, nullptr}; - -const Network *os_network(void) -{ -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if ((true)) { - return nullptr; - } -#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ -#ifdef OS_WIN32 - WSADATA wsaData; - - if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) { - return nullptr; - } -#endif /* OS_WIN32 */ - return &os_network_obj; -} - -#if 0 -/* TODO(iphydf): Call this from functions that use `os_network()`. */ -void os_network_deinit(const Network *ns) -{ -#ifdef OS_WIN32 - WSACleanup(); -#endif /* OS_WIN32 */ -} -#endif /* 0 */ - -static int net_setsockopt(const Network *_Nonnull ns, Socket sock, int level, int optname, const void *_Nonnull optval, size_t optlen) -{ - return ns->funcs->setsockopt(ns->obj, sock, level, optname, optval, optlen); -} - -static int net_getsockopt(const Network *_Nonnull ns, Socket sock, int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen) -{ - return ns->funcs->getsockopt(ns->obj, sock, level, optname, optval, optlen); -} - int net_send(const Network *ns, const Logger *log, Socket sock, const uint8_t *buf, size_t len, const IP_Port *ip_port, Net_Profile *net_profile) { - const int res = ns->funcs->send(ns->obj, sock, buf, len); + const int res = ns_send(ns, sock, buf, len); if (res > 0) { netprof_record_packet(net_profile, buf[0], res, PACKET_DIRECTION_SEND); @@ -786,82 +71,48 @@ int net_send(const Network *ns, const Logger *log, return res; } -static int net_sendto(const Network *_Nonnull ns, Socket sock, const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull ip_port) -{ - return ns->funcs->sendto(ns->obj, sock, buf, len, ip_port); -} - 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, buf, len); + const int res = ns_recv(ns, sock, buf, len); net_log_data(log, "=>T", buf, len, ip_port, res); return res; } -static int net_recvfrom(const Network *_Nonnull ns, Socket sock, uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr) -{ - return ns->funcs->recvfrom(ns->obj, sock, buf, len, addr); -} - int net_listen(const Network *ns, Socket sock, int backlog) { - return ns->funcs->listen(ns->obj, sock, backlog); -} - -static int net_bind(const Network *_Nonnull ns, Socket sock, const IP_Port *_Nonnull addr) -{ - return ns->funcs->bind(ns->obj, sock, addr); + return ns_listen(ns, sock, backlog); } Socket net_accept(const Network *ns, Socket sock) { - return ns->funcs->accept(ns->obj, sock); + return ns_accept(ns, sock); } /** Close the socket. */ void kill_sock(const Network *ns, Socket sock) { - ns->funcs->close(ns->obj, sock); + ns_close(ns, sock); } bool set_socket_nonblock(const Network *ns, Socket sock) { - return ns->funcs->socket_nonblock(ns->obj, sock, true) == 0; + return net_set_socket_nonblock(ns, sock); } 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 /* __APPLE__ */ + return net_set_socket_nosigpipe(ns, sock); } bool set_socket_reuseaddr(const Network *ns, Socket sock) { - int set = 1; -#if defined(OS_WIN32) - return net_setsockopt(ns, sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, &set, sizeof(set)) == 0; -#else - return net_setsockopt(ns, sock, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set)) == 0; -#endif /* OS_WIN32 */ + return net_set_socket_reuseaddr(ns, sock); } 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; + return net_set_socket_dualstack(ns, sock); } typedef struct Packet_Handler { @@ -938,7 +189,7 @@ int net_send_packet(const Networking_Core *net, const IP_Port *ip_port, Net_Pack ipp_copy.ip.ip.v6 = ip6; } - const long res = net_sendto(net->ns, net->sock, packet.data, packet.length, &ipp_copy); + const long res = ns_sendto(net->ns, net->sock, packet.data, packet.length, &ipp_copy); net_log_data(net->log, "O=>", packet.data, packet.length, ip_port, res); assert(res <= INT_MAX); @@ -971,12 +222,12 @@ static int receivepacket(const Network *_Nonnull ns, const Logger *_Nonnull log, memset(ip_port, 0, sizeof(IP_Port)); *length = 0; - const int fail_or_len = net_recvfrom(ns, sock, data, MAX_UDP_PACKET_SIZE, ip_port); + const int fail_or_len = ns_recvfrom(ns, sock, data, MAX_UDP_PACKET_SIZE, ip_port); if (fail_or_len < 0) { const int error = net_error(); - if (!should_ignore_recv_error(error)) { + if (!net_should_ignore_recv_error(error)) { Net_Strerror error_str; LOGGER_ERROR(log, "unexpected error reading from socket: %u, %s", (unsigned int)error, net_strerror(error, &error_str)); } @@ -1115,21 +366,13 @@ Networking_Core *new_networking_ex( /* Functions to increase the size of the send and receive UDP buffers. */ - int n = 1024 * 1024 * 2; - - if (net_setsockopt(ns, temp->sock, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) != 0) { - LOGGER_WARNING(log, "failed to set socket option %d", SO_RCVBUF); - } - - if (net_setsockopt(ns, temp->sock, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) != 0) { - LOGGER_WARNING(log, "failed to set socket option %d", SO_SNDBUF); + if (!net_set_socket_buffer_size(ns, temp->sock, 1024 * 1024 * 2)) { + LOGGER_WARNING(log, "failed to set socket buffer size"); } /* Enable broadcast on socket */ - int broadcast = 1; - - 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); + if (!net_set_socket_broadcast(ns, temp->sock)) { + LOGGER_ERROR(log, "failed to set socket broadcast"); } /* iOS UDP sockets are weird and apparently can SIGPIPE */ @@ -1177,25 +420,15 @@ Networking_Core *new_networking_ex( 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 = {{{{0}}}}; - mreq.ipv6mr_multiaddr.s6_addr[0] = 0xFF; - mreq.ipv6mr_multiaddr.s6_addr[1] = 0x02; - mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01; - mreq.ipv6mr_interface = 0; - - const int res = net_setsockopt(ns, temp->sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); - - const int neterror = net_error(); - Net_Strerror error_str; - - if (res < 0) { + if (!net_join_multicast(ns, temp->sock, ip->family)) { + const int neterror = net_error(); + Net_Strerror error_str; LOGGER_INFO(log, "Failed to activate local multicast membership in FF02::1. (%d, %s)", neterror, net_strerror(neterror, &error_str)); } else { + const int neterror = net_error(); + Net_Strerror error_str; LOGGER_TRACE(log, "Local multicast group joined successfully. (%d, %s)", neterror, net_strerror(neterror, &error_str)); } -#endif /* ESP_PLATFORM */ } /* A hanging program or a different user might block the standard port. @@ -1218,7 +451,7 @@ Networking_Core *new_networking_ex( *portptr = net_htons(port_to_try); for (uint16_t tries = port_from; tries <= port_to; ++tries) { - const int res = net_bind(ns, temp->sock, &addr); + const int res = ns_bind(ns, temp->sock, &addr); if (res == 0) { temp->port = *portptr; @@ -1231,7 +464,7 @@ Networking_Core *new_networking_ex( * binds with parallel clients yield a -EPERM to the outside if * errno isn't cleared here */ if (tries > 0) { - errno = 0; + net_clear_error(); } if (error != nullptr) { @@ -1305,11 +538,7 @@ bool ip_equal(const IP *a, const IP *b) /* same family */ if (a->family.value == b->family.value) { if (net_family_is_ipv4(a->family) || net_family_is_tcp_ipv4(a->family)) { - struct in_addr addr_a; - struct in_addr addr_b; - fill_addr4(&a->ip.v4, &addr_a); - fill_addr4(&b->ip.v4, &addr_b); - return addr_a.s_addr == addr_b.s_addr; + return a->ip.v4.uint32 == b->ip.v4.uint32; } if (net_family_is_ipv6(a->family) || net_family_is_tcp_ipv6(a->family)) { @@ -1323,15 +552,11 @@ bool ip_equal(const IP *a, const IP *b) /* different family: check on the IPv6 one if it is the IPv4 one embedded */ if (net_family_is_ipv4(a->family) && net_family_is_ipv6(b->family)) { if (ipv6_ipv4_in_v6(&b->ip.v6)) { - struct in_addr addr_a; - fill_addr4(&a->ip.v4, &addr_a); - return addr_a.s_addr == b->ip.v6.uint32[3]; + return a->ip.v4.uint32 == b->ip.v6.uint32[3]; } } else if (net_family_is_ipv6(a->family) && net_family_is_ipv4(b->family)) { if (ipv6_ipv4_in_v6(&a->ip.v6)) { - struct in_addr addr_b; - fill_addr4(&b->ip.v4, &addr_b); - return a->ip.v6.uint32[3] == addr_b.s_addr; + return a->ip.v6.uint32[3] == b->ip.v4.uint32; } } @@ -1657,17 +882,11 @@ bool ip_parse_addr(const IP *ip, char *address, size_t length) } if (net_family_is_ipv4(ip->family) || net_family_is_tcp_ipv4(ip->family)) { - struct in_addr addr; - assert(make_family(ip->family) == AF_INET); - fill_addr4(&ip->ip.v4, &addr); - return inet_ntop4(&addr, address, length) != nullptr; + return net_inet_ntop4(&ip->ip.v4, address, length) != nullptr; } if (net_family_is_ipv6(ip->family) || net_family_is_tcp_ipv6(ip->family)) { - struct in6_addr addr; - assert(make_family(ip->family) == AF_INET6); - fill_addr6(&ip->ip.v6, &addr); - return inet_ntop6(&addr, address, length) != nullptr; + return net_inet_ntop6(&ip->ip.v6, address, length) != nullptr; } return false; @@ -1679,19 +898,13 @@ bool addr_parse_ip(const char *address, IP *to) return false; } - struct in_addr addr4; - - if (inet_pton4(address, &addr4) == 1) { + if (net_inet_pton4(address, &to->ip.v4) == 1) { to->family = net_family_ipv4(); - get_ip4(&to->ip.v4, &addr4); return true; } - struct in6_addr addr6; - - if (inet_pton6(address, &addr6) == 1) { + if (net_inet_pton6(address, &to->ip.v6) == 1) { to->family = net_family_ipv6(); - get_ip6(&to->ip.v6, &addr6); return true; } @@ -1731,11 +944,10 @@ static bool addr_resolve(const Network *_Nonnull ns, const Memory *_Nonnull mem, return false; } - const Family tox_family = to->family; - const int family = make_family(tox_family); + const int family = to->family.value; IP_Port *addrs = nullptr; - const int rc = ns->funcs->getaddrinfo(ns->obj, mem, address, family, 0, &addrs); + const int rc = ns_getaddrinfo(ns, mem, address, family, 0, &addrs); // Lookup failed / empty. if (rc <= 0) { @@ -1774,7 +986,7 @@ static bool addr_resolve(const Network *_Nonnull ns, const Memory *_Nonnull mem, } } - if (family == AF_UNSPEC) { + if (family == TOX_AF_UNSPEC) { if ((result & TOX_ADDR_RESOLVE_INET6) != 0) { ip_copy(to, &ip6); @@ -1788,7 +1000,7 @@ static bool addr_resolve(const Network *_Nonnull ns, const Memory *_Nonnull mem, } } - ns->funcs->freeaddrinfo(ns->obj, mem, addrs); + ns_freeaddrinfo(ns, mem, addrs); return result != 0; } @@ -1835,13 +1047,13 @@ bool net_connect(const Network *ns, const Memory *mem, const Logger *log, Socket Ip_Ntoa ip_str; LOGGER_DEBUG(log, "connecting socket %d to %s:%d", net_socket_to_native(sock), net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port)); - errno = 0; + net_clear_error(); - if (ns->funcs->connect(ns->obj, sock, ip_port) == -1) { + if (ns_connect(ns, sock, ip_port) == -1) { const int error = net_error(); // Non-blocking socket: "Operation in progress" means it's connecting. - if (!should_ignore_connect_error(error)) { + if (!net_should_ignore_connect_error(error)) { Net_Strerror error_str; LOGGER_WARNING(log, "failed to connect to %s:%d: %d (%s)", net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port), error, net_strerror(error, &error_str)); @@ -1887,14 +1099,14 @@ int32_t net_getipport(const Network *ns, const Memory *mem, const char *node, IP abort(); } ip_port->ip.ip.v4.uint32 = net_htonl(0x7F000003); // 127.0.0.3 - ip_port->ip.family = *make_tox_family(AF_INET); + ip_port->ip.family = net_family_ipv4(); *res = ip_port; return 1; } #endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ - int type = make_socktype(tox_type); + int type = tox_type; // ugly if (tox_type == -1) { type = 0; @@ -1902,7 +1114,7 @@ int32_t net_getipport(const Network *ns, const Memory *mem, const char *node, IP // It's not an IP address, so now we try doing a DNS lookup. IP_Port *addrs = nullptr; - const int rc = ns->funcs->getaddrinfo(ns->obj, mem, node, AF_UNSPEC, type, &addrs); + const int rc = ns_getaddrinfo(ns, mem, node, TOX_AF_UNSPEC, type, &addrs); // Lookup failed / empty. if (rc <= 0) { @@ -1934,166 +1146,25 @@ bool bind_to_port(const Network *ns, Socket sock, Family family, uint16_t port) addr.port = net_htons(port); - return net_bind(ns, sock, &addr) == 0; + return ns_bind(ns, sock, &addr) == 0; } Socket net_socket(const Network *ns, Family domain, int type, int protocol) { - const int platform_domain = make_family(domain); - const int platform_type = make_socktype(type); - const int platform_prot = make_proto(protocol); - return ns->funcs->socket(ns->obj, platform_domain, platform_type, platform_prot); + return ns_socket(ns, domain.value, type, protocol); } uint16_t net_socket_data_recv_buffer(const Network *ns, Socket sock) { - const int count = ns->funcs->recvbuf(ns->obj, sock); + const int count = ns_recvbuf(ns, sock); return (uint16_t)max_s32(0, min_s32(count, UINT16_MAX)); } -uint32_t net_htonl(uint32_t hostlong) -{ - return htonl(hostlong); -} - -uint16_t net_htons(uint16_t hostshort) -{ - return htons(hostshort); -} - -uint32_t net_ntohl(uint32_t hostlong) -{ - return ntohl(hostlong); -} - -uint16_t net_ntohs(uint16_t hostshort) -{ - return ntohs(hostshort); -} - -size_t net_pack_bool(uint8_t *bytes, bool v) -{ - bytes[0] = v ? 1 : 0; - return 1; -} - -size_t net_pack_u16(uint8_t *bytes, uint16_t v) -{ - bytes[0] = (v >> 8) & 0xff; - bytes[1] = v & 0xff; - return sizeof(v); -} - -size_t net_pack_u32(uint8_t *bytes, uint32_t v) -{ - uint8_t *p = bytes; - p += net_pack_u16(p, (v >> 16) & 0xffff); - p += net_pack_u16(p, v & 0xffff); - return p - bytes; -} - -size_t net_pack_u64(uint8_t *bytes, uint64_t v) -{ - uint8_t *p = bytes; - p += net_pack_u32(p, (v >> 32) & 0xffffffff); - p += net_pack_u32(p, v & 0xffffffff); - return p - bytes; -} - -size_t net_unpack_bool(const uint8_t *bytes, bool *v) -{ - *v = bytes[0] != 0; - return 1; -} - -size_t net_unpack_u16(const uint8_t *bytes, uint16_t *v) -{ - const uint8_t hi = bytes[0]; - const uint8_t lo = bytes[1]; - *v = ((uint16_t)hi << 8) | lo; - return sizeof(*v); -} - -size_t net_unpack_u32(const uint8_t *bytes, uint32_t *v) -{ - const uint8_t *p = bytes; - uint16_t hi; - uint16_t lo; - p += net_unpack_u16(p, &hi); - p += net_unpack_u16(p, &lo); - *v = ((uint32_t)hi << 16) | lo; - return p - bytes; -} - -size_t net_unpack_u64(const uint8_t *bytes, uint64_t *v) -{ - const uint8_t *p = bytes; - uint32_t hi; - uint32_t lo; - p += net_unpack_u32(p, &hi); - p += net_unpack_u32(p, &lo); - *v = ((uint64_t)hi << 32) | lo; - return p - bytes; -} - bool ipv6_ipv4_in_v6(const IP6 *a) { return a->uint64[0] == 0 && a->uint32[2] == net_htonl(0xffff); } -int net_error(void) -{ -#ifdef OS_WIN32 - return WSAGetLastError(); -#else - return errno; -#endif /* OS_WIN32 */ -} - -#ifdef OS_WIN32 -char *net_strerror(int error, Net_Strerror *buf) -{ - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, - error, 0, buf->data, NET_STRERROR_SIZE, nullptr); - return buf->data; -} -#else -#if defined(_GNU_SOURCE) && defined(__GLIBC__) -static const char *net_strerror_r(int error, char *_Nonnull tmp, size_t tmp_size) -{ - const char *retstr = strerror_r(error, tmp, tmp_size); - - if (errno != 0) { - snprintf(tmp, tmp_size, "error %d (strerror_r failed with errno %d)", error, errno); - } - - return retstr; -} -#else -static const char *net_strerror_r(int error, char *_Nonnull tmp, size_t tmp_size) -{ - const int fmt_error = strerror_r(error, tmp, tmp_size); - - if (fmt_error != 0) { - snprintf(tmp, tmp_size, "error %d (strerror_r failed with error %d, errno %d)", error, fmt_error, errno); - } - - return tmp; -} -#endif /* GNU */ -char *net_strerror(int error, Net_Strerror *buf) -{ - errno = 0; - - const char *retstr = net_strerror_r(error, buf->data, NET_STRERROR_SIZE); - const size_t retstr_len = strlen(retstr); - assert(retstr_len < NET_STRERROR_SIZE); - buf->size = (uint16_t)retstr_len; - - return buf->data; -} -#endif /* OS_WIN32 */ - const Net_Profile *net_get_net_profile(const Networking_Core *net) { if (net == nullptr) { diff --git a/toxcore/network.h b/toxcore/network.h index 37c5cdfc..f44f9eac 100644 --- a/toxcore/network.h +++ b/toxcore/network.h @@ -17,126 +17,14 @@ #include "bin_pack.h" #include "logger.h" #include "mem.h" +#include "net.h" #include "net_profile.h" +#include "os_network.h" #ifdef __cplusplus extern "C" { #endif -typedef bitwise int Socket_Value; -typedef struct Socket { - Socket_Value value; -} Socket; - -#define SIZE_IP4 4 -#define SIZE_IP6 16 -#define SIZE_IP (1 + SIZE_IP6) -#define SIZE_PORT 2 -#define SIZE_IPPORT (SIZE_IP + SIZE_PORT) - -typedef struct Family { - uint8_t value; -} Family; - -typedef union IP4 { - uint32_t uint32; - uint16_t uint16[2]; - uint8_t uint8[4]; -} IP4; - -typedef union IP6 { - uint8_t uint8[16]; - uint16_t uint16[8]; - uint32_t uint32[4]; - uint64_t uint64[2]; -} IP6; - -typedef union IP_Union { - IP4 v4; - IP6 v6; -} IP_Union; - -typedef struct IP { - Family family; - IP_Union ip; -} IP; - -typedef struct IP_Port { - IP ip; - uint16_t port; -} IP_Port; - -int net_socket_to_native(Socket sock); -Socket net_socket_from_native(int sock); - -typedef int net_close_cb(void *_Nullable obj, Socket sock); -typedef Socket net_accept_cb(void *_Nullable obj, Socket sock); -typedef int net_bind_cb(void *_Nullable obj, Socket sock, const IP_Port *_Nonnull addr); -typedef int net_listen_cb(void *_Nullable obj, Socket sock, int backlog); -typedef int net_connect_cb(void *_Nullable obj, Socket sock, const IP_Port *_Nonnull addr); -typedef int net_recvbuf_cb(void *_Nullable obj, Socket sock); -typedef int net_recv_cb(void *_Nullable obj, Socket sock, uint8_t *_Nonnull buf, size_t len); -typedef int net_recvfrom_cb(void *_Nullable obj, Socket sock, uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr); -typedef int net_send_cb(void *_Nullable obj, Socket sock, const uint8_t *_Nonnull buf, size_t len); -typedef int net_sendto_cb(void *_Nullable obj, Socket sock, const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr); -typedef Socket net_socket_cb(void *_Nullable obj, int domain, int type, int proto); -typedef int net_socket_nonblock_cb(void *_Nullable obj, Socket sock, bool nonblock); -typedef int net_getsockopt_cb(void *_Nullable obj, Socket sock, int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen); -typedef int net_setsockopt_cb(void *_Nullable obj, Socket sock, int level, int optname, const void *_Nonnull optval, size_t optlen); -typedef int net_getaddrinfo_cb(void *_Nullable obj, const Memory *_Nonnull mem, const char *_Nonnull address, int family, int protocol, IP_Port *_Nullable *_Nonnull addrs); -typedef int net_freeaddrinfo_cb(void *_Nullable obj, const Memory *_Nonnull mem, IP_Port *_Nullable 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 *_Nullable close; - net_accept_cb *_Nullable accept; - net_bind_cb *_Nullable bind; - net_listen_cb *_Nullable listen; - net_connect_cb *_Nullable connect; - net_recvbuf_cb *_Nullable recvbuf; - net_recv_cb *_Nullable recv; - net_recvfrom_cb *_Nullable recvfrom; - net_send_cb *_Nullable send; - net_sendto_cb *_Nullable sendto; - net_socket_cb *_Nullable socket; - net_socket_nonblock_cb *_Nullable socket_nonblock; - net_getsockopt_cb *_Nullable getsockopt; - net_setsockopt_cb *_Nullable setsockopt; - net_getaddrinfo_cb *_Nullable getaddrinfo; - net_freeaddrinfo_cb *_Nullable freeaddrinfo; -} Network_Funcs; - -typedef struct Network { - const Network_Funcs *_Nullable funcs; - void *_Nullable obj; -} Network; - -const Network *_Nullable os_network(void); - -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_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); - -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 typedef enum Net_Packet_Type { @@ -192,25 +80,6 @@ typedef enum Net_Packet_Type { #define TOX_PORTRANGE_TO 33545 #define TOX_PORT_DEFAULT TOX_PORTRANGE_FROM -/** Redefinitions of variables for safe transfer over wire. */ -#define TOX_AF_UNSPEC 0 -#define TOX_AF_INET 2 -#define TOX_AF_INET6 10 -#define TOX_TCP_INET 130 -#define TOX_TCP_INET6 138 - -#define TOX_SOCK_STREAM 1 -#define TOX_SOCK_DGRAM 2 - -#define TOX_PROTO_TCP 1 -#define TOX_PROTO_UDP 2 - -/** TCP related */ -#define TCP_CLIENT_FAMILY (TOX_AF_INET6 + 1) -#define TCP_INET (TOX_AF_INET6 + 2) -#define TCP_INET6 (TOX_AF_INET6 + 3) -#define TCP_SERVER_FAMILY (TOX_AF_INET6 + 4) - IP4 get_ip4_loopback(void); IP4 get_ip4_broadcast(void); @@ -226,8 +95,6 @@ Socket net_socket(const Network *_Nonnull ns, Family domain, int type, int proto */ bool sock_valid(Socket sock); -Socket net_invalid_socket(void); - /** * Calls send(sockfd, buf, len, MSG_NOSIGNAL). * @@ -267,22 +134,6 @@ Socket net_accept(const Network *_Nonnull ns, Socket sock); */ uint16_t net_socket_data_recv_buffer(const Network *_Nonnull ns, Socket sock); -/** 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); - -size_t net_pack_bool(uint8_t *_Nonnull bytes, bool v); -size_t net_pack_u16(uint8_t *_Nonnull bytes, uint16_t v); -size_t net_pack_u32(uint8_t *_Nonnull bytes, uint32_t v); -size_t net_pack_u64(uint8_t *_Nonnull bytes, uint64_t v); - -size_t net_unpack_bool(const uint8_t *_Nonnull bytes, bool *_Nonnull v); -size_t net_unpack_u16(const uint8_t *_Nonnull bytes, uint16_t *_Nonnull v); -size_t net_unpack_u32(const uint8_t *_Nonnull bytes, uint32_t *_Nonnull v); -size_t net_unpack_u64(const uint8_t *_Nonnull bytes, uint64_t *_Nonnull v); - /** Does the IP6 struct a contain an IPv4 address in an IPv6 one? */ bool ipv6_ipv4_in_v6(const IP6 *_Nonnull a); @@ -531,40 +382,6 @@ int unpack_ip_port(IP_Port *_Nonnull ip_port, const uint8_t *_Nonnull data, uint */ bool bind_to_port(const Network *_Nonnull ns, Socket sock, Family family, uint16_t port); -/** @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. - * - * 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_strerror()` to print the error. - * - * return platform-dependent network error code, if any. - */ -int net_error(void); - -#define NET_STRERROR_SIZE 256 - -/** @brief Contains a null terminated formatted error message. - * - * This struct should not contain more than at most the 2 fields. - */ -typedef struct Net_Strerror { - char data[NET_STRERROR_SIZE]; - uint16_t size; -} Net_Strerror; - -/** @brief Get a text explanation for the error code from `net_error()`. - * - * @param error The error code to get a string for. - * @param buf The struct to store the error message in (usually on stack). - * - * @return pointer to a NULL-terminated string describing the error code. - */ -char *_Nonnull net_strerror(int error, Net_Strerror *_Nonnull buf); - /** @brief Initialize networking. * Bind to ip and port. * ip must be in network order EX: 127.0.0.1 = (7F000001). diff --git a/toxcore/network_test.cc b/toxcore/network_test.cc index da88fb6c..19742161 100644 --- a/toxcore/network_test.cc +++ b/toxcore/network_test.cc @@ -5,6 +5,8 @@ #include +#include + #include "network_test_util.hh" namespace { @@ -106,7 +108,7 @@ TEST(IpportCmp, BehavesLikeMemcmp) << "b=" << b; EXPECT_EQ( // ipport_cmp_handler(&a, &b, sizeof(IP_Port)), // - cmp_val(memcmp(&a, &b, sizeof(IP_Port)))) + cmp_val(std::memcmp(&a, &b, sizeof(IP_Port)))) << "a=" << a << "\n" << "b=" << b; @@ -119,7 +121,7 @@ TEST(IpportCmp, BehavesLikeMemcmp) << "b=" << b; EXPECT_EQ( // ipport_cmp_handler(&a, &b, sizeof(IP_Port)), // - cmp_val(memcmp(&a, &b, sizeof(IP_Port)))) + cmp_val(std::memcmp(&a, &b, sizeof(IP_Port)))) << "a=" << a << "\n" << "b=" << b; } diff --git a/toxcore/network_test_util.cc b/toxcore/network_test_util.cc index 6f74f35b..d6b10bb5 100644 --- a/toxcore/network_test_util.cc +++ b/toxcore/network_test_util.cc @@ -1,6 +1,7 @@ #include "network_test_util.hh" #include +#include #include "crypto_core.h" #include "mem.h" @@ -20,15 +21,12 @@ IP_Port increasing_ip_port::operator()() return ip_port; } -IP_Port random_ip_port(const Random *rng) +IP_Port random_ip_port(const Random *_Nonnull rng) { IP_Port ip_port; - ip_port.ip.family = net_family_ipv4(); - ip_port.ip.ip.v4.uint8[0] = 192; - ip_port.ip.ip.v4.uint8[1] = 168; - ip_port.ip.ip.v4.uint8[2] = 0; - ip_port.ip.ip.v4.uint8[3] = random_u08(rng); - ip_port.port = random_u16(rng); + ip_init(&ip_port.ip, false); + ip_port.ip.ip.v4.uint32 = random_u32(rng); + ip_port.port = net_htons(random_u16(rng)); return ip_port; } diff --git a/toxcore/network_test_util.hh b/toxcore/network_test_util.hh index 620cc285..c4115e6f 100644 --- a/toxcore/network_test_util.hh +++ b/toxcore/network_test_util.hh @@ -1,24 +1,28 @@ #ifndef C_TOXCORE_TOXCORE_NETWORK_TEST_UTIL_H #define C_TOXCORE_TOXCORE_NETWORK_TEST_UTIL_H +#include #include +#include "attributes.h" #include "crypto_core.h" #include "mem.h" +#include "net.h" #include "network.h" +#include "rng.h" #include "test_util.hh" template <> struct Deleter : Function_Deleter { }; -IP_Port random_ip_port(const Random *rng); +IP_Port random_ip_port(const Random *_Nonnull rng); class increasing_ip_port { - uint8_t start_; - const Random *rng_; + std::uint8_t start_; + const Random *_Nonnull rng_; public: - explicit increasing_ip_port(uint8_t start, const Random *rng) + explicit increasing_ip_port(std::uint8_t start, const Random *_Nonnull rng) : start_(start) , rng_(rng) { diff --git a/toxcore/onion.h b/toxcore/onion.h index 6c967ba8..2c0bcfd6 100644 --- a/toxcore/onion.h +++ b/toxcore/onion.h @@ -9,15 +9,23 @@ #ifndef C_TOXCORE_TOXCORE_ONION_H #define C_TOXCORE_TOXCORE_ONION_H +#include + #include "DHT.h" #include "attributes.h" #include "crypto_core.h" #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "network.h" +#include "rng.h" #include "shared_key_cache.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef int onion_recv_1_cb(void *_Nullable object, const IP_Port *_Nonnull dest, const uint8_t *_Nonnull data, uint16_t length); typedef struct Onion { @@ -143,4 +151,9 @@ void set_callback_handle_recv_1(Onion *_Nonnull onion, onion_recv_1_cb *_Nullabl Onion *_Nullable new_onion(const Logger *_Nonnull log, const Memory *_Nonnull mem, const Mono_Time *_Nonnull mono_time, const Random *_Nonnull rng, DHT *_Nonnull dht, Networking_Core *_Nonnull net); void kill_onion(Onion *_Nullable onion); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif /* C_TOXCORE_TOXCORE_ONION_H */ diff --git a/toxcore/onion_announce.h b/toxcore/onion_announce.h index fca6798c..5551d5c0 100644 --- a/toxcore/onion_announce.h +++ b/toxcore/onion_announce.h @@ -9,12 +9,15 @@ #ifndef C_TOXCORE_TOXCORE_ONION_ANNOUNCE_H #define C_TOXCORE_TOXCORE_ONION_ANNOUNCE_H +#include + #include "DHT.h" #include "attributes.h" #include "crypto_core.h" #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "network.h" #include "onion.h" #include "timed_auth.h" @@ -41,6 +44,10 @@ #define ONION_DATA_REQUEST_MIN_SIZE (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE) #define MAX_DATA_REQUEST_SIZE (ONION_MAX_DATA_SIZE - ONION_DATA_REQUEST_MIN_SIZE) +#ifdef __cplusplus +extern "C" { +#endif + typedef struct Onion_Announce Onion_Announce; /** These two are not public; they are for tests only! */ @@ -128,4 +135,9 @@ Onion_Announce *_Nullable new_onion_announce(const Logger *_Nonnull log, const M Networking_Core *_Nonnull net); void kill_onion_announce(Onion_Announce *_Nullable onion_a); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif /* C_TOXCORE_TOXCORE_ONION_ANNOUNCE_H */ diff --git a/toxcore/onion_client.c b/toxcore/onion_client.c index 1aa42d25..362997fd 100644 --- a/toxcore/onion_client.c +++ b/toxcore/onion_client.c @@ -959,7 +959,7 @@ static int handle_announce_response(void *_Nonnull object, const IP_Port *_Nonnu } uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE] = {0}; - IP_Port ip_port = {0}; + IP_Port ip_port = {{{0}}}; uint32_t path_num = 0; const uint32_t num = check_sendback(onion_c, packet + 1, public_key, &ip_port, &path_num); @@ -1066,7 +1066,7 @@ static int handle_announce_response_old(void *_Nonnull object, const IP_Port *_N const uint16_t len_nodes = length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE; uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE] = {0}; - IP_Port ip_port = {0}; + IP_Port ip_port = {{{0}}}; uint32_t path_num = 0; const uint32_t num = check_sendback(onion_c, packet + 1, public_key, &ip_port, &path_num); diff --git a/toxcore/onion_client.h b/toxcore/onion_client.h index 5b7a090b..0ab321c5 100644 --- a/toxcore/onion_client.h +++ b/toxcore/onion_client.h @@ -11,6 +11,7 @@ #define C_TOXCORE_TOXCORE_ONION_CLIENT_H #include +#include #include "DHT.h" #include "attributes.h" @@ -18,6 +19,7 @@ #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "net_crypto.h" #include "network.h" #include "onion_announce.h" diff --git a/toxcore/onion_client_fuzz_test.cc b/toxcore/onion_client_fuzz_test.cc index d0a5b815..4481b154 100644 --- a/toxcore/onion_client_fuzz_test.cc +++ b/toxcore/onion_client_fuzz_test.cc @@ -10,9 +10,11 @@ #include "../testing/support/public/fuzz_data.hh" #include "../testing/support/public/simulated_environment.hh" #include "DHT.h" +#include "attributes.h" #include "net_crypto.h" #include "net_profile.h" #include "network.h" +#include "test_util.hh" namespace { @@ -32,12 +34,12 @@ T consume_range(Fuzz_Data &input, T min, T max) // Minimal DHT wrapper for fuzzing class FuzzDHT { public: - FuzzDHT(SimulatedEnvironment &env, uint16_t port) + FuzzDHT(SimulatedEnvironment &env, std::uint16_t port) : node_(env.create_node(port)) , logger_(logger_new(&node_->c_memory), [](Logger *l) { logger_kill(l); }) , mono_time_(mono_time_new( &node_->c_memory, - [](void *ud) -> uint64_t { + [](void *ud) -> std::uint64_t { return static_cast(ud)->current_time_ms(); }, &env.fake_clock()), @@ -57,12 +59,12 @@ public: mono_time_.get(), networking_.get(), true, true)); } - DHT *get_dht() { return dht_.get(); } - Networking_Core *networking() { return networking_.get(); } - Mono_Time *mono_time() { return mono_time_.get(); } - Logger *logger() { return logger_.get(); } + DHT *_Nonnull get_dht() { return REQUIRE_NOT_NULL(dht_.get()); } + Networking_Core *_Nonnull networking() { return REQUIRE_NOT_NULL(networking_.get()); } + Mono_Time *_Nonnull mono_time() { return REQUIRE_NOT_NULL(mono_time_.get()); } + Logger *_Nonnull logger() { return REQUIRE_NOT_NULL(logger_.get()); } tox::test::ScopedToxSystem &node() { return *node_; } - FakeUdpSocket *endpoint() { return node_->endpoint; } + FakeUdpSocket *_Nullable endpoint() { return node_->endpoint; } static const Net_Crypto_DHT_Funcs funcs; @@ -75,11 +77,11 @@ private: }; const Net_Crypto_DHT_Funcs FuzzDHT::funcs = { - [](void *obj, const uint8_t *public_key) { + [](void *_Nonnull obj, const std::uint8_t *_Nonnull public_key) { return dht_get_shared_key_sent(static_cast(obj), public_key); }, - [](const void *obj) { return dht_get_self_public_key(static_cast(obj)); }, - [](const void *obj) { return dht_get_self_secret_key(static_cast(obj)); }, + [](const void *_Nonnull obj) { return dht_get_self_public_key(static_cast(obj)); }, + [](const void *_Nonnull obj) { return dht_get_self_secret_key(static_cast(obj)); }, }; class OnionClientFuzzer { @@ -104,7 +106,7 @@ public: // Register a handler for onion data to verify reception oniondata_registerhandler( onion_client_.get(), 0, - [](void *, const uint8_t *, const uint8_t *, uint16_t, void *) { + [](void *, const std::uint8_t *, const std::uint8_t *, std::uint16_t, void *) { // Callback hit return 0; }, @@ -124,7 +126,7 @@ public: private: void Action(Fuzz_Data &input) { - uint8_t op = input.consume_integral(); + std::uint8_t op = input.consume_integral(); switch (op % 12) { case 0: AddFriend(input); @@ -167,15 +169,15 @@ private: void AddFriend(Fuzz_Data &input) { - uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(&dht_.node().c_random, pk, sk); int friend_num = onion_addfriend(onion_client_.get(), pk); if (friend_num != -1) { friends_.push_back(friend_num); - friend_keys_[friend_num] = {std::vector(pk, pk + CRYPTO_PUBLIC_KEY_SIZE), - std::vector(sk, sk + CRYPTO_SECRET_KEY_SIZE)}; + friend_keys_[friend_num] = {std::vector(pk, pk + CRYPTO_PUBLIC_KEY_SIZE), + std::vector(sk, sk + CRYPTO_SECRET_KEY_SIZE)}; } } @@ -183,7 +185,7 @@ private: { if (friends_.empty()) return; - size_t idx = consume_range(input, 0, friends_.size() - 1); + std::size_t idx = consume_range(input, 0, friends_.size() - 1); int friend_num = friends_[idx]; onion_delfriend(onion_client_.get(), friend_num); friends_.erase(friends_.begin() + idx); @@ -194,7 +196,7 @@ private: { if (friends_.empty()) return; - int friend_num = friends_[consume_range(input, 0, friends_.size() - 1)]; + int friend_num = friends_[consume_range(input, 0, friends_.size() - 1)]; bool online = input.consume_integral(); onion_set_friend_online(onion_client_.get(), friend_num, online); } @@ -203,8 +205,8 @@ private: { if (friends_.empty()) return; - int friend_num = friends_[consume_range(input, 0, friends_.size() - 1)]; - CONSUME_OR_RETURN(const uint8_t *pk, input, CRYPTO_PUBLIC_KEY_SIZE); + int friend_num = friends_[consume_range(input, 0, friends_.size() - 1)]; + CONSUME_OR_RETURN(const std::uint8_t *pk, input, CRYPTO_PUBLIC_KEY_SIZE); onion_set_friend_dht_pubkey(onion_client_.get(), friend_num, pk); } @@ -212,8 +214,8 @@ private: { IP_Port ip_port; ip_init(&ip_port.ip, 1); - ip_port.port = input.consume_integral(); - CONSUME_OR_RETURN(const uint8_t *pk, input, CRYPTO_PUBLIC_KEY_SIZE); + ip_port.port = input.consume_integral(); + CONSUME_OR_RETURN(const std::uint8_t *pk, input, CRYPTO_PUBLIC_KEY_SIZE); onion_add_bs_path_node(onion_client_.get(), &ip_port, pk); } @@ -222,30 +224,30 @@ private: if (input.remaining_bytes() < 1) return; - std::vector packet; - uint8_t type = input.consume_integral(); + std::vector packet; + std::uint8_t type = input.consume_integral(); if (type < 50) { - size_t size = consume_range(input, 10, 500); + std::size_t size = consume_range(input, 10, 500); if (input.remaining_bytes() >= size) { - const uint8_t *ptr = input.consume("ReceivePacket", size); + const std::uint8_t *ptr = input.consume("ReceivePacket", size); if (ptr) packet.assign(ptr, ptr + size); } } else if (type >= 50 && type < 150) { // Generate valid NET_PACKET_ANNOUNCE_RESPONSE - uint8_t secret_key[CRYPTO_SYMMETRIC_KEY_SIZE]; + std::uint8_t secret_key[CRYPTO_SYMMETRIC_KEY_SIZE]; onion_testonly_get_secret_symmetric_key(onion_client_.get(), secret_key); - uint8_t nonce[CRYPTO_NONCE_SIZE]; + std::uint8_t nonce[CRYPTO_NONCE_SIZE]; random_bytes(&dht_.node().c_random, nonce, sizeof(nonce)); - size_t data_len = consume_range(input, 1, 100); - std::vector plaintext = input.consume_bytes(data_len); + std::size_t data_len = consume_range(input, 1, 100); + std::vector plaintext = input.consume_bytes(data_len); if (plaintext.empty()) plaintext = {1, 2, 3}; // fallback - std::vector ciphertext(plaintext.size() + CRYPTO_MAC_SIZE); + std::vector ciphertext(plaintext.size() + CRYPTO_MAC_SIZE); int len = encrypt_data_symmetric(&dht_.node().c_memory, secret_key, nonce, plaintext.data(), plaintext.size(), ciphertext.data()); @@ -257,25 +259,25 @@ private: } else if (type >= 150 && !friends_.empty()) { // Valid onion data response injection - int friend_num = friends_[consume_range(input, 0, friends_.size() - 1)]; + int friend_num = friends_[consume_range(input, 0, friends_.size() - 1)]; const auto &keys = friend_keys_[friend_num]; - uint8_t sender_temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t sender_temp_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t sender_temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t sender_temp_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(&dht_.node().c_random, sender_temp_pk, sender_temp_sk); - uint8_t nonce[CRYPTO_NONCE_SIZE]; + std::uint8_t nonce[CRYPTO_NONCE_SIZE]; random_bytes(&dht_.node().c_random, nonce, sizeof(nonce)); // Inner packet - Let fuzzer choose type - uint8_t inner_type = input.consume_integral(); - std::vector inner_data = {inner_type}; + std::uint8_t inner_type = input.consume_integral(); + std::vector inner_data = {inner_type}; - size_t data_len = consume_range(input, 1, 100); - std::vector rand_data = input.consume_bytes(data_len); + std::size_t data_len = consume_range(input, 1, 100); + std::vector rand_data = input.consume_bytes(data_len); inner_data.insert(inner_data.end(), rand_data.begin(), rand_data.end()); - std::vector inner_ciphertext(inner_data.size() + CRYPTO_MAC_SIZE); + std::vector inner_ciphertext(inner_data.size() + CRYPTO_MAC_SIZE); int len = encrypt_data(&dht_.node().c_memory, dht_get_self_public_key(dht_.get_dht()), keys.second.data(), nonce, inner_data.data(), inner_data.size(), inner_ciphertext.data()); @@ -283,15 +285,16 @@ private: return; // Outer packet content: Sender Real PK + Inner Ciphertext - std::vector outer_plaintext(CRYPTO_PUBLIC_KEY_SIZE + inner_ciphertext.size()); - memcpy(outer_plaintext.data(), keys.first.data(), CRYPTO_PUBLIC_KEY_SIZE); - memcpy(outer_plaintext.data() + CRYPTO_PUBLIC_KEY_SIZE, inner_ciphertext.data(), + std::vector outer_plaintext( + CRYPTO_PUBLIC_KEY_SIZE + inner_ciphertext.size()); + std::memcpy(outer_plaintext.data(), keys.first.data(), CRYPTO_PUBLIC_KEY_SIZE); + std::memcpy(outer_plaintext.data() + CRYPTO_PUBLIC_KEY_SIZE, inner_ciphertext.data(), inner_ciphertext.size()); - uint8_t receiver_temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t receiver_temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; onion_testonly_get_temp_public_key(onion_client_.get(), receiver_temp_pk); - std::vector outer_ciphertext(outer_plaintext.size() + CRYPTO_MAC_SIZE); + std::vector outer_ciphertext(outer_plaintext.size() + CRYPTO_MAC_SIZE); len = encrypt_data(&dht_.node().c_memory, receiver_temp_pk, sender_temp_sk, nonce, outer_plaintext.data(), outer_plaintext.size(), outer_ciphertext.data()); if (len == -1) @@ -318,7 +321,7 @@ private: void AdvanceTime(Fuzz_Data &input) { - uint32_t ms = input.consume_integral_in_range(1, 10000); + std::uint32_t ms = input.consume_integral_in_range(1, 10000); env_.fake_clock().advance(ms); } @@ -326,11 +329,11 @@ private: { if (friends_.empty()) return; - int friend_num = friends_[consume_range(input, 0, friends_.size() - 1)]; + int friend_num = friends_[consume_range(input, 0, friends_.size() - 1)]; - uint16_t length = consume_range(input, 1, 1024); + std::uint16_t length = consume_range(input, 1, 1024); if (input.remaining_bytes() >= length) { - const uint8_t *ptr = input.consume("SendData", length); + const std::uint8_t *ptr = input.consume("SendData", length); if (ptr) send_onion_data(onion_client_.get(), friend_num, ptr, length); } @@ -340,7 +343,7 @@ private: { if (friends_.empty()) return; - int friend_num = friends_[consume_range(input, 0, friends_.size() - 1)]; + int friend_num = friends_[consume_range(input, 0, friends_.size() - 1)]; IP_Port ip_port; onion_getfriendip(onion_client_.get(), friend_num, &ip_port); } @@ -357,11 +360,13 @@ private: { if (friends_.empty()) return; - int friend_num = friends_[input.consume_integral_in_range(0, friends_.size() - 1)]; + int friend_num + = friends_[input.consume_integral_in_range(0, friends_.size() - 1)]; // Just setting a dummy callback recv_tcp_relay_handler( onion_client_.get(), friend_num, - [](void *, uint32_t, const IP_Port *, const uint8_t *) { return 0; }, this, 0); + [](void *, std::uint32_t, const IP_Port *, const std::uint8_t *) { return 0; }, this, + 0); } SimulatedEnvironment &env_; @@ -370,13 +375,13 @@ private: std::unique_ptr net_crypto_; std::unique_ptr onion_client_; std::vector friends_; - std::map, std::vector>> friend_keys_; + std::map, std::vector>> friend_keys_; }; } // namespace -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size); +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { if (size == 0) return 0; diff --git a/toxcore/onion_client_test.cc b/toxcore/onion_client_test.cc index 3aa09fd1..7c27777b 100644 --- a/toxcore/onion_client_test.cc +++ b/toxcore/onion_client_test.cc @@ -7,12 +7,15 @@ #include #include #include +#include #include #include +#include #include #include "../testing/support/public/simulated_environment.hh" #include "DHT_test_util.hh" +#include "attributes.h" #include "crypto_core.h" #include "logger.h" #include "mono_time.h" @@ -31,12 +34,14 @@ using namespace tox::test; template class OnionTestNode { public: - OnionTestNode(SimulatedEnvironment &env, uint16_t port) + OnionTestNode(SimulatedEnvironment &env, std::uint16_t port) : dht_wrapper_(env, port) , net_profile_(netprof_new(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory), [mem = &dht_wrapper_.node().c_memory](Net_Profile *p) { netprof_kill(mem, p); }) , net_crypto_(nullptr, [](Net_Crypto *c) { kill_net_crypto(c); }) , onion_client_(nullptr, [](Onion_Client *c) { kill_onion_client(c); }) + , onion_server_(nullptr, [](Onion *o) { kill_onion(o); }) + , onion_announce_(nullptr, [](Onion_Announce *a) { kill_onion_announce(a); }) { // Setup NetCrypto TCP_Proxy_Info proxy_info = {{0}, TCP_PROXY_NONE}; @@ -49,23 +54,43 @@ public: onion_client_.reset(new_onion_client(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory, &dht_wrapper_.node().c_random, dht_wrapper_.mono_time(), net_crypto_.get(), dht_wrapper_.get_dht(), dht_wrapper_.networking())); + + // Setup Onion Server + onion_server_.reset(new_onion(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory, + dht_wrapper_.mono_time(), &dht_wrapper_.node().c_random, dht_wrapper_.get_dht(), + dht_wrapper_.networking())); + + // Setup Onion Announce + onion_announce_.reset(new_onion_announce(dht_wrapper_.logger(), + &dht_wrapper_.node().c_memory, &dht_wrapper_.node().c_random, dht_wrapper_.mono_time(), + dht_wrapper_.get_dht(), dht_wrapper_.networking())); } - Onion_Client *get_onion_client() { return onion_client_.get(); } - Net_Crypto *get_net_crypto() { return net_crypto_.get(); } - DHT *get_dht() { return dht_wrapper_.get_dht(); } - Logger *get_logger() { return dht_wrapper_.logger(); } - const uint8_t *dht_public_key() const { return dht_wrapper_.dht_public_key(); } - const uint8_t *real_public_key() const { return nc_get_self_public_key(net_crypto_.get()); } - const Random *get_random() { return &dht_wrapper_.node().c_random; } + Onion_Client *_Nonnull get_onion_client() { return REQUIRE_NOT_NULL(onion_client_.get()); } + Net_Crypto *_Nonnull get_net_crypto() { return REQUIRE_NOT_NULL(net_crypto_.get()); } + DHT *_Nonnull get_dht() { return dht_wrapper_.get_dht(); } + Logger *_Nonnull get_logger() { return dht_wrapper_.logger(); } + const std::uint8_t *_Nonnull dht_public_key() const { return dht_wrapper_.dht_public_key(); } + const std::uint8_t *_Nonnull real_public_key() const + { + return nc_get_self_public_key(net_crypto_.get()); + } + const std::uint8_t *_Nonnull dht_secret_key() const { return dht_wrapper_.dht_secret_key(); } + const Random *_Nonnull get_random() { return &dht_wrapper_.node().c_random; } IP_Port get_ip_port() const { return dht_wrapper_.get_ip_port(); } + tox::test::ScopedToxSystem &node() { return dht_wrapper_.node(); } + Onion_Announce *_Nonnull get_onion_announce() + { + return REQUIRE_NOT_NULL(onion_announce_.get()); + } - void poll() + void poll(bool poll_onion = true) { dht_wrapper_.poll(); do_net_crypto(net_crypto_.get(), nullptr); - do_onion_client(onion_client_.get()); + if (poll_onion) + do_onion_client(onion_client_.get()); } ~OnionTestNode(); @@ -75,6 +100,8 @@ private: std::unique_ptr> net_profile_; std::unique_ptr net_crypto_; std::unique_ptr onion_client_; + std::unique_ptr onion_server_; + std::unique_ptr onion_announce_; }; template @@ -84,9 +111,11 @@ using OnionNode = OnionTestNode; class OnionClientTest : public ::testing::Test { public: - static void print_log(void *context, Logger_Level level, const char *file, uint32_t line, + static void print_log(void *context, Logger_Level level, const char *file, std::uint32_t line, const char *func, const char *message, void *userdata) { + if (level == LOGGER_LEVEL_TRACE) + return; fprintf(stderr, "[%d] %s:%u %s: %s\n", level, file, line, func, message); } @@ -103,8 +132,8 @@ TEST_F(OnionClientTest, CreationAndDestruction) TEST_F(OnionClientTest, FriendManagement) { OnionNode alice(env, 33445); - uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t friend_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t friend_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(alice.get_random(), friend_pk, friend_sk); // Add Friend @@ -134,19 +163,19 @@ TEST_F(OnionClientTest, FriendManagement) TEST_F(OnionClientTest, FriendStatus) { OnionNode alice(env, 33445); - uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t friend_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t friend_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(alice.get_random(), friend_pk, friend_sk); int friend_num = onion_addfriend(alice.get_onion_client(), friend_pk); ASSERT_NE(friend_num, -1); // Set DHT Key so we can get IP - uint8_t dht_key[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t dht_key[CRYPTO_PUBLIC_KEY_SIZE]; crypto_new_keypair(alice.get_random(), dht_key, friend_sk); EXPECT_EQ(onion_set_friend_dht_pubkey(alice.get_onion_client(), friend_num, dht_key), 0); - uint32_t lock_token; + std::uint32_t lock_token; EXPECT_EQ( dht_addfriend(alice.get_dht(), dht_key, nullptr, nullptr, friend_num, &lock_token), 0); @@ -167,21 +196,21 @@ TEST_F(OnionClientTest, FriendStatus) TEST_F(OnionClientTest, DHTKey) { OnionNode alice(env, 33445); - uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t friend_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t friend_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(alice.get_random(), friend_pk, friend_sk); int friend_num = onion_addfriend(alice.get_onion_client(), friend_pk); ASSERT_NE(friend_num, -1); - uint8_t dht_key[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t dht_key[CRYPTO_PUBLIC_KEY_SIZE]; crypto_new_keypair(alice.get_random(), dht_key, friend_sk); // Set DHT Key EXPECT_EQ(onion_set_friend_dht_pubkey(alice.get_onion_client(), friend_num, dht_key), 0); // Get DHT Key - uint8_t retrieved_key[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t retrieved_key[CRYPTO_PUBLIC_KEY_SIZE]; EXPECT_EQ(onion_getfriend_dht_pubkey(alice.get_onion_client(), friend_num, retrieved_key), 1); EXPECT_EQ(std::memcmp(dht_key, retrieved_key, CRYPTO_PUBLIC_KEY_SIZE), 0); @@ -196,13 +225,13 @@ TEST_F(OnionClientTest, BootstrapNodes) IP_Port ip_port; ip_init(&ip_port.ip, 1); ip_port.port = 1234; - uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE]; crypto_new_keypair(alice.get_random(), pk, pk); EXPECT_TRUE(onion_add_bs_path_node(alice.get_onion_client(), &ip_port, pk)); Node_format nodes[MAX_ONION_CLIENTS]; - uint16_t count = onion_backup_nodes(alice.get_onion_client(), nodes, MAX_ONION_CLIENTS); + std::uint16_t count = onion_backup_nodes(alice.get_onion_client(), nodes, MAX_ONION_CLIENTS); EXPECT_GE(count, 0); } @@ -217,8 +246,8 @@ TEST_F(OnionClientTest, ConnectionStatus) TEST_F(OnionClientTest, GroupChatHelpers) { OnionNode alice(env, 33445); - uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t friend_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t friend_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(alice.get_random(), friend_pk, friend_sk); int friend_num = onion_addfriend(alice.get_onion_client(), friend_pk); @@ -228,30 +257,34 @@ TEST_F(OnionClientTest, GroupChatHelpers) EXPECT_NE(friend_obj, nullptr); // Test Group Chat Public Key - uint8_t gc_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t gc_pk[CRYPTO_PUBLIC_KEY_SIZE]; crypto_new_keypair(alice.get_random(), gc_pk, friend_sk); onion_friend_set_gc_public_key(friend_obj, gc_pk); - const uint8_t *retrieved_gc_pk = onion_friend_get_gc_public_key(friend_obj); + const std::uint8_t *retrieved_gc_pk = onion_friend_get_gc_public_key(friend_obj); EXPECT_EQ(std::memcmp(gc_pk, retrieved_gc_pk, CRYPTO_PUBLIC_KEY_SIZE), 0); // Test Group Chat Flag EXPECT_FALSE(onion_friend_is_groupchat(friend_obj)); - uint8_t data[] = {1, 2, 3}; + std::uint8_t data[] = {1, 2, 3}; onion_friend_set_gc_data(friend_obj, data, sizeof(data)); EXPECT_TRUE(onion_friend_is_groupchat(friend_obj)); } TEST_F(OnionClientTest, OOBReadInHandleAnnounceResponse) { + constexpr bool kEnableLogging = false; + OnionNode alice(env, 33445); - logger_callback_log(alice.get_logger(), OnionClientTest::print_log, nullptr, nullptr); + if (kEnableLogging) { + logger_callback_log(alice.get_logger(), OnionClientTest::print_log, nullptr, nullptr); + } WrappedDHT bob(env, 12345); FakeUdpSocket *bob_socket = bob.node().endpoint; IP_Port bob_ip = bob.get_ip_port(); - const uint8_t *bob_pk = bob.dht_public_key(); - const uint8_t *bob_sk = bob.dht_secret_key(); + const std::uint8_t *bob_pk = bob.dht_public_key(); + const std::uint8_t *bob_sk = bob.dht_secret_key(); // Bootstrap Alice to Bob dht_bootstrap(alice.get_dht(), &bob_ip, bob_pk); @@ -260,15 +293,15 @@ TEST_F(OnionClientTest, OOBReadInHandleAnnounceResponse) onion_add_bs_path_node(alice.get_onion_client(), &bob_ip, bob_pk); // Get internal state - uint64_t initial_recv_time = onion_testonly_get_last_packet_recv(alice.get_onion_client()); + std::uint64_t initial_recv_time = onion_testonly_get_last_packet_recv(alice.get_onion_client()); // Setup Memory - Tox_Memory mem_struct = env.fake_memory().get_c_memory(); + Memory mem_struct = env.fake_memory().c_memory(); const Memory *mem = &mem_struct; // Observer bool triggered = false; - bob_socket->set_recv_observer([&](const std::vector &data, const IP_Port &from) { + bob_socket->set_recv_observer([&](const std::vector &data, const IP_Port &from) { if (triggered) return; if (data.size() < 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE) @@ -278,14 +311,14 @@ TEST_F(OnionClientTest, OOBReadInHandleAnnounceResponse) if (data[0] != NET_PACKET_ONION_SEND_INITIAL) return; - uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; - uint8_t nonce[CRYPTO_NONCE_SIZE]; - memcpy(nonce, data.data() + 1, CRYPTO_NONCE_SIZE); - const uint8_t *ephem_pk = data.data() + 1 + CRYPTO_NONCE_SIZE; + std::uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; + std::uint8_t nonce[CRYPTO_NONCE_SIZE]; + std::memcpy(nonce, data.data() + 1, CRYPTO_NONCE_SIZE); + const std::uint8_t *ephem_pk = data.data() + 1 + CRYPTO_NONCE_SIZE; encrypt_precompute(ephem_pk, bob_sk, shared_key); - std::vector decrypted1( + std::vector decrypted1( data.size() - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE)); int dlen = decrypt_data_symmetric(mem, shared_key, nonce, data.data() + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, @@ -294,51 +327,51 @@ TEST_F(OnionClientTest, OOBReadInHandleAnnounceResponse) return; // Decrypted 1: [IP] [PK] [Encrypted 2] - size_t offset = SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE; - if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) + std::size_t offset = SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE; + if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) return; ephem_pk = decrypted1.data() + SIZE_IPPORT; encrypt_precompute(ephem_pk, bob_sk, shared_key); - std::vector decrypted2(dlen - offset - CRYPTO_MAC_SIZE); + std::vector decrypted2(dlen - offset - CRYPTO_MAC_SIZE); dlen = decrypt_data_symmetric( mem, shared_key, nonce, decrypted1.data() + offset, dlen - offset, decrypted2.data()); if (dlen <= 0) return; // Decrypted 2: [IP] [PK] [Encrypted 3] - if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) + if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) return; ephem_pk = decrypted2.data() + SIZE_IPPORT; encrypt_precompute(ephem_pk, bob_sk, shared_key); - std::vector decrypted3(dlen - offset - CRYPTO_MAC_SIZE); + std::vector decrypted3(dlen - offset - CRYPTO_MAC_SIZE); dlen = decrypt_data_symmetric( mem, shared_key, nonce, decrypted2.data() + offset, dlen - offset, decrypted3.data()); if (dlen <= 0) return; // Decrypted 3: [IP] [Data] - size_t data_offset = SIZE_IPPORT; - if (static_cast(dlen) <= data_offset) + std::size_t data_offset = SIZE_IPPORT; + if (static_cast(dlen) <= data_offset) return; - uint8_t *req = decrypted3.data() + data_offset; - size_t req_len = dlen - data_offset; + std::uint8_t *req = decrypted3.data() + data_offset; + std::size_t req_len = dlen - data_offset; // Announce Request: [131] [Nonce] [Alice PK] [Encrypted] if (req[0] != 0x87 && req[0] != 0x83) return; - uint8_t req_nonce[CRYPTO_NONCE_SIZE]; - memcpy(req_nonce, req + 1, CRYPTO_NONCE_SIZE); - uint8_t alice_pk[CRYPTO_PUBLIC_KEY_SIZE]; - memcpy(alice_pk, req + 1 + CRYPTO_NONCE_SIZE, CRYPTO_PUBLIC_KEY_SIZE); + std::uint8_t req_nonce[CRYPTO_NONCE_SIZE]; + std::memcpy(req_nonce, req + 1, CRYPTO_NONCE_SIZE); + std::uint8_t alice_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::memcpy(alice_pk, req + 1 + CRYPTO_NONCE_SIZE, CRYPTO_PUBLIC_KEY_SIZE); - uint8_t *req_enc = req + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE; - size_t req_enc_len = req_len - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); + std::uint8_t *req_enc = req + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE; + std::size_t req_enc_len = req_len - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); - std::vector req_plain(req_enc_len - CRYPTO_MAC_SIZE); + std::vector req_plain(req_enc_len - CRYPTO_MAC_SIZE); int plen = decrypt_data( mem, alice_pk, bob_sk, req_nonce, req_enc, req_enc_len, req_plain.data()); @@ -346,26 +379,26 @@ TEST_F(OnionClientTest, OOBReadInHandleAnnounceResponse) return; // Payload: [Ping ID (32)] [Search ID (32)] [Data PK (32)] [Sendback (Rest)] - size_t sendback_offset = 32 + 32 + 32; - if (static_cast(plen) < sendback_offset + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH) + std::size_t sendback_offset = 32 + 32 + 32; + if (static_cast(plen) < sendback_offset + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH) return; - uint8_t *sendback = req_plain.data() + sendback_offset; - size_t sendback_len = ONION_ANNOUNCE_SENDBACK_DATA_LENGTH; + std::uint8_t *sendback = req_plain.data() + sendback_offset; + std::size_t sendback_len = ONION_ANNOUNCE_SENDBACK_DATA_LENGTH; // Construct Malicious Response - std::vector resp; + std::vector resp; resp.push_back(NET_PACKET_ANNOUNCE_RESPONSE); resp.insert(resp.end(), sendback, sendback + sendback_len); - uint8_t resp_nonce[CRYPTO_NONCE_SIZE]; + std::uint8_t resp_nonce[CRYPTO_NONCE_SIZE]; random_nonce(alice.get_random(), resp_nonce); resp.insert(resp.end(), resp_nonce, resp_nonce + CRYPTO_NONCE_SIZE); // Encrypted Payload: [is_stored (1)] [ping_id (32)] // Total 33 bytes. OMIT count. - std::vector payload(33, 0); + std::vector payload(33, 0); - std::vector ciphertext(33 + CRYPTO_MAC_SIZE); + std::vector ciphertext(33 + CRYPTO_MAC_SIZE); encrypt_data(mem, alice_pk, bob_sk, resp_nonce, payload.data(), 33, ciphertext.data()); resp.insert(resp.end(), ciphertext.begin(), ciphertext.end()); @@ -393,7 +426,7 @@ TEST_F(OnionClientTest, OOBReadInHandleAnnounceResponse) // Check if the packet was accepted // If accepted, last_packet_recv should be updated - uint64_t final_recv_time = onion_testonly_get_last_packet_recv(alice.get_onion_client()); + std::uint64_t final_recv_time = onion_testonly_get_last_packet_recv(alice.get_onion_client()); // IF the vulnerability is present, the code accepts the packet and updates last_packet_recv. // We want the test to FAIL if vulnerability is present. @@ -405,10 +438,10 @@ TEST_F(OnionClientTest, OOBReadInHandleAnnounceResponse) TEST_F(OnionClientTest, DISABLED_IntegerOverflowNumFriends) { OnionNode alice(env, 33445); - uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; std::memset(friend_pk, 0, sizeof(friend_pk)); - // Add 65536 friends to trigger integer overflow of uint16_t num_friends + // Add 65536 friends to trigger integer overflow of std::uint16_t num_friends // This loop demonstrates that we can add enough friends to wrap the counter. for (int i = 0; i < 65536; ++i) { // Ensure unique public key @@ -420,7 +453,7 @@ TEST_F(OnionClientTest, DISABLED_IntegerOverflowNumFriends) } } - // After 65536 adds, num_friends should be 65536 (no overflow with uint32_t) + // After 65536 adds, num_friends should be 65536 (no overflow with std::uint32_t) EXPECT_EQ(onion_get_friend_count(alice.get_onion_client()), 65536); // Add one more friend with a GUARANTEED unique key. @@ -441,27 +474,33 @@ TEST_F(OnionClientTest, DISABLED_IntegerOverflowNumFriends) TEST_F(OnionClientTest, OnionAnnounceResponse_TooShort) { + constexpr bool kEnableLogging = false; + OnionNode alice(env, 33445); - logger_callback_log(alice.get_logger(), OnionClientTest::print_log, nullptr, nullptr); + if (kEnableLogging) { + logger_callback_log(alice.get_logger(), OnionClientTest::print_log, nullptr, nullptr); + } WrappedDHT bob(env, 12345); - logger_callback_log(bob.logger(), OnionClientTest::print_log, nullptr, nullptr); + if (kEnableLogging) { + logger_callback_log(bob.logger(), OnionClientTest::print_log, nullptr, nullptr); + } FakeUdpSocket *bob_socket = bob.node().endpoint; IP_Port bob_ip = bob.get_ip_port(); - const uint8_t *bob_pk = bob.dht_public_key(); - const uint8_t *bob_sk = bob.dht_secret_key(); + const std::uint8_t *bob_pk = bob.dht_public_key(); + const std::uint8_t *bob_sk = bob.dht_secret_key(); dht_bootstrap(alice.get_dht(), &bob_ip, bob_pk); onion_add_bs_path_node(alice.get_onion_client(), &bob_ip, bob_pk); - uint64_t initial_recv_time = onion_testonly_get_last_packet_recv(alice.get_onion_client()); + std::uint64_t initial_recv_time = onion_testonly_get_last_packet_recv(alice.get_onion_client()); bool triggered = false; // Setup Memory - Tox_Memory mem_struct = env.fake_memory().get_c_memory(); + Memory mem_struct = env.fake_memory().c_memory(); const Memory *mem = &mem_struct; - bob_socket->set_recv_observer([&](const std::vector &data, const IP_Port &from) { + bob_socket->set_recv_observer([&](const std::vector &data, const IP_Port &from) { if (triggered) return; if (data.size() < 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE) @@ -470,14 +509,14 @@ TEST_F(OnionClientTest, OnionAnnounceResponse_TooShort) if (data[0] != NET_PACKET_ONION_SEND_INITIAL) return; - uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; - uint8_t nonce[CRYPTO_NONCE_SIZE]; - memcpy(nonce, data.data() + 1, CRYPTO_NONCE_SIZE); - const uint8_t *ephem_pk = data.data() + 1 + CRYPTO_NONCE_SIZE; + std::uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; + std::uint8_t nonce[CRYPTO_NONCE_SIZE]; + std::memcpy(nonce, data.data() + 1, CRYPTO_NONCE_SIZE); + const std::uint8_t *ephem_pk = data.data() + 1 + CRYPTO_NONCE_SIZE; encrypt_precompute(ephem_pk, bob_sk, shared_key); - std::vector decrypted1( + std::vector decrypted1( data.size() - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE)); int dlen = decrypt_data_symmetric(mem, shared_key, nonce, data.data() + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, @@ -485,73 +524,73 @@ TEST_F(OnionClientTest, OnionAnnounceResponse_TooShort) if (dlen <= 0) return; - size_t offset = SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE; - if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) + std::size_t offset = SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE; + if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) return; ephem_pk = decrypted1.data() + SIZE_IPPORT; encrypt_precompute(ephem_pk, bob_sk, shared_key); - std::vector decrypted2(dlen - offset - CRYPTO_MAC_SIZE); + std::vector decrypted2(dlen - offset - CRYPTO_MAC_SIZE); dlen = decrypt_data_symmetric( mem, shared_key, nonce, decrypted1.data() + offset, dlen - offset, decrypted2.data()); if (dlen <= 0) return; - if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) + if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) return; ephem_pk = decrypted2.data() + SIZE_IPPORT; encrypt_precompute(ephem_pk, bob_sk, shared_key); - std::vector decrypted3(dlen - offset - CRYPTO_MAC_SIZE); + std::vector decrypted3(dlen - offset - CRYPTO_MAC_SIZE); dlen = decrypt_data_symmetric( mem, shared_key, nonce, decrypted2.data() + offset, dlen - offset, decrypted3.data()); if (dlen <= 0) return; - size_t data_offset = SIZE_IPPORT; - if (static_cast(dlen) <= data_offset) + std::size_t data_offset = SIZE_IPPORT; + if (static_cast(dlen) <= data_offset) return; - uint8_t *req = decrypted3.data() + data_offset; - size_t req_len = dlen - data_offset; + std::uint8_t *req = decrypted3.data() + data_offset; + std::size_t req_len = dlen - data_offset; if (req[0] != 0x87 && req[0] != 0x83) return; - uint8_t req_nonce[CRYPTO_NONCE_SIZE]; - memcpy(req_nonce, req + 1, CRYPTO_NONCE_SIZE); - uint8_t alice_pk[CRYPTO_PUBLIC_KEY_SIZE]; - memcpy(alice_pk, req + 1 + CRYPTO_NONCE_SIZE, CRYPTO_PUBLIC_KEY_SIZE); + std::uint8_t req_nonce[CRYPTO_NONCE_SIZE]; + std::memcpy(req_nonce, req + 1, CRYPTO_NONCE_SIZE); + std::uint8_t alice_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::memcpy(alice_pk, req + 1 + CRYPTO_NONCE_SIZE, CRYPTO_PUBLIC_KEY_SIZE); - uint8_t *req_enc = req + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE; - size_t req_enc_len = req_len - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); + std::uint8_t *req_enc = req + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE; + std::size_t req_enc_len = req_len - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); - std::vector req_plain(req_enc_len - CRYPTO_MAC_SIZE); + std::vector req_plain(req_enc_len - CRYPTO_MAC_SIZE); int plen = decrypt_data( mem, alice_pk, bob_sk, req_nonce, req_enc, req_enc_len, req_plain.data()); if (plen <= 0) return; - size_t sendback_offset = 32 + 32 + 32; - if (static_cast(plen) < sendback_offset + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH) + std::size_t sendback_offset = 32 + 32 + 32; + if (static_cast(plen) < sendback_offset + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH) return; - uint8_t *sendback = req_plain.data() + sendback_offset; - size_t sendback_len = ONION_ANNOUNCE_SENDBACK_DATA_LENGTH; + std::uint8_t *sendback = req_plain.data() + sendback_offset; + std::size_t sendback_len = ONION_ANNOUNCE_SENDBACK_DATA_LENGTH; - std::vector resp; + std::vector resp; resp.push_back(NET_PACKET_ANNOUNCE_RESPONSE); resp.insert(resp.end(), sendback, sendback + sendback_len); - uint8_t resp_nonce[CRYPTO_NONCE_SIZE]; + std::uint8_t resp_nonce[CRYPTO_NONCE_SIZE]; random_nonce(alice.get_random(), resp_nonce); resp.insert(resp.end(), resp_nonce, resp_nonce + CRYPTO_NONCE_SIZE); // PAYLOAD SIZE 33 (1 + 32) // This is exactly what triggers the missing byte read for nodes_count - std::vector payload(33, 0); + std::vector payload(33, 0); - std::vector ciphertext(payload.size() + CRYPTO_MAC_SIZE); + std::vector ciphertext(payload.size() + CRYPTO_MAC_SIZE); encrypt_data( mem, alice_pk, bob_sk, resp_nonce, payload.data(), payload.size(), ciphertext.data()); @@ -576,4 +615,244 @@ TEST_F(OnionClientTest, OnionAnnounceResponse_TooShort) EXPECT_EQ(onion_testonly_get_last_packet_recv(alice.get_onion_client()), initial_recv_time); } +TEST_F(OnionClientTest, SharedKeyCacheUseAfterFreeRegression) +{ + OnionNode alice(env, 33445); + OnionNode bob(env, 33446); + OnionNode charlie(env, 33447); + OnionNode dave(env, 33448); + + std::vector nodes = {&bob, &charlie, &dave}; + + // Make everyone know everyone via bootstrap + for (auto n1 : nodes) { + for (auto n2 : nodes) { + if (n1 == n2) + continue; + IP_Port ip = n2->get_ip_port(); + dht_bootstrap(n1->get_dht(), &ip, n2->dht_public_key()); + } + } + + for (auto node : nodes) { + IP_Port ip = node->get_ip_port(); + const std::uint8_t *pk = node->dht_public_key(); + dht_bootstrap(alice.get_dht(), &ip, pk); + onion_add_bs_path_node(alice.get_onion_client(), &ip, pk); + } + + Memory mem_struct = env.fake_memory().c_memory(); + const Memory *mem = &mem_struct; + + int total_decryption_failures = 0; + std::set> seen_announcements; + + auto observer + = [&](OnionNode *node, const std::vector &data, const IP_Port &from) { + if (data.empty()) + return; + // If it's an onion packet, it will be handled by Onion server. + // We want to catch the final hop announce request. + if (data[0] == 0x83 || data[0] == 0x87) { + // An announce request has 57 bytes of header and 120 bytes of ciphertext. + // There's also RETURN_3 appended, but we don't need it for decryption. + const std::size_t kHeaderSize = 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE; + const std::size_t kCiphertextSize = 120; + + if (data.size() < kHeaderSize + kCiphertextSize) + return; + + const std::uint8_t *nonce = data.data() + 1; + const std::uint8_t *sender_pk = data.data() + 1 + CRYPTO_NONCE_SIZE; + const std::uint8_t *ciphertext = sender_pk + CRYPTO_PUBLIC_KEY_SIZE; + + std::vector plain(kCiphertextSize - CRYPTO_MAC_SIZE); + int plen = decrypt_data(mem, sender_pk, node->dht_secret_key(), nonce, ciphertext, + kCiphertextSize, plain.data()); + + if (plen > 0) { + // The real PK (client_id) is in the payload at offset ONION_PING_ID_SIZE + // (32). + const std::uint8_t *real_pk = plain.data() + 32; + std::array real_pk_arr; + std::memcpy(real_pk_arr.data(), real_pk, CRYPTO_PUBLIC_KEY_SIZE); + seen_announcements.insert(real_pk_arr); + } else { + total_decryption_failures++; +#if 0 + fprintf(stderr, "Node %02x%02x... FAILED to decrypt announcement from %02x%02x... (size %zu)\n", + node->dht_public_key()[0], node->dht_public_key()[1], sender_pk[0], + sender_pk[1], data.size()); +#endif + } + } + }; + + bob.node().endpoint->set_recv_observer( + [&](const std::vector &data, const IP_Port &from) { + observer(&bob, data, from); + }); + charlie.node().endpoint->set_recv_observer( + [&](const std::vector &data, const IP_Port &from) { + observer(&charlie, data, from); + }); + dave.node().endpoint->set_recv_observer( + [&](const std::vector &data, const IP_Port &from) { + observer(&dave, data, from); + }); + + // Give Alice time to connect. + for (int i = 0; i < 30; ++i) { + env.advance_time(500); + alice.poll(); + for (auto node : nodes) + node->poll(); + if (onion_connection_status(alice.get_onion_client()) != ONION_CONNECTION_STATUS_NONE) { + break; + } + } + + ASSERT_NE(onion_connection_status(alice.get_onion_client()), ONION_CONNECTION_STATUS_NONE); + + const int kNumFriends = 2; + std::vector> friend_pks; + + for (int i = 0; i < kNumFriends; ++i) { + std::uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE], sk[CRYPTO_SECRET_KEY_SIZE]; + crypto_new_keypair(alice.get_random(), pk, sk); + onion_addfriend(alice.get_onion_client(), pk); + std::array pk_arr; + std::memcpy(pk_arr.data(), pk, CRYPTO_PUBLIC_KEY_SIZE); + friend_pks.push_back(pk_arr); + } + + // Wait for announcements. + for (int i = 0; i < 20; ++i) { + env.advance_time(1000); + alice.poll(); + for (auto node : nodes) + node->poll(); + + // If we've seen most friends announced, we can stop early. + int found_count = 0; + for (const auto &pk : friend_pks) { + if (seen_announcements.count(pk)) + found_count++; + } + if (found_count >= kNumFriends) { + break; + } + } + + // On incorrect code, we expect many decryption failures because Alice reuses shared keys. + // On correct code, there should be zero decryption failures. + EXPECT_EQ(total_decryption_failures, 0) << "Decryption failed at the target node! This " + "indicates Alice reused a shared key incorrectly."; + + int found_count = 0; + for (const auto &pk : friend_pks) { + if (seen_announcements.count(pk)) + found_count++; + } + EXPECT_GE(found_count, kNumFriends / 2) + << "At least some friends should have been announced successfully."; +} + +TEST_F(OnionClientTest, SharedKeyReuseOnEviction) +{ + OnionNode alice(env, 33445); + + // Alice adds a friend + std::uint8_t friend_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t friend_sk[CRYPTO_SECRET_KEY_SIZE]; + crypto_new_keypair(alice.get_random(), friend_pk, friend_sk); + onion_addfriend(alice.get_onion_client(), friend_pk); + + // We use 20 nodes to ensure we fill the announcement list (size 12) and trigger evictions. + std::vector> nodes; + for (int i = 0; i < 20; ++i) { + nodes.push_back(std::make_unique(env, 33446 + i)); + IP_Port ip = nodes.back()->get_ip_port(); + onion_add_bs_path_node(alice.get_onion_client(), &ip, nodes.back()->dht_public_key()); + } + + // Make everyone know everyone via bootstrap + for (auto &n1 : nodes) { + // Bootstrap Alice to this node + IP_Port ip1 = n1->get_ip_port(); + dht_bootstrap(alice.get_dht(), &ip1, n1->dht_public_key()); + + for (auto &n2 : nodes) { + if (n1 == n2) + continue; + IP_Port ip2 = n2->get_ip_port(); + dht_bootstrap(n1->get_dht(), &ip2, n2->dht_public_key()); + } + } + + // Alice connects + for (int i = 0; i < 20; ++i) { + env.advance_time(500); + alice.poll(); + for (auto &n : nodes) + n->poll(); + if (onion_connection_status(alice.get_onion_client()) != ONION_CONNECTION_STATUS_NONE) + break; + } + + int total_decrypted = 0; + int total_failed = 0; + Memory mem_struct = env.fake_memory().c_memory(); + const Memory *mem = &mem_struct; + + auto observer = [&](OnionNode *node, const std::vector &data) { + if (data.size() < 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE) + return; + + // Check for announce request (0x83 or 0x87) + if (data[0] != 0x83 && data[0] != 0x87) + return; + + const std::uint8_t *nonce = data.data() + 1; + const std::uint8_t *sender_pk = data.data() + 1 + CRYPTO_NONCE_SIZE; + + const std::uint8_t *ciphertext = sender_pk + CRYPTO_PUBLIC_KEY_SIZE; + // The announce request ciphertext is exactly 120 bytes. + const std::size_t kCiphertextSize = 120; + + if (data.size() < (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + kCiphertextSize)) + return; + + std::vector plain(kCiphertextSize - CRYPTO_MAC_SIZE); + int plen = decrypt_data(mem, sender_pk, node->dht_secret_key(), nonce, ciphertext, + kCiphertextSize, plain.data()); + + if (plen > 0) { + total_decrypted++; + } else { + total_failed++; + } + }; + + for (auto &n : nodes) { + n->node().endpoint->set_recv_observer( + [&, ptr = n.get()](const std::vector &data, const IP_Port &from) { + observer(ptr, data); + }); + } + + // Run for a while to ensure many announcements are sent and some slots are reused. + for (int i = 0; i < 30; ++i) { + env.advance_time(1000); + alice.poll(); + for (auto &n : nodes) + n->poll(); + } + + EXPECT_GT(total_decrypted, 0); + EXPECT_EQ(total_failed, 0) + << "Decryption failed! This indicates Alice reused a shared key incorrectly after a node " + "slot was evicted and replaced."; +} + } // namespace diff --git a/toxcore/os_event.c b/toxcore/os_event.c new file mode 100644 index 00000000..b9e92659 --- /dev/null +++ b/toxcore/os_event.c @@ -0,0 +1,667 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#include "os_event.h" + +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#endif /* WIN32 */ + +#ifdef EV_USE_EPOLL +#include +#elif defined(ESP_PLATFORM) +#include +#elif !defined(_WIN32) +#include +#endif /* EV_USE_EPOLL */ + +#include "ccompat.h" +#include "logger.h" +#include "mem.h" + +#ifdef _WIN32 +// Using the undocumented AFD driver directly (IOCTL_AFD_POLL). +// This bypasses the 64-handle limit of WaitForMultipleEvents and the bugs of WSAPoll. +// It provides true Level-Triggered notifications similar to poll/epoll. + +#define IOCTL_AFD_POLL 0x00012024 + +typedef struct AFD_Poll_Handle_Info { + void *handle; + unsigned long events; + uint32_t status; +} AFD_Poll_Handle_Info; + +typedef struct AFD_Poll_Info { + int64_t timeout; + unsigned long number_of_handles; + unsigned long exclusive; + AFD_Poll_Handle_Info handles[1]; +} AFD_Poll_Info; + +typedef enum Afd_Poll { + AFD_POLL_RECEIVE = 0x0001, + AFD_POLL_RECEIVE_EXPEDITED = 0x0002, + AFD_POLL_SEND = 0x0004, + AFD_POLL_DISCONNECT = 0x0008, + AFD_POLL_ABORT = 0x0010, + AFD_POLL_LOCAL_CLOSE = 0x0020, + AFD_POLL_ACCEPT = 0x0080, + AFD_POLL_CONNECT_FAIL = 0x0100, +} Afd_Poll; +#endif /* _WIN32 */ + +typedef struct OS_Ev_Registration { + Socket sock; + Ev_Events events; + void *data; +} OS_Ev_Registration; + +typedef struct OS_Ev { + const Memory *mem; + const Logger *log; + OS_Ev_Registration **regs; + uint32_t regs_count; + uint32_t regs_capacity; + +#ifdef EV_USE_EPOLL + int epoll_fd; + struct epoll_event *events; + uint32_t events_capacity; +#elif defined(_WIN32) + AFD_Poll_Handle_Info *h_events; + uint32_t h_events_capacity; + SOCKET helper_sock; +#else + struct pollfd *pfds; + uint32_t pfds_count; +#endif /* EV_USE_EPOLL */ +} OS_Ev; + +static OS_Ev_Registration *os_ev_prepare_add(OS_Ev *os_ev, Socket sock, Ev_Events events, void *data) +{ + for (uint32_t i = 0; i < os_ev->regs_count; ++i) { + if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) { + return nullptr; + } + } + + if (os_ev->regs_count == os_ev->regs_capacity) { + const uint32_t new_capacity = os_ev->regs_capacity == 0 ? 8 : os_ev->regs_capacity * 2; + OS_Ev_Registration **new_regs + = (OS_Ev_Registration **)mem_vrealloc(os_ev->mem, os_ev->regs, new_capacity, sizeof(OS_Ev_Registration *)); + + if (new_regs == nullptr) { + return nullptr; + } + + os_ev->regs = new_regs; + os_ev->regs_capacity = new_capacity; + +#ifdef _WIN32 + AFD_Poll_Handle_Info *new_afd = (AFD_Poll_Handle_Info *)mem_vrealloc( + os_ev->mem, os_ev->h_events, new_capacity, sizeof(AFD_Poll_Handle_Info)); + if (new_afd == nullptr) { + return nullptr; + } + os_ev->h_events = new_afd; + os_ev->h_events_capacity = new_capacity; +#endif /* _WIN32 */ + } + + OS_Ev_Registration *reg = (OS_Ev_Registration *)mem_alloc(os_ev->mem, sizeof(OS_Ev_Registration)); + if (reg == nullptr) { + return nullptr; + } + reg->sock = sock; + reg->events = events; + reg->data = data; + os_ev->regs[os_ev->regs_count] = reg; + + return reg; +} + +#ifdef EV_USE_EPOLL + +static uint32_t events_to_epoll(Ev_Events events) +{ + uint32_t e = 0; + + if ((events & EV_READ) != 0) { + e |= EPOLLIN; + } + + if ((events & EV_WRITE) != 0) { + e |= EPOLLOUT; + } + + return e; +} + +static Ev_Events epoll_to_events(uint32_t e) +{ + Ev_Events events = 0; + + if ((e & EPOLLIN) != 0) { + events |= EV_READ; + } + + if ((e & EPOLLOUT) != 0) { + events |= EV_WRITE; + } + + if ((e & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) != 0) { + events |= EV_ERROR; + } + + return events; +} + +static bool os_ev_add(void *self, Socket sock, Ev_Events events, void *data) +{ + OS_Ev *os_ev = (OS_Ev *)self; + + OS_Ev_Registration *reg = os_ev_prepare_add(os_ev, sock, events, data); + if (reg == nullptr) { + return false; + } + + struct epoll_event ep_ev; + ep_ev.events = events_to_epoll(events); + ep_ev.data.ptr = reg; + + if (epoll_ctl(os_ev->epoll_fd, EPOLL_CTL_ADD, net_socket_to_native(sock), &ep_ev) == -1) { + Net_Strerror error_buf; + LOGGER_ERROR(os_ev->log, "epoll_ctl(ADD) failed: %s", net_strerror(errno, &error_buf)); + mem_delete(os_ev->mem, reg); + return false; + } + + LOGGER_DEBUG(os_ev->log, "Added socket %d to epoll (events: %u)", net_socket_to_native(sock), events); + + ++os_ev->regs_count; + return true; +} + +static bool os_ev_mod(void *self, Socket sock, Ev_Events events, void *data) +{ + OS_Ev *os_ev = (OS_Ev *)self; + + for (uint32_t i = 0; i < os_ev->regs_count; ++i) { + if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) { + OS_Ev_Registration *reg = os_ev->regs[i]; + reg->events = events; + reg->data = data; + + struct epoll_event ep_ev; + ep_ev.events = events_to_epoll(events); + ep_ev.data.ptr = reg; + + if (epoll_ctl(os_ev->epoll_fd, EPOLL_CTL_MOD, net_socket_to_native(sock), &ep_ev) == -1) { + Net_Strerror error_buf; + LOGGER_ERROR(os_ev->log, "epoll_ctl(MOD) failed: %s", net_strerror(errno, &error_buf)); + return false; + } + + LOGGER_DEBUG(os_ev->log, "Modified socket %d in epoll (events: %u)", net_socket_to_native(sock), events); + + return true; + } + } + + return false; +} + +static bool os_ev_del(void *self, Socket sock) +{ + OS_Ev *os_ev = (OS_Ev *)self; + + for (uint32_t i = 0; i < os_ev->regs_count; ++i) { + if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) { + if (epoll_ctl(os_ev->epoll_fd, EPOLL_CTL_DEL, net_socket_to_native(sock), nullptr) == -1) { + Net_Strerror error_buf; + LOGGER_ERROR(os_ev->log, "epoll_ctl(DEL) failed: %s", net_strerror(errno, &error_buf)); + } + + LOGGER_DEBUG(os_ev->log, "Removed socket %d from epoll", net_socket_to_native(sock)); + + mem_delete(os_ev->mem, os_ev->regs[i]); + os_ev->regs[i] = os_ev->regs[os_ev->regs_count - 1]; + --os_ev->regs_count; + return true; + } + } + + return false; +} + +static int32_t os_ev_run(void *self, Ev_Result *results, uint32_t max_results, int32_t timeout_ms) +{ + OS_Ev *os_ev = (OS_Ev *)self; + + if (os_ev->events_capacity < max_results) { + struct epoll_event *new_events + = (struct epoll_event *)mem_vrealloc(os_ev->mem, os_ev->events, max_results, sizeof(struct epoll_event)); + + if (new_events == nullptr) { + return -1; + } + + os_ev->events = new_events; + os_ev->events_capacity = max_results; + } + + const int n = epoll_wait(os_ev->epoll_fd, os_ev->events, (int)max_results, (int)timeout_ms); + + if (n < 0) { + if (errno == EINTR) { + return 0; + } + + Net_Strerror error_buf; + LOGGER_ERROR(os_ev->log, "epoll_wait failed: %s", net_strerror(errno, &error_buf)); + return -1; + } + + if (n == 0) { + return 0; + } + + uint32_t count = 0; + + for (int i = 0; i < n; ++i) { + OS_Ev_Registration *reg = (OS_Ev_Registration *)os_ev->events[i].data.ptr; + + if (reg != nullptr) { + results[count].sock = reg->sock; + results[count].events = epoll_to_events(os_ev->events[i].events); + results[count].data = reg->data; + ++count; + } + } + + return (int32_t)count; +} + +#elif defined(_WIN32) + +static uint32_t events_to_afd(Ev_Events events) +{ + uint32_t e = AFD_POLL_ABORT | AFD_POLL_LOCAL_CLOSE | AFD_POLL_CONNECT_FAIL; // Always monitor errors + + if (events & EV_READ) { + e |= AFD_POLL_RECEIVE | AFD_POLL_RECEIVE_EXPEDITED | AFD_POLL_ACCEPT | AFD_POLL_DISCONNECT; + } + + if (events & EV_WRITE) { + e |= AFD_POLL_SEND; + } + + return e; +} + +static Ev_Events afd_to_events(uint32_t e) +{ + Ev_Events events = 0; + + if (e & (AFD_POLL_RECEIVE | AFD_POLL_RECEIVE_EXPEDITED | AFD_POLL_ACCEPT | AFD_POLL_DISCONNECT)) { + events |= EV_READ; + } + + if (e & AFD_POLL_SEND) { + events |= EV_WRITE; + } + + if (e & (AFD_POLL_ABORT | AFD_POLL_LOCAL_CLOSE | AFD_POLL_CONNECT_FAIL)) { + events |= EV_ERROR; + } + + return events; +} + +static bool os_ev_add(void *self, Socket sock, Ev_Events events, void *data) +{ + OS_Ev *os_ev = (OS_Ev *)self; + + OS_Ev_Registration *reg = os_ev_prepare_add(os_ev, sock, events, data); + if (reg == nullptr) { + return false; + } + + AFD_Poll_Handle_Info *afd_handles = os_ev->h_events; + afd_handles[os_ev->regs_count].handle = (HANDLE)(uintptr_t)net_socket_to_native(sock); + afd_handles[os_ev->regs_count].events = events_to_afd(events); + afd_handles[os_ev->regs_count].status = 0; + + LOGGER_DEBUG(os_ev->log, "Added socket %d to AFD (events: %u)", net_socket_to_native(sock), events); + + ++os_ev->regs_count; + + return true; +} + +static bool os_ev_mod(void *self, Socket sock, Ev_Events events, void *data) +{ + OS_Ev *os_ev = (OS_Ev *)self; + + for (uint32_t i = 0; i < os_ev->regs_count; ++i) { + if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) { + os_ev->regs[i]->events = events; + os_ev->regs[i]->data = data; + + AFD_Poll_Handle_Info *afd_handles = os_ev->h_events; + afd_handles[i].events = events_to_afd(events); + afd_handles[i].status = 0; + + LOGGER_DEBUG(os_ev->log, "Modified socket %d in AFD (events: %u)", net_socket_to_native(sock), events); + + return true; + } + } + + return false; +} + +static bool os_ev_del(void *self, Socket sock) +{ + OS_Ev *os_ev = (OS_Ev *)self; + + for (uint32_t i = 0; i < os_ev->regs_count; ++i) { + if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) { + mem_delete(os_ev->mem, os_ev->regs[i]); + os_ev->regs[i] = os_ev->regs[os_ev->regs_count - 1]; + + AFD_Poll_Handle_Info *afd_handles = os_ev->h_events; + afd_handles[i] = afd_handles[os_ev->regs_count - 1]; + + --os_ev->regs_count; + + LOGGER_DEBUG(os_ev->log, "Removed socket %d from AFD", net_socket_to_native(sock)); + + return true; + } + } + + return false; +} + +static int32_t os_ev_run(void *self, Ev_Result *results, uint32_t max_results, int32_t timeout_ms) +{ + OS_Ev *os_ev = (OS_Ev *)self; + if (os_ev->regs_count == 0) { + if (timeout_ms > 0) { + Sleep(timeout_ms); + } + return 0; + } + + const size_t info_size = sizeof(AFD_Poll_Info) + (os_ev->regs_count - 1) * sizeof(AFD_Poll_Handle_Info); + AFD_Poll_Info *info = (AFD_Poll_Info *)HeapAlloc(GetProcessHeap(), 0, info_size); + if (info == nullptr) { + return -1; + } + + info->timeout = timeout_ms < 0 ? INT64_MAX : (int64_t)timeout_ms * -10000; + info->number_of_handles = os_ev->regs_count; + info->exclusive = 0; + + memcpy(info->handles, os_ev->h_events, os_ev->regs_count * sizeof(AFD_Poll_Handle_Info)); + + DWORD bytes_ret = 0; + + if (!DeviceIoControl((HANDLE)os_ev->helper_sock, IOCTL_AFD_POLL, info, (DWORD)info_size, info, (DWORD)info_size, + &bytes_ret, NULL)) { + const int err = net_error(); + HeapFree(GetProcessHeap(), 0, info); + + if (err == WAIT_TIMEOUT) { + return 0; + } + + Net_Strerror error_buf; + LOGGER_ERROR(os_ev->log, "AFD_POLL failed: %s", net_strerror(err, &error_buf)); + return -1; + } + + uint32_t count = 0; + for (uint32_t i = 0; i < info->number_of_handles && count < max_results; ++i) { + if (info->handles[i].events != 0) { + results[count].sock = os_ev->regs[i]->sock; + results[count].data = os_ev->regs[i]->data; + results[count].events = afd_to_events(info->handles[i].events); + ++count; + } + } + + HeapFree(GetProcessHeap(), 0, info); + return (int32_t)count; +} + +#else // POLL + +static bool os_ev_add(void *self, Socket sock, Ev_Events events, void *data) +{ + OS_Ev *os_ev = (OS_Ev *)self; + + OS_Ev_Registration *reg = os_ev_prepare_add(os_ev, sock, events, data); + if (reg == nullptr) { + return false; + } + + LOGGER_DEBUG(os_ev->log, "Added socket %d to poll (events: %u)", net_socket_to_native(sock), events); + + ++os_ev->regs_count; + + return true; +} + +static bool os_ev_mod(void *self, Socket sock, Ev_Events events, void *data) +{ + OS_Ev *os_ev = (OS_Ev *)self; + + for (uint32_t i = 0; i < os_ev->regs_count; ++i) { + if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) { + os_ev->regs[i]->events = events; + os_ev->regs[i]->data = data; + + LOGGER_DEBUG(os_ev->log, "Modified socket %d in poll (events: %u)", net_socket_to_native(sock), events); + + return true; + } + } + + return false; +} + +static bool os_ev_del(void *self, Socket sock) +{ + OS_Ev *os_ev = (OS_Ev *)self; + + for (uint32_t i = 0; i < os_ev->regs_count; ++i) { + if (net_socket_to_native(os_ev->regs[i]->sock) == net_socket_to_native(sock)) { + mem_delete(os_ev->mem, os_ev->regs[i]); + os_ev->regs[i] = os_ev->regs[os_ev->regs_count - 1]; + --os_ev->regs_count; + + LOGGER_DEBUG(os_ev->log, "Removed socket %d from poll", net_socket_to_native(sock)); + + return true; + } + } + + return false; +} + +static int32_t os_ev_run(void *self, Ev_Result *results, uint32_t max_results, int32_t timeout_ms) +{ + OS_Ev *os_ev = (OS_Ev *)self; + + if (os_ev->pfds_count < os_ev->regs_count) { + struct pollfd *new_pfds + = (struct pollfd *)mem_vrealloc(os_ev->mem, os_ev->pfds, os_ev->regs_count, sizeof(struct pollfd)); + + if (new_pfds == nullptr) { + return -1; + } + + os_ev->pfds = new_pfds; + os_ev->pfds_count = os_ev->regs_count; + } + + for (uint32_t i = 0; i < os_ev->regs_count; ++i) { + os_ev->pfds[i].fd = net_socket_to_native(os_ev->regs[i]->sock); + os_ev->pfds[i].events = 0; + + if ((os_ev->regs[i]->events & EV_READ) != 0) { + os_ev->pfds[i].events |= POLLIN; + } + + if ((os_ev->regs[i]->events & EV_WRITE) != 0) { + os_ev->pfds[i].events |= POLLOUT; + } + + os_ev->pfds[i].revents = 0; + } + + const int n = poll(os_ev->pfds, (nfds_t)os_ev->regs_count, (int)timeout_ms); + + if (n < 0) { + if (errno == EINTR) { + return 0; + } + + Net_Strerror error_buf; + LOGGER_ERROR(os_ev->log, "poll failed: %s", net_strerror(errno, &error_buf)); + return -1; + } + + if (n == 0) { + return 0; + } + + uint32_t count = 0; + + for (uint32_t i = 0; i < os_ev->regs_count && count < max_results; ++i) { + if (os_ev->pfds[i].revents == 0) { + continue; + } + + results[count].sock = os_ev->regs[i]->sock; + results[count].events = 0; + results[count].data = os_ev->regs[i]->data; + + if ((os_ev->pfds[i].revents & POLLIN) != 0) { + results[count].events |= EV_READ; + } + + if ((os_ev->pfds[i].revents & POLLOUT) != 0) { + results[count].events |= EV_WRITE; + } + + if ((os_ev->pfds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) != 0) { + results[count].events |= EV_ERROR; + } + + ++count; + } + + return (int32_t)count; +} + +#endif /* EV_USE_EPOLL */ + +static void os_ev_kill(Ev *ev) +{ + if (ev == nullptr) { + return; + } + + OS_Ev *os_ev = (OS_Ev *)ev->user_data; + const Memory *mem = os_ev->mem; + +#ifdef EV_USE_EPOLL + close(os_ev->epoll_fd); + mem_delete(mem, os_ev->events); +#elif defined(_WIN32) + closesocket(os_ev->helper_sock); + mem_delete(mem, os_ev->h_events); +#else + mem_delete(mem, os_ev->pfds); +#endif /* EV_USE_EPOLL */ + + for (uint32_t i = 0; i < os_ev->regs_count; ++i) { + mem_delete(mem, os_ev->regs[i]); + } + mem_delete(mem, os_ev->regs); + mem_delete(mem, os_ev); + mem_delete(mem, ev); +} + +static const Ev_Funcs os_ev_funcs = { + os_ev_add, + os_ev_mod, + os_ev_del, + os_ev_run, + os_ev_kill, +}; + +Ev *os_event_new(const Memory *mem, const Logger *log) +{ + OS_Ev *os_ev = (OS_Ev *)mem_alloc(mem, sizeof(OS_Ev)); + + if (os_ev == nullptr) { + return nullptr; + } + + os_ev->mem = mem; + os_ev->log = log; + +#ifdef EV_USE_EPOLL + os_ev->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + + if (os_ev->epoll_fd == -1) { + Net_Strerror error_buf; + LOGGER_ERROR(log, "Failed to create epoll fd: %s", net_strerror(errno, &error_buf)); + mem_delete(mem, os_ev); + return nullptr; + } + +#elif defined(_WIN32) + // Create a dummy socket for AFD communication + os_ev->helper_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (os_ev->helper_sock == INVALID_SOCKET) { + Net_Strerror error_buf; + LOGGER_ERROR(log, "Failed to create helper socket: %s", net_strerror(net_error(), &error_buf)); + mem_delete(mem, os_ev); + return nullptr; + } +#endif /* EV_USE_EPOLL */ + + Ev *ev = (Ev *)mem_alloc(mem, sizeof(Ev)); + + if (ev == nullptr) { +#ifdef EV_USE_EPOLL + close(os_ev->epoll_fd); +#elif defined(_WIN32) + closesocket(os_ev->helper_sock); +#endif /* EV_USE_EPOLL */ + mem_delete(mem, os_ev); + return nullptr; + } + + ev->funcs = &os_ev_funcs; + ev->user_data = os_ev; + + LOGGER_DEBUG(log, "Created new os_event loop"); + + return ev; +} diff --git a/toxcore/os_event.h b/toxcore/os_event.h new file mode 100644 index 00000000..4040afb1 --- /dev/null +++ b/toxcore/os_event.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#ifndef C_TOXCORE_TOXCORE_OS_EVENT_H +#define C_TOXCORE_TOXCORE_OS_EVENT_H + +#include "ev.h" +#include "logger.h" +#include "mem.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Create a new system-specific event loop (epoll/poll/select). */ +Ev *_Nullable os_event_new(const Memory *_Nonnull mem, const Logger *_Nullable log); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* C_TOXCORE_TOXCORE_OS_EVENT_H */ diff --git a/toxcore/os_memory.c b/toxcore/os_memory.c index 7275a45b..0910f97f 100644 --- a/toxcore/os_memory.c +++ b/toxcore/os_memory.c @@ -6,8 +6,7 @@ #include #include "attributes.h" -#include "tox_memory.h" -#include "tox_memory_impl.h" // IWYU pragma: keep +#include "mem.h" static void *os_malloc(void *_Nonnull self, uint32_t size) { @@ -27,14 +26,14 @@ static void os_dealloc(void *_Nonnull self, void *_Nullable ptr) free(ptr); } -static const Tox_Memory_Funcs os_memory_funcs = { +static const Memory_Funcs os_memory_funcs = { os_malloc, os_realloc, os_dealloc, }; -const Tox_Memory os_memory_obj = {&os_memory_funcs}; +const Memory os_memory_obj = {&os_memory_funcs}; -const Tox_Memory *os_memory(void) +const Memory *os_memory(void) { return &os_memory_obj; } diff --git a/toxcore/os_memory.h b/toxcore/os_memory.h index cfbe6a2c..530843b8 100644 --- a/toxcore/os_memory.h +++ b/toxcore/os_memory.h @@ -5,18 +5,19 @@ #ifndef C_TOXCORE_TOXCORE_OS_MEMORY_H #define C_TOXCORE_TOXCORE_OS_MEMORY_H -#include "tox_memory.h" +#include "mem.h" #ifdef __cplusplus extern "C" { #endif -extern const Tox_Memory os_memory_obj; +extern const Memory os_memory_obj; -const Tox_Memory *os_memory(void); +const Memory *os_memory(void); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* C_TOXCORE_TOXCORE_OS_MEMORY_H */ + diff --git a/toxcore/os_network.c b/toxcore/os_network.c new file mode 100644 index 00000000..174e6889 --- /dev/null +++ b/toxcore/os_network.c @@ -0,0 +1,786 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2016-2026 The TokTok team. + * Copyright © 2013 Tox project. + */ + +#ifdef __APPLE__ +#define _DARWIN_C_SOURCE +#endif /* __APPLE__ */ + +// For Solaris. +#ifdef __sun +#define __EXTENSIONS__ 1 +#endif /* __sun */ + +// For Linux (and some BSDs). +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 700 +#endif /* _XOPEN_SOURCE */ + +#include "os_network.h" + +#if defined(_WIN32) && defined(_WIN32_WINNT) && defined(_WIN32_WINNT_WINXP) && _WIN32_WINNT >= _WIN32_WINNT_WINXP +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x501 +#endif /* defined(_WIN32) && defined(_WIN32_WINNT) && defined(_WIN32_WINNT_WINXP) && _WIN32_WINNT >= _WIN32_WINNT_WINXP */ + +#if !defined(OS_WIN32) && (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) +#define OS_WIN32 +#endif /* !defined(OS_WIN32) && (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) */ + +#if defined(OS_WIN32) && !defined(WINVER) +// Windows XP +#define WINVER 0x0501 +#endif /* defined(OS_WIN32) && !defined(WINVER) */ + +#ifdef OS_WIN32 +#include +#include +#include +#endif /* OS_WIN32 */ + +#if !defined(OS_WIN32) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __sun +#include +#include +#endif /* __sun */ + +#else +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 27 +#endif /* IPV6_V6ONLY */ +#endif /* !defined(OS_WIN32) */ + +#include +#include +#include +#include + +#include "attributes.h" +#include "ccompat.h" +#include "mem.h" +#include "net.h" + +// Disable MSG_NOSIGNAL on systems not supporting it, e.g. Windows, FreeBSD +#if !defined(MSG_NOSIGNAL) +#define MSG_NOSIGNAL 0 +#endif /* !defined(MSG_NOSIGNAL) */ + +static int make_proto(int proto) +{ + switch (proto) { + case TOX_PROTO_TCP: + return IPPROTO_TCP; + + case TOX_PROTO_UDP: + return IPPROTO_UDP; + + default: + return proto; + } +} + +static int make_socktype(int type) +{ + switch (type) { + case TOX_SOCK_STREAM: + return SOCK_STREAM; + + case TOX_SOCK_DGRAM: + return SOCK_DGRAM; + + default: + return type; + } +} + +static int make_family(int family_value) +{ + switch (family_value) { + case TOX_AF_INET: + case TCP_INET: + return AF_INET; + + case TOX_AF_INET6: + case TCP_INET6: + return AF_INET6; + + case TOX_AF_UNSPEC: + return AF_UNSPEC; + + default: + return family_value; + } +} + +static void get_ip4(IP4 *_Nonnull result, const struct in_addr *_Nonnull 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; +} + +static void get_ip6(IP6 *_Nonnull result, const struct in6_addr *_Nonnull 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)); +} + +static void fill_addr4(const IP4 *_Nonnull ip, struct in_addr *_Nonnull addr) +{ + addr->s_addr = ip->uint32; +} + +static void fill_addr6(const IP6 *_Nonnull ip, struct in6_addr *_Nonnull addr) +{ + memcpy(addr->s6_addr, ip->uint8, sizeof(ip->uint8)); +} + +#ifndef OS_WIN32 +#define INVALID_SOCKET (-1) +#endif /* OS_WIN32 */ + +typedef struct Network_Addr { + struct sockaddr_storage addr; + size_t size; +} Network_Addr; + +static void ip_port_to_network_addr(const IP_Port *ip_port, Network_Addr *addr) +{ + addr->size = 0; + if (net_family_is_ipv4(ip_port->ip.family)) { + struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr->addr; + addr->size = sizeof(struct sockaddr_in); + addr4->sin_family = AF_INET; + fill_addr4(&ip_port->ip.ip.v4, &addr4->sin_addr); + addr4->sin_port = ip_port->port; + } else if (net_family_is_ipv6(ip_port->ip.family)) { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr->addr; + addr->size = sizeof(struct sockaddr_in6); + addr6->sin6_family = AF_INET6; + fill_addr6(&ip_port->ip.ip.v6, &addr6->sin6_addr); + addr6->sin6_port = ip_port->port; + addr6->sin6_flowinfo = 0; + addr6->sin6_scope_id = 0; + } +} + +static bool network_addr_to_ip_port(const Network_Addr *addr, IP_Port *ip_port) +{ + if (addr->addr.ss_family == AF_INET) { + const struct sockaddr_in *addr_in = (const struct sockaddr_in *)&addr->addr; + ip_port->ip.family = net_family_ipv4(); + get_ip4(&ip_port->ip.ip.v4, &addr_in->sin_addr); + ip_port->port = addr_in->sin_port; + return true; + } else if (addr->addr.ss_family == AF_INET6) { + const struct sockaddr_in6 *addr_in6 = (const struct sockaddr_in6 *)&addr->addr; + ip_port->ip.family = net_family_ipv6(); + get_ip6(&ip_port->ip.ip.v6, &addr_in6->sin6_addr); + ip_port->port = addr_in6->sin6_port; + return true; + } + return false; +} + +#if !defined(OS_WIN32) + +static bool should_ignore_recv_error(int err) +{ + return err == EWOULDBLOCK; +} + +static bool should_ignore_connect_error(int err) +{ + return err == EWOULDBLOCK || err == EINPROGRESS; +} + +static const char *inet_ntop4(const struct in_addr *_Nonnull addr, char *_Nonnull buf, size_t bufsize) +{ + return inet_ntop(AF_INET, addr, buf, bufsize); +} + +static const char *inet_ntop6(const struct in6_addr *_Nonnull addr, char *_Nonnull buf, size_t bufsize) +{ + return inet_ntop(AF_INET6, addr, buf, bufsize); +} + +static int inet_pton4(const char *_Nonnull addr_string, struct in_addr *_Nonnull addrbuf) +{ + return inet_pton(AF_INET, addr_string, addrbuf); +} + +static int inet_pton6(const char *_Nonnull addr_string, struct in6_addr *_Nonnull addrbuf) +{ + return inet_pton(AF_INET6, addr_string, addrbuf); +} + +#else + +static bool should_ignore_recv_error(int err) +{ + // We ignore WSAECONNRESET as Windows helpfully* sends that error if a + // previously sent UDP packet wasn't delivered. + return err == WSAEWOULDBLOCK || err == WSAECONNRESET; +} + +static bool should_ignore_connect_error(int err) +{ + return err == WSAEWOULDBLOCK || err == WSAEINPROGRESS; +} + +static const char *inet_ntop4(const struct in_addr *_Nonnull addr, char *_Nonnull buf, size_t bufsize) +{ + struct sockaddr_in saddr = {0}; + + saddr.sin_family = AF_INET; + saddr.sin_addr = *addr; + + DWORD len = (DWORD)bufsize; + + if (WSAAddressToStringA((LPSOCKADDR)&saddr, sizeof(saddr), nullptr, buf, &len)) { + return nullptr; + } + + return buf; +} + +static const char *inet_ntop6(const struct in6_addr *_Nonnull addr, char *_Nonnull buf, size_t bufsize) +{ + struct sockaddr_in6 saddr = {0}; + + saddr.sin6_family = AF_INET6; + saddr.sin6_addr = *addr; + + DWORD len = (DWORD)bufsize; + + if (WSAAddressToStringA((LPSOCKADDR)&saddr, sizeof(saddr), nullptr, buf, &len)) { + return nullptr; + } + + return buf; +} + +static int inet_pton4(const char *_Nonnull addrString, struct in_addr *_Nonnull addrbuf) +{ + struct sockaddr_in saddr = {0}; + + INT len = sizeof(saddr); + + if (WSAStringToAddressA((LPSTR)addrString, AF_INET, nullptr, (LPSOCKADDR)&saddr, &len)) { + return 0; + } + + *addrbuf = saddr.sin_addr; + + return 1; +} + +static int inet_pton6(const char *_Nonnull addrString, struct in6_addr *_Nonnull addrbuf) +{ + struct sockaddr_in6 saddr = {0}; + + INT len = sizeof(saddr); + + if (WSAStringToAddressA((LPSTR)addrString, AF_INET6, nullptr, (LPSOCKADDR)&saddr, &len)) { + return 0; + } + + *addrbuf = saddr.sin6_addr; + + return 1; +} + +#endif /* !defined(OS_WIN32) */ + +const char *net_inet_ntop4(const IP4 *addr, char *buf, size_t bufsize) +{ + struct in_addr a; + fill_addr4(addr, &a); + return inet_ntop4(&a, buf, bufsize); +} + +const char *net_inet_ntop6(const IP6 *addr, char *buf, size_t bufsize) +{ + struct in6_addr a; + fill_addr6(addr, &a); + return inet_ntop6(&a, buf, bufsize); +} + +int net_inet_pton4(const char *addr_string, IP4 *addrbuf) +{ + struct in_addr a; + const int ret = inet_pton4(addr_string, &a); + if (ret == 1) { + get_ip4(addrbuf, &a); + } + return ret; +} + +int net_inet_pton6(const char *addr_string, IP6 *addrbuf) +{ + struct in6_addr a; + const int ret = inet_pton6(addr_string, &a); + if (ret == 1) { + get_ip6(addrbuf, &a); + } + return ret; +} + +bool net_should_ignore_recv_error(int err) +{ + return should_ignore_recv_error(err); +} + +bool net_should_ignore_connect_error(int err) +{ + return should_ignore_connect_error(err); +} + +static int sys_close(void *_Nullable obj, Socket sock) +{ +#if defined(OS_WIN32) + return closesocket(net_socket_to_native(sock)); +#else // !OS_WIN32 + return close(net_socket_to_native(sock)); +#endif /* OS_WIN32 */ +} + +static Socket sys_accept(void *_Nullable obj, Socket sock) +{ + return net_socket_from_native(accept(net_socket_to_native(sock), nullptr, nullptr)); +} + +static int sys_bind(void *_Nullable obj, Socket sock, const IP_Port *_Nonnull addr) +{ + Network_Addr naddr; + ip_port_to_network_addr(addr, &naddr); + if (naddr.size == 0) { + return -1; + } + return bind(net_socket_to_native(sock), (const struct sockaddr *)&naddr.addr, (socklen_t)naddr.size); +} + +static int sys_listen(void *_Nullable obj, Socket sock, int backlog) +{ + return listen(net_socket_to_native(sock), backlog); +} + +static int sys_connect(void *_Nullable obj, Socket sock, const IP_Port *_Nonnull addr) +{ + Network_Addr naddr; + ip_port_to_network_addr(addr, &naddr); + if (naddr.size == 0) { + return -1; + } + return connect(net_socket_to_native(sock), (const struct sockaddr *)&naddr.addr, (socklen_t)naddr.size); +} + +static int sys_recvbuf(void *_Nullable obj, Socket sock) +{ +#ifdef OS_WIN32 + u_long count = 0; + ioctlsocket(net_socket_to_native(sock), FIONREAD, &count); +#else + int count = 0; + ioctl(net_socket_to_native(sock), FIONREAD, &count); +#endif /* OS_WIN32 */ + + return count; +} + +static int sys_recv(void *_Nullable obj, Socket sock, uint8_t *_Nonnull buf, size_t len) +{ + return recv(net_socket_to_native(sock), (char *)buf, len, MSG_NOSIGNAL); +} + +static int sys_send(void *_Nullable obj, Socket sock, const uint8_t *_Nonnull buf, size_t len) +{ + return send(net_socket_to_native(sock), (const char *)buf, len, MSG_NOSIGNAL); +} + +static int sys_sendto(void *_Nullable obj, Socket sock, const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr) +{ + Network_Addr naddr; + ip_port_to_network_addr(addr, &naddr); + if (naddr.size == 0) { + return -1; + } + return sendto(net_socket_to_native(sock), (const char *)buf, len, 0, (const struct sockaddr *)&naddr.addr, (socklen_t)naddr.size); +} + +static int sys_recvfrom(void *_Nullable obj, Socket sock, uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr) +{ + Network_Addr naddr = {{0}}; + socklen_t addrlen = sizeof(naddr.addr); + const int ret = recvfrom(net_socket_to_native(sock), (char *)buf, len, 0, (struct sockaddr *)&naddr.addr, &addrlen); + naddr.size = addrlen; + if (ret >= 0) { + if (!network_addr_to_ip_port(&naddr, addr)) { + // Ignore packets from unknown families + return -1; + } + } + return ret; +} + +static Socket sys_socket(void *_Nullable obj, int domain, int type, int proto) +{ + const int platform_domain = make_family(domain); + const int platform_type = make_socktype(type); + const int platform_prot = make_proto(proto); + return net_socket_from_native(socket(platform_domain, platform_type, platform_prot)); +} + +static int sys_socket_nonblock(void *_Nullable obj, Socket sock, bool nonblock) +{ +#ifdef OS_WIN32 + u_long mode = nonblock ? 1 : 0; + return ioctlsocket(net_socket_to_native(sock), FIONBIO, &mode); +#else + const int fd = net_socket_to_native(sock); + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) { + return -1; + } + if (nonblock) { + flags |= O_NONBLOCK; + } else { + flags &= ~O_NONBLOCK; + } + return fcntl(fd, F_SETFL, flags); +#endif /* OS_WIN32 */ +} + +static int sys_getsockopt(void *_Nullable obj, Socket sock, int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen) +{ + socklen_t len = (socklen_t) * optlen; + const int ret = getsockopt(net_socket_to_native(sock), level, optname, (char *)optval, &len); + *optlen = len; + return ret; +} + +static int sys_setsockopt(void *_Nullable obj, Socket sock, int level, int optname, const void *_Nonnull optval, size_t optlen) +{ +#ifdef EMSCRIPTEN + return 0; +#else + return setsockopt(net_socket_to_native(sock), level, optname, (const char *)optval, (socklen_t)optlen); +#endif /* EMSCRIPTEN */ +} + +static int sys_getaddrinfo(void *_Nullable obj, const Memory *_Nonnull mem, const char *_Nonnull address, int family, int sock_type, IP_Port *_Nullable *_Nonnull addrs) +{ + assert(addrs != nullptr); + + struct addrinfo hints = {0}; + hints.ai_family = make_family(family); + hints.ai_socktype = make_socktype(sock_type); + + struct addrinfo *infos = nullptr; + + const int rc = getaddrinfo(address, nullptr, &hints, &infos); + + if (rc != 0) { + return 0; + } + + const int32_t max_count = INT32_MAX / sizeof(IP_Port); + + int result = 0; + for (struct addrinfo *walker = infos; walker != nullptr && result < max_count; walker = walker->ai_next) { + if (walker->ai_family == hints.ai_family || hints.ai_family == AF_UNSPEC) { + ++result; + } + } + + assert(max_count >= result); + + IP_Port *tmp_addrs = (IP_Port *)mem_valloc(mem, result, sizeof(IP_Port)); + if (tmp_addrs == nullptr) { + freeaddrinfo(infos); + return 0; + } + + int i = 0; + for (struct addrinfo *walker = infos; walker != nullptr; walker = walker->ai_next) { + if (walker->ai_family == hints.ai_family || hints.ai_family == AF_UNSPEC) { + Network_Addr naddr; + naddr.size = walker->ai_addrlen; + memcpy(&naddr.addr, walker->ai_addr, walker->ai_addrlen); + + if (network_addr_to_ip_port(&naddr, &tmp_addrs[i])) { + ++i; + } + } + } + + result = i; + freeaddrinfo(infos); + *addrs = tmp_addrs; + + return result; +} + +static int sys_freeaddrinfo(void *_Nullable obj, const Memory *_Nonnull mem, IP_Port *_Nullable addrs) +{ + if (addrs == nullptr) { + return 0; + } + + mem_delete(mem, addrs); + + return 0; +} + +static const Network_Funcs os_network_funcs = { + sys_close, + sys_accept, + sys_bind, + sys_listen, + sys_connect, + sys_recvbuf, + sys_recv, + sys_recvfrom, + sys_send, + sys_sendto, + sys_socket, + sys_socket_nonblock, + sys_getsockopt, + sys_setsockopt, + sys_getaddrinfo, + sys_freeaddrinfo, +}; +const Network os_network_obj = {&os_network_funcs, nullptr}; + +const Network *os_network(void) +{ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if ((true)) { + return nullptr; + } +#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ +#ifdef OS_WIN32 + WSADATA wsaData; + + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) { + return nullptr; + } +#endif /* OS_WIN32 */ + return &os_network_obj; +} + +Socket net_invalid_socket(void) +{ + return net_socket_from_native(INVALID_SOCKET); +} + +uint32_t net_htonl(uint32_t hostlong) +{ + return htonl(hostlong); +} + +uint16_t net_htons(uint16_t hostshort) +{ + return htons(hostshort); +} + +uint32_t net_ntohl(uint32_t hostlong) +{ + return ntohl(hostlong); +} + +uint16_t net_ntohs(uint16_t hostshort) +{ + return ntohs(hostshort); +} + +#if !defined(INADDR_LOOPBACK) +#define INADDR_LOOPBACK 0x7f000001 +#endif /* !defined(INADDR_LOOPBACK) */ + +IP4 net_get_ip4_broadcast(void) +{ + const IP4 ip4_broadcast = {INADDR_BROADCAST}; + return ip4_broadcast; +} + +IP6 net_get_ip6_broadcast(void) +{ + const IP6 ip6_broadcast = { + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + }; + return ip6_broadcast; +} + +IP4 net_get_ip4_loopback(void) +{ + IP4 loopback; + loopback.uint32 = htonl(INADDR_LOOPBACK); + return loopback; +} + +IP6 net_get_ip6_loopback(void) +{ + /* in6addr_loopback isn't available everywhere, so we do it ourselves. */ + IP6 loopback = {{0}}; + loopback.uint8[15] = 1; + return loopback; +} + +#ifndef IPV6_ADD_MEMBERSHIP +#ifdef IPV6_JOIN_GROUP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif /* IPV6_JOIN_GROUP */ +#endif /* IPV6_ADD_MEMBERSHIP */ + +bool net_join_multicast(const Network *ns, Socket sock, Family family) +{ +#ifndef ESP_PLATFORM + if (net_family_is_ipv6(family)) { + /* multicast local nodes */ + struct ipv6_mreq mreq = {{{{0}}}}; + mreq.ipv6mr_multiaddr.s6_addr[0] = 0xFF; + mreq.ipv6mr_multiaddr.s6_addr[1] = 0x02; + mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01; + mreq.ipv6mr_interface = 0; + + return ns_setsockopt(ns, sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == 0; + } +#endif /* ESP_PLATFORM */ + return false; +} + +bool net_set_socket_nonblock(const Network *ns, Socket sock) +{ + return ns_socket_nonblock(ns, sock, true) == 0; +} + +bool net_set_socket_nosigpipe(const Network *ns, Socket sock) +{ +#if defined(__APPLE__) + int set = 1; + return ns_setsockopt(ns, sock, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof(int)) == 0; +#else + return true; +#endif /* __APPLE__ */ +} + +bool net_set_socket_reuseaddr(const Network *ns, Socket sock) +{ + int set = 1; +#if defined(OS_WIN32) + return ns_setsockopt(ns, sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, &set, sizeof(set)) == 0; +#else + return ns_setsockopt(ns, sock, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set)) == 0; +#endif /* OS_WIN32 */ +} + +bool net_set_socket_dualstack(const Network *ns, Socket sock) +{ + int ipv6only = 0; + size_t optsize = sizeof(ipv6only); + const int res = ns_getsockopt(ns, sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, &optsize); + + if ((res == 0) && (ipv6only == 0)) { + return true; + } + + ipv6only = 0; + return ns_setsockopt(ns, sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)) == 0; +} + +bool net_set_socket_buffer_size(const Network *ns, Socket sock, int size) +{ + bool ok = true; + + if (ns_setsockopt(ns, sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) != 0) { + ok = false; + } + + if (ns_setsockopt(ns, sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) != 0) { + ok = false; + } + + return ok; +} + +bool net_set_socket_broadcast(const Network *ns, Socket sock) +{ + int broadcast = 1; + return ns_setsockopt(ns, sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) == 0; +} + +int net_error(void) +{ +#ifdef OS_WIN32 + return WSAGetLastError(); +#else + return errno; +#endif /* OS_WIN32 */ +} + +void net_clear_error(void) +{ +#ifdef OS_WIN32 + WSASetLastError(0); +#else + errno = 0; +#endif /* OS_WIN32 */ +} + +#ifdef OS_WIN32 +char *net_strerror(int error, Net_Strerror *buf) +{ + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, + error, 0, buf->data, NET_STRERROR_SIZE, nullptr); + return buf->data; +} +#else +#if defined(_GNU_SOURCE) && defined(__GLIBC__) +static const char *net_strerror_r(int error, char *_Nonnull tmp, size_t tmp_size) +{ + const char *retstr = strerror_r(error, tmp, tmp_size); + + if (errno != 0) { + snprintf(tmp, tmp_size, "error %d (strerror_r failed with errno %d)", error, errno); + } + + return retstr; +} +#else +static const char *net_strerror_r(int error, char *_Nonnull tmp, size_t tmp_size) +{ + const int fmt_error = strerror_r(error, tmp, tmp_size); + + if (fmt_error != 0) { + snprintf(tmp, tmp_size, "error %d (strerror_r failed with error %d, errno %d)", error, fmt_error, errno); + } + + return tmp; +} +#endif /* GNU */ +char *net_strerror(int error, Net_Strerror *buf) +{ + errno = 0; + + const char *retstr = net_strerror_r(error, buf->data, NET_STRERROR_SIZE); + const size_t retstr_len = strlen(retstr); + assert(retstr_len < NET_STRERROR_SIZE); + buf->size = (uint16_t)retstr_len; + + return buf->data; +} +#endif /* OS_WIN32 */ diff --git a/toxcore/os_network.h b/toxcore/os_network.h new file mode 100644 index 00000000..45659543 --- /dev/null +++ b/toxcore/os_network.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022-2026 The TokTok team. + */ + +#ifndef C_TOXCORE_TOXCORE_OS_NETWORK_H +#define C_TOXCORE_TOXCORE_OS_NETWORK_H + +#include "net.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const Network os_network_obj; + +const Network *_Nullable os_network(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* C_TOXCORE_TOXCORE_OS_NETWORK_H */ diff --git a/toxcore/os_random.c b/toxcore/os_random.c index ee71ce30..7597f9f1 100644 --- a/toxcore/os_random.c +++ b/toxcore/os_random.c @@ -4,11 +4,11 @@ #include "os_random.h" #include +#include #include "attributes.h" #include "ccompat.h" -#include "tox_random.h" -#include "tox_random_impl.h" +#include "rng.h" static void os_random_bytes(void *_Nonnull self, uint8_t *_Nonnull bytes, uint32_t length) { @@ -20,14 +20,14 @@ static uint32_t os_random_uniform(void *_Nonnull self, uint32_t upper_bound) return randombytes_uniform(upper_bound); } -static const Tox_Random_Funcs os_random_funcs = { +static const Random_Funcs os_random_funcs = { os_random_bytes, os_random_uniform, }; -const Tox_Random os_random_obj = {&os_random_funcs}; +const Random os_random_obj = {&os_random_funcs}; -const Tox_Random *os_random(void) +const Random *os_random(void) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if ((true)) { diff --git a/toxcore/os_random.h b/toxcore/os_random.h index a670b8ee..51d44245 100644 --- a/toxcore/os_random.h +++ b/toxcore/os_random.h @@ -5,19 +5,19 @@ #ifndef C_TOXCORE_TOXCORE_OS_RANDOM_H #define C_TOXCORE_TOXCORE_OS_RANDOM_H -#include "tox_random.h" +#include "rng.h" #ifdef __cplusplus extern "C" { #endif -extern const Tox_Random os_random_obj; +extern const Random os_random_obj; /** @brief System random number generator. * * Uses libsodium's CSPRNG (on Linux, `/dev/urandom`). */ -const Tox_Random *os_random(void); +const Random *os_random(void); #ifdef __cplusplus } /* extern "C" */ diff --git a/toxcore/ping.h b/toxcore/ping.h index 0ed705b7..fa3f57ac 100644 --- a/toxcore/ping.h +++ b/toxcore/ping.h @@ -17,6 +17,7 @@ #include "crypto_core.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "network.h" typedef struct Ping Ping; diff --git a/toxcore/ping_array.h b/toxcore/ping_array.h index 9c025e02..cbee8377 100644 --- a/toxcore/ping_array.h +++ b/toxcore/ping_array.h @@ -16,6 +16,7 @@ #include "crypto_core.h" #include "mem.h" #include "mono_time.h" +#include "rng.h" #ifdef __cplusplus extern "C" { diff --git a/toxcore/ping_array_test.cc b/toxcore/ping_array_test.cc index 11bc61a4..a7308ff9 100644 --- a/toxcore/ping_array_test.cc +++ b/toxcore/ping_array_test.cc @@ -8,6 +8,7 @@ #include #include +#include "attributes.h" #include "crypto_core_test_util.hh" #include "mono_time.h" @@ -16,20 +17,20 @@ namespace { using tox::test::SimulatedEnvironment; struct Ping_Array_Deleter { - void operator()(Ping_Array *arr) { ping_array_kill(arr); } + void operator()(Ping_Array *_Nullable arr) { ping_array_kill(arr); } }; using Ping_Array_Ptr = std::unique_ptr; struct Mono_Time_Deleter { - Mono_Time_Deleter(Tox_Memory mem) + Mono_Time_Deleter(Memory mem) : mem_(mem) { } - void operator()(Mono_Time *arr) { mono_time_free(&mem_, arr); } + void operator()(Mono_Time *_Nullable arr) { mono_time_free(&mem_, arr); } private: - Tox_Memory mem_; + Memory mem_; }; using Mono_Time_Ptr = std::unique_ptr; @@ -37,7 +38,7 @@ using Mono_Time_Ptr = std::unique_ptr; TEST(PingArray, MinimumTimeoutIsOne) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); EXPECT_EQ(ping_array_new(&c_mem, 1, 0), nullptr); EXPECT_NE(Ping_Array_Ptr(ping_array_new(&c_mem, 1, 1)), nullptr); } @@ -45,7 +46,7 @@ TEST(PingArray, MinimumTimeoutIsOne) TEST(PingArray, MinimumArraySizeIsOne) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); EXPECT_EQ(ping_array_new(&c_mem, 0, 1), nullptr); EXPECT_NE(Ping_Array_Ptr(ping_array_new(&c_mem, 1, 1)), nullptr); } @@ -53,7 +54,7 @@ TEST(PingArray, MinimumArraySizeIsOne) TEST(PingArray, ArraySizeMustBePowerOfTwo) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Ping_Array_Ptr arr; arr.reset(ping_array_new(&c_mem, 2, 1)); @@ -70,57 +71,57 @@ TEST(PingArray, ArraySizeMustBePowerOfTwo) TEST(PingArray, StoredDataCanBeRetrieved) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); - auto c_rng = env.fake_random().get_c_random(); + auto c_mem = env.fake_memory().c_memory(); + auto c_rng = env.fake_random().c_random(); Ping_Array_Ptr const arr(ping_array_new(&c_mem, 2, 1)); Mono_Time_Ptr const mono_time(mono_time_new(&c_mem, nullptr, nullptr), c_mem); ASSERT_NE(mono_time, nullptr); - uint64_t const ping_id = ping_array_add( - arr.get(), mono_time.get(), &c_rng, std::vector{1, 2, 3, 4}.data(), 4); + std::uint64_t const ping_id = ping_array_add( + arr.get(), mono_time.get(), &c_rng, std::vector{1, 2, 3, 4}.data(), 4); EXPECT_NE(ping_id, 0); - std::vector data(4); + std::vector data(4); EXPECT_EQ(ping_array_check(arr.get(), mono_time.get(), data.data(), data.size(), ping_id), 4); - EXPECT_EQ(data, std::vector({1, 2, 3, 4})); + EXPECT_EQ(data, std::vector({1, 2, 3, 4})); } TEST(PingArray, RetrievingDataWithTooSmallOutputBufferHasNoEffect) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); - auto c_rng = env.fake_random().get_c_random(); + auto c_mem = env.fake_memory().c_memory(); + auto c_rng = env.fake_random().c_random(); Ping_Array_Ptr const arr(ping_array_new(&c_mem, 2, 1)); Mono_Time_Ptr const mono_time(mono_time_new(&c_mem, nullptr, nullptr), c_mem); ASSERT_NE(mono_time, nullptr); - uint64_t const ping_id = ping_array_add( - arr.get(), mono_time.get(), &c_rng, (std::vector{1, 2, 3, 4}).data(), 4); + std::uint64_t const ping_id = ping_array_add( + arr.get(), mono_time.get(), &c_rng, (std::vector{1, 2, 3, 4}).data(), 4); EXPECT_NE(ping_id, 0); - std::vector data(4); + std::vector data(4); EXPECT_EQ(ping_array_check(arr.get(), mono_time.get(), data.data(), 3, ping_id), -1); // It doesn't write anything to the data array. - EXPECT_EQ(data, std::vector({0, 0, 0, 0})); + EXPECT_EQ(data, std::vector({0, 0, 0, 0})); // Afterwards, we can still read it. EXPECT_EQ(ping_array_check(arr.get(), mono_time.get(), data.data(), 4, ping_id), 4); - EXPECT_EQ(data, std::vector({1, 2, 3, 4})); + EXPECT_EQ(data, std::vector({1, 2, 3, 4})); } TEST(PingArray, ZeroLengthDataCanBeAdded) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); - auto c_rng = env.fake_random().get_c_random(); + auto c_mem = env.fake_memory().c_memory(); + auto c_rng = env.fake_random().c_random(); Ping_Array_Ptr const arr(ping_array_new(&c_mem, 2, 1)); Mono_Time_Ptr const mono_time(mono_time_new(&c_mem, nullptr, nullptr), c_mem); ASSERT_NE(mono_time, nullptr); - uint8_t c = 0; - uint64_t const ping_id = ping_array_add(arr.get(), mono_time.get(), &c_rng, &c, sizeof(c)); + std::uint8_t c = 0; + std::uint64_t const ping_id = ping_array_add(arr.get(), mono_time.get(), &c_rng, &c, sizeof(c)); EXPECT_NE(ping_id, 0); EXPECT_EQ(ping_array_check(arr.get(), mono_time.get(), &c, sizeof(c), ping_id), 1); @@ -129,13 +130,13 @@ TEST(PingArray, ZeroLengthDataCanBeAdded) TEST(PingArray, PingId0IsInvalid) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); + auto c_mem = env.fake_memory().c_memory(); Ping_Array_Ptr const arr(ping_array_new(&c_mem, 2, 1)); Mono_Time_Ptr const mono_time(mono_time_new(&c_mem, nullptr, nullptr), c_mem); ASSERT_NE(mono_time, nullptr); - uint8_t c = 0; + std::uint8_t c = 0; EXPECT_EQ(ping_array_check(arr.get(), mono_time.get(), &c, sizeof(c), 0), -1); } @@ -143,15 +144,15 @@ TEST(PingArray, PingId0IsInvalid) TEST(PingArray, DataCanOnlyBeRetrievedOnce) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); - auto c_rng = env.fake_random().get_c_random(); + auto c_mem = env.fake_memory().c_memory(); + auto c_rng = env.fake_random().c_random(); Ping_Array_Ptr const arr(ping_array_new(&c_mem, 2, 1)); Mono_Time_Ptr const mono_time(mono_time_new(&c_mem, nullptr, nullptr), c_mem); ASSERT_NE(mono_time, nullptr); - uint8_t c = 0; - uint64_t const ping_id = ping_array_add(arr.get(), mono_time.get(), &c_rng, &c, sizeof(c)); + std::uint8_t c = 0; + std::uint64_t const ping_id = ping_array_add(arr.get(), mono_time.get(), &c_rng, &c, sizeof(c)); EXPECT_NE(ping_id, 0); EXPECT_EQ(ping_array_check(arr.get(), mono_time.get(), &c, sizeof(c), ping_id), 1); @@ -161,18 +162,18 @@ TEST(PingArray, DataCanOnlyBeRetrievedOnce) TEST(PingArray, PingIdMustMatchOnCheck) { SimulatedEnvironment env; - auto c_mem = env.fake_memory().get_c_memory(); - auto c_rng = env.fake_random().get_c_random(); + auto c_mem = env.fake_memory().c_memory(); + auto c_rng = env.fake_random().c_random(); Ping_Array_Ptr const arr(ping_array_new(&c_mem, 1, 1)); Mono_Time_Ptr const mono_time(mono_time_new(&c_mem, nullptr, nullptr), c_mem); ASSERT_NE(mono_time, nullptr); - uint8_t c = 0; - uint64_t const ping_id = ping_array_add(arr.get(), mono_time.get(), &c_rng, &c, sizeof(c)); + std::uint8_t c = 0; + std::uint64_t const ping_id = ping_array_add(arr.get(), mono_time.get(), &c_rng, &c, sizeof(c)); EXPECT_NE(ping_id, 0); - uint64_t const bad_ping_id = ping_id == 1 ? 2 : 1; + std::uint64_t const bad_ping_id = ping_id == 1 ? 2 : 1; // bad_ping_id will also be pointing at the same element, but won't match the // actual ping_id. diff --git a/toxcore/rng.c b/toxcore/rng.c new file mode 100644 index 00000000..ea8bbf2e --- /dev/null +++ b/toxcore/rng.c @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022-2026 The TokTok team. + */ +#include "rng.h" + +void rng_bytes(const Random *rng, uint8_t *bytes, uint32_t length) +{ + rng->funcs->bytes_callback(rng->user_data, bytes, length); +} + +uint32_t rng_uniform(const Random *rng, uint32_t upper_bound) +{ + return rng->funcs->uniform_callback(rng->user_data, upper_bound); +} + diff --git a/toxcore/tox_random_impl.h b/toxcore/rng.h similarity index 54% rename from toxcore/tox_random_impl.h rename to toxcore/rng.h index b7469015..9ff164ce 100644 --- a/toxcore/tox_random_impl.h +++ b/toxcore/rng.h @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: GPL-3.0-or-later - * Copyright © 2022-2025 The TokTok team. + * Copyright © 2022-2026 The TokTok team. */ -#ifndef C_TOXCORE_TOXCORE_TOX_RANDOM_IMPL_H -#define C_TOXCORE_TOXCORE_TOX_RANDOM_IMPL_H +#ifndef C_TOXCORE_TOXCORE_RNG_H +#define C_TOXCORE_TOXCORE_RNG_H -#include "tox_memory.h" -#include "tox_random.h" +#include + +#include "attributes.h" #ifdef __cplusplus extern "C" { @@ -18,20 +19,20 @@ extern "C" { * secure pseudo-random number generator (CSPRNG). The security of Tox heavily * depends on the security of this RNG. */ -typedef void tox_random_bytes_cb(void *self, uint8_t *bytes, uint32_t length); +typedef void random_bytes_cb(void *_Nullable self, uint8_t *_Nonnull bytes, uint32_t length); /** @brief Generate a random integer between 0 and @p upper_bound. * * Should produce a uniform random distribution, but Tox security does not * depend on this being correct. In principle, it could even be a non-CSPRNG. */ -typedef uint32_t tox_random_uniform_cb(void *self, uint32_t upper_bound); +typedef uint32_t random_uniform_cb(void *_Nullable self, uint32_t upper_bound); /** @brief Virtual function table for Random. */ -struct Tox_Random_Funcs { - tox_random_bytes_cb *bytes_callback; - tox_random_uniform_cb *uniform_callback; -}; +typedef struct Random_Funcs { + random_bytes_cb *_Nonnull bytes_callback; + random_uniform_cb *_Nonnull uniform_callback; +} Random_Funcs; /** @brief Random number generator object. * @@ -39,15 +40,16 @@ struct Tox_Random_Funcs { * well-defined (non-random) ways. Production code ought to use libsodium's * CSPRNG and use `os_random` below. */ -struct Tox_Random { - const Tox_Random_Funcs *funcs; - void *user_data; +typedef struct Random { + const Random_Funcs *_Nonnull funcs; + void *_Nullable user_data; +} Random; - const Tox_Memory *mem; -}; +void rng_bytes(const Random *_Nonnull rng, uint8_t *_Nonnull bytes, uint32_t length); +uint32_t rng_uniform(const Random *_Nonnull rng, uint32_t upper_bound); #ifdef __cplusplus } /* extern "C" */ #endif -#endif /* C_TOXCORE_TOXCORE_TOX_RANDOM_IMPL_H */ +#endif /* C_TOXCORE_TOXCORE_RNG_H */ diff --git a/toxcore/shared_key_cache.h b/toxcore/shared_key_cache.h index c9892d06..5d16a4bc 100644 --- a/toxcore/shared_key_cache.h +++ b/toxcore/shared_key_cache.h @@ -13,6 +13,10 @@ #include "mem.h" #include "mono_time.h" +#ifdef __cplusplus +extern "C" { +#endif + /** * This implements a cache for shared keys, since key generation is expensive. */ @@ -46,4 +50,8 @@ void shared_key_cache_free(Shared_Key_Cache *_Nullable cache); */ const uint8_t *_Nullable shared_key_cache_lookup(Shared_Key_Cache *_Nonnull cache, const uint8_t public_key[_Nonnull CRYPTO_PUBLIC_KEY_SIZE]); +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif /* C_TOXCORE_TOXCORE_SHARED_KEY_CACHE_H */ diff --git a/toxcore/shared_key_cache_test.cc b/toxcore/shared_key_cache_test.cc new file mode 100644 index 00000000..0cef9e3b --- /dev/null +++ b/toxcore/shared_key_cache_test.cc @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#include "shared_key_cache.h" + +#include + +#include +#include +#include + +#include "../testing/support/public/simulation.hh" +#include "attributes.h" +#include "mono_time.h" + +namespace { + +using tox::test::SimulatedNode; +using tox::test::Simulation; + +class SharedKeyCacheTest : public ::testing::Test { +protected: + void SetUp() override + { + sim = std::make_unique(); + node = sim->create_node(); + crypto_new_keypair(&node->c_random, alice_pk, alice_sk); + + sim->advance_time(1000); // Ensure mono_time > 0 + + mono_time = mono_time_new( + &node->c_memory, + [](void *_Nullable user_data) -> std::uint64_t { + return static_cast(user_data)->clock().current_time_ms(); + }, + sim.get()); + mono_time_update(mono_time); + + logger = logger_new(&node->c_memory); + + cache = shared_key_cache_new(logger, mono_time, &node->c_memory, alice_sk, 10000, 4); + ASSERT_NE(cache, nullptr); + } + + void TearDown() override + { + shared_key_cache_free(cache); + logger_kill(logger); + mono_time_free(&node->c_memory, mono_time); + } + + std::unique_ptr sim; + std::unique_ptr node; + std::uint8_t alice_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t alice_sk[CRYPTO_SECRET_KEY_SIZE]; + Mono_Time *_Nullable mono_time = nullptr; + Logger *_Nullable logger = nullptr; + Shared_Key_Cache *_Nullable cache = nullptr; +}; + +TEST_F(SharedKeyCacheTest, BasicLookup) +{ + std::uint8_t bob_pk[CRYPTO_PUBLIC_KEY_SIZE], bob_sk[CRYPTO_SECRET_KEY_SIZE]; + crypto_new_keypair(&node->c_random, bob_pk, bob_sk); + + const std::uint8_t *shared1 = shared_key_cache_lookup(cache, bob_pk); + ASSERT_NE(shared1, nullptr); + + // Second lookup should return cached pointer + const std::uint8_t *shared2 = shared_key_cache_lookup(cache, bob_pk); + EXPECT_EQ(shared1, shared2); + + // Verify key correctness + std::uint8_t expected[CRYPTO_SHARED_KEY_SIZE]; + encrypt_precompute(bob_pk, alice_sk, expected); + EXPECT_EQ(std::memcmp(shared1, expected, CRYPTO_SHARED_KEY_SIZE), 0); +} + +TEST_F(SharedKeyCacheTest, TimeoutEviction) +{ + std::uint8_t bob_pk[CRYPTO_PUBLIC_KEY_SIZE], bob_sk[CRYPTO_SECRET_KEY_SIZE]; + crypto_new_keypair(&node->c_random, bob_pk, bob_sk); + + shared_key_cache_lookup(cache, bob_pk); + sim->advance_time(11000); // Past 10s timeout + mono_time_update(mono_time); + + // Should re-compute/re-insert after timeout + const std::uint8_t *shared = shared_key_cache_lookup(cache, bob_pk); + ASSERT_NE(shared, nullptr); +} + +TEST_F(SharedKeyCacheTest, SlotExhaustionAndLRU) +{ + // Fill one bucket (4 slots) + std::vector> pks; + // Store pointers to verify cache hits (stable memory addresses) + std::vector pointers; + std::uint8_t target_hash = 0x42; + + for (int i = 0; i < 5; ++i) { + std::uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE], sk[CRYPTO_SECRET_KEY_SIZE]; + while (true) { + crypto_new_keypair(&node->c_random, pk, sk); + if (pk[8] == target_hash) + break; + } + pks.push_back(std::vector(pk, pk + CRYPTO_PUBLIC_KEY_SIZE)); + sim->advance_time(100); + mono_time_update(mono_time); + pointers.push_back(shared_key_cache_lookup(cache, pk)); + } + + // 5th lookup should have evicted the oldest (LRU) + bool reused = false; + for (int i = 0; i < 4; ++i) { + if (pointers[4] == pointers[i]) + reused = true; + } + EXPECT_TRUE(reused); + + // Most recent should still be cached + EXPECT_EQ(shared_key_cache_lookup(cache, pks[1].data()), pointers[1]); +} + +TEST_F(SharedKeyCacheTest, HashDistribution) +{ + const int total_keys = 256; + std::vector> pks; + // Store pointers to verify cache hits (stable memory addresses) + std::vector pointers; + + // Fill cache with keys having unique hashes + for (int i = 0; i < total_keys; ++i) { + std::uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE], sk[CRYPTO_SECRET_KEY_SIZE]; + while (true) { + crypto_new_keypair(&node->c_random, pk, sk); + std::uint8_t h = pk[8]; + bool duplicate = false; + for (const auto &existing : pks) { + if (existing[8] == h) { + duplicate = true; + break; + } + } + if (!duplicate) + break; + } + pks.push_back(std::vector(pk, pk + CRYPTO_PUBLIC_KEY_SIZE)); + pointers.push_back(shared_key_cache_lookup(cache, pk)); + } + + // Verify high hit rate (low collisions) + int hits = 0; + for (int i = 0; i < total_keys; ++i) { + if (shared_key_cache_lookup(cache, pks[i].data()) == pointers[i]) + hits++; + } + EXPECT_EQ(hits, total_keys); +} + +} // namespace diff --git a/toxcore/sort_bench.cc b/toxcore/sort_bench.cc index df4036a3..06b67d67 100644 --- a/toxcore/sort_bench.cc +++ b/toxcore/sort_bench.cc @@ -9,6 +9,7 @@ #include #include +#include "attributes.h" #include "mem.h" #include "sort.h" #include "sort_test_util.hh" @@ -19,12 +20,12 @@ std::pair, std::minstd_rand> random_vec(benchmark::State { std::minstd_rand rng; // INT_MAX-1 so later we have room to add 1 larger element if needed. - std::uniform_int_distribution dist{ - std::numeric_limits::min(), std::numeric_limits::max() - 1}; + std::uniform_int_distribution dist{ + std::numeric_limits::min(), std::numeric_limits::max() - 1}; std::vector vec(state.range(0)); std::generate(std::begin(vec), std::end(vec), [&]() { - std::array compare_value; + std::array compare_value; std::generate( std::begin(compare_value), std::end(compare_value), [&]() { return dist(rng); }); return Some_Type{nullptr, compare_value, "hello there"}; diff --git a/toxcore/sort_test.cc b/toxcore/sort_test.cc index 74d118c9..384f905b 100644 --- a/toxcore/sort_test.cc +++ b/toxcore/sort_test.cc @@ -24,7 +24,7 @@ TEST(MergeSort, BehavesLikeStdSort) constexpr auto int_funcs = sort_funcs(); // Test with int arrays. - for (uint32_t i = 1; i < 500; ++i) { + for (std::uint32_t i = 1; i < 500; ++i) { std::vector vec(i); std::generate(std::begin(vec), std::end(vec), [&]() { return dist(rng); }); @@ -34,7 +34,7 @@ TEST(MergeSort, BehavesLikeStdSort) // If vec was accidentally sorted, add another larger element that almost definitely makes // it not sorted. if (vec == sorted) { - int const largest = *std::prev(sorted.end()) + 1; + int const largest = sorted.empty() ? 0 : sorted.back() + 1; sorted.push_back(largest); vec.insert(vec.begin(), largest); } @@ -55,7 +55,7 @@ TEST(MergeSort, WorksWithNonTrivialTypes) constexpr auto string_funcs = sort_funcs(); // Test with std::string arrays. - for (uint32_t i = 1; i < 500; ++i) { + for (std::uint32_t i = 1; i < 500; ++i) { std::vector vec(i); std::generate(std::begin(vec), std::end(vec), [&]() { return std::to_string(dist(rng)); }); @@ -67,7 +67,11 @@ TEST(MergeSort, WorksWithNonTrivialTypes) if (vec == sorted) { std::string const largest = "larger than largest int"; sorted.push_back(largest); - vec.insert(vec.begin(), largest); + vec.push_back(largest); + // Swap the last two elements. Since i >= 1, vec has at least 2 elements now. + // This guarantees the vector is not sorted because 'largest' is now before the last + // element (which is smaller than 'largest'). + std::iter_swap(vec.end() - 2, vec.end() - 1); } ASSERT_NE(vec, sorted); diff --git a/toxcore/sort_test_util.cc b/toxcore/sort_test_util.cc index ed851277..e4e2a8fd 100644 --- a/toxcore/sort_test_util.cc +++ b/toxcore/sort_test_util.cc @@ -22,7 +22,7 @@ int cmp_uint_array(const std::array &a, const std::array &b) const Sort_Funcs Some_Type::funcs = sort_funcs(); -int my_type_cmp(const void *va, const void *vb) +int my_type_cmp(const void *_Nonnull va, const void *_Nonnull vb) { const auto *a = static_cast(va); const auto *b = static_cast(vb); diff --git a/toxcore/sort_test_util.hh b/toxcore/sort_test_util.hh index 3e8a2c83..1c097171 100644 --- a/toxcore/sort_test_util.hh +++ b/toxcore/sort_test_util.hh @@ -6,49 +6,55 @@ #define C_TOXCORE_TOXCORE_SORT_TEST_UTIL_H #include +#include +#include "attributes.h" #include "sort.h" -struct Tox_Memory; +struct Memory; template constexpr Sort_Funcs sort_funcs() { - return { - [](const void *object, const void *va, const void *vb) { - const T *a = static_cast(va); - const T *b = static_cast(vb); + return {[](const void *_Nonnull object, const void *_Nonnull va, const void *_Nonnull vb) { + const T *a = static_cast(va); + const T *b = static_cast(vb); - return *a < *b; - }, - [](const void *arr, uint32_t index) -> const void * { - const T *vec = static_cast(arr); - return &vec[index]; - }, - [](void *arr, uint32_t index, const void *val) { - T *vec = static_cast(arr); - const T *value = static_cast(val); - vec[index] = *value; - }, - [](void *arr, uint32_t index, uint32_t size) -> void * { - T *vec = static_cast(arr); - return &vec[index]; - }, - [](const void *object, uint32_t size) -> void * { return new T[size]; }, - [](const void *object, void *arr, uint32_t size) { delete[] static_cast(arr); }, - }; + return *a < *b; + }, + [](const void *_Nonnull arr, std::uint32_t index) -> const void *_Nonnull {const T * vec + = static_cast(arr); + return &vec[index]; +} +, + [](void *_Nonnull arr, std::uint32_t index, const void *_Nonnull val) { + T *vec = static_cast(arr); + const T *value = static_cast(val); + vec[index] = *value; + }, + [](void *_Nonnull arr, std::uint32_t index, std::uint32_t size) -> void *_Nonnull +{ + T *vec = static_cast(arr); + return &vec[index]; +} +, [](const void *_Nonnull object, std::uint32_t size) -> void *_Nonnull { return new T[size]; } +, [](const void *_Nonnull object, void *_Nonnull arr, std::uint32_t size) { + delete[] static_cast(arr); +}, +} +; } // A realistic test case where we have a struct with some stuff and an expensive value we compare. struct Some_Type { - const Tox_Memory *mem; - std::array compare_value; - const char *name; + const Memory *_Nullable mem; + std::array compare_value; + const char *_Nullable name; static const Sort_Funcs funcs; }; -int my_type_cmp(const void *va, const void *vb); +int my_type_cmp(const void *_Nonnull va, const void *_Nonnull vb); bool operator<(const Some_Type &a, const Some_Type &b); #endif // C_TOXCORE_TOXCORE_SORT_TEST_UTIL_H diff --git a/toxcore/state.h b/toxcore/state.h index 8ae9ab66..422441c6 100644 --- a/toxcore/state.h +++ b/toxcore/state.h @@ -17,6 +17,8 @@ #ifndef C_TOXCORE_TOXCORE_STATE_H #define C_TOXCORE_TOXCORE_STATE_H +#include + #include "attributes.h" #include "logger.h" diff --git a/toxcore/test_util.hh b/toxcore/test_util.hh index 08bc9482..a4c991bc 100644 --- a/toxcore/test_util.hh +++ b/toxcore/test_util.hh @@ -9,9 +9,11 @@ #include #include -template +#include "attributes.h" + +template struct Function_Deleter { - void operator()(T *ptr) const { Delete(ptr); } + void operator()(T *_Nullable ptr) const { Delete(ptr); } }; // No default deleter, because we want to catch when we forget to specialise this one. @@ -25,9 +27,9 @@ template struct Method; template -struct Method { - template - static R invoke(void *self, Args... args) +struct Method { + template + static R invoke(void *_Nonnull self, Args... args) { return (static_cast(self)->*M)(self, args...); } @@ -69,7 +71,7 @@ Container sorted(Container arr, Less less) } template -T *require_not_null(const char *file, int line, T *ptr) +T *_Nonnull require_not_null(const char *_Nonnull file, int line, T *_Nullable ptr) { if (ptr == nullptr) { std::fprintf(stderr, "unexpected null pointer at %s:%d\n", file, line); diff --git a/toxcore/test_util_test.cc b/toxcore/test_util_test.cc index 1dc4527a..76ba20d4 100644 --- a/toxcore/test_util_test.cc +++ b/toxcore/test_util_test.cc @@ -17,10 +17,10 @@ using tox::test::SimulatedEnvironment; TEST(CryptoCoreTestUtil, RandomBytesDoesNotTouchZeroSizeArray) { SimulatedEnvironment env; - auto c_rng = env.fake_random().get_c_random(); + auto c_rng = env.fake_random().c_random(); - std::array bytes{}; - for (uint32_t i = 0; i < 100; ++i) { + std::array bytes{}; + for (std::uint32_t i = 0; i < 100; ++i) { random_bytes(&c_rng, bytes.data(), 0); ASSERT_THAT(bytes, Each(Eq(0x00))); } @@ -29,15 +29,15 @@ TEST(CryptoCoreTestUtil, RandomBytesDoesNotTouchZeroSizeArray) TEST(CryptoCoreTestUtil, RandomBytesFillsEntireArray) { SimulatedEnvironment env; - auto c_rng = env.fake_random().get_c_random(); + auto c_rng = env.fake_random().c_random(); - std::array bytes{}; + std::array bytes{}; - for (uint32_t size = 1; size < bytes.size(); ++size) { + for (std::uint32_t size = 1; size < bytes.size(); ++size) { bool const success = [&]() { // Try a few times. There ought to be a non-zero byte in our randomness at // some point. - for (uint32_t i = 0; i < 100; ++i) { + for (std::uint32_t i = 0; i < 100; ++i) { random_bytes(&c_rng, bytes.data(), bytes.size()); if (bytes[size - 1] != 0x00) { return true; @@ -52,17 +52,18 @@ TEST(CryptoCoreTestUtil, RandomBytesFillsEntireArray) TEST(CryptoCoreTestUtil, RandomBytesDoesNotBufferOverrun) { SimulatedEnvironment env; - auto c_rng = env.fake_random().get_c_random(); + auto c_rng = env.fake_random().c_random(); - std::array bytes{}; + std::array bytes{}; // Try a few times. It should never overrun. - for (uint32_t i = 0; i < 100; ++i) { - for (uint32_t diff = 1; diff < sizeof(uint64_t); ++diff) { + for (std::uint32_t i = 0; i < 100; ++i) { + for (std::uint32_t diff = 1; diff < sizeof(std::uint64_t); ++diff) { bytes = {}; random_bytes(&c_rng, bytes.data(), bytes.size() - diff); // All bytes not in the range we want to write should be 0. - ASSERT_THAT(std::vector(bytes.begin() + (bytes.size() - diff), bytes.end()), + ASSERT_THAT( + std::vector(bytes.begin() + (bytes.size() - diff), bytes.end()), Each(Eq(0x00))); } } diff --git a/toxcore/timed_auth.h b/toxcore/timed_auth.h index bd26a9e0..b4d0cdad 100644 --- a/toxcore/timed_auth.h +++ b/toxcore/timed_auth.h @@ -4,6 +4,9 @@ #ifndef C_TOXCORE_TOXCORE_TIMED_AUTH_H #define C_TOXCORE_TOXCORE_TIMED_AUTH_H +#include +#include + #include "attributes.h" #include "crypto_core.h" #include "mono_time.h" diff --git a/toxcore/tox.c b/toxcore/tox.c index e830a25a..faea0e43 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -28,6 +28,7 @@ #include "logger.h" #include "mem.h" #include "mono_time.h" +#include "net.h" #include "net_crypto.h" #include "network.h" #include "onion_client.h" @@ -173,7 +174,7 @@ static void tox_friend_read_receipt_handler(Messenger *_Nonnull m, uint32_t frie } static m_friend_request_cb tox_friend_request_handler; -static void tox_friend_request_handler(Messenger *_Nonnull m, const uint8_t public_key[TOX_PUBLIC_KEY_SIZE], const uint8_t *_Nonnull message, size_t length, +static void tox_friend_request_handler(Messenger *_Nonnull m, const Tox_Public_Key public_key, const uint8_t *_Nonnull message, size_t length, void *_Nullable user_data) { struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data; @@ -673,7 +674,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ const Network *const ns = sys->ns; const Memory *const mem = sys->mem; - Messenger_Options m_options = {false}; + Messenger_Options m_options = {nullptr}; m_options.dns_enabled = !tox_options_get_experimental_disable_dns(opts); @@ -735,9 +736,21 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ } 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); + + Logger *log = logger_new(mem); + + if (log == nullptr) { + SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC); + mem_delete(mem, tox); + tox_options_free(default_options); + return nullptr; + } + + tox->log = log; + + m_options.log = tox->log; + + logger_callback_log(tox->log, tox_log_handler, tox, tox_options_get_log_user_data(opts)); switch (tox_options_get_proxy_type(opts)) { case TOX_PROXY_TYPE_HTTP: { @@ -757,6 +770,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ default: { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_TYPE); + logger_kill(tox->log); mem_delete(mem, tox); tox_options_free(default_options); return nullptr; @@ -768,6 +782,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ 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); + logger_kill(tox->log); mem_delete(mem, tox); tox_options_free(default_options); return nullptr; @@ -786,6 +801,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ || !addr_resolve_or_parse_ip(ns, mem, proxy_host, &m_options.proxy_info.ip_port.ip, nullptr, dns_enabled)) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_HOST); // TODO(irungentoo): TOX_ERR_NEW_PROXY_NOT_FOUND if domain. + logger_kill(tox->log); mem_delete(mem, tox); tox_options_free(default_options); return nullptr; @@ -798,6 +814,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ if (temp_mono_time == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC); + logger_kill(tox->log); mem_delete(mem, tox); tox_options_free(default_options); return nullptr; @@ -809,6 +826,8 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ if (mutex == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC); + mono_time_free(mem, tox->mono_time); + logger_kill(tox->log); mem_delete(mem, tox); tox_options_free(default_options); return nullptr; @@ -848,6 +867,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ } mem_delete(mem, tox->mutex); + logger_kill(tox->log); mem_delete(mem, tox); tox_options_free(default_options); return nullptr; @@ -867,6 +887,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ } mem_delete(mem, tox->mutex); + logger_kill(tox->log); mem_delete(mem, tox); SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC); @@ -887,6 +908,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ } mem_delete(mem, tox->mutex); + logger_kill(tox->log); mem_delete(mem, tox); SET_ERROR_PARAMETER(error, TOX_ERR_NEW_LOAD_BAD_FORMAT); @@ -989,6 +1011,7 @@ void tox_kill(Tox *tox) LOGGER_ASSERT(tox->m->log, tox->toxav_object == nullptr, "Attempted to kill tox while toxav is still alive"); kill_groupchats(tox->m->conferences_object); kill_messenger(tox->m); + logger_kill(tox->log); mono_time_free(tox->sys.mem, tox->mono_time); tox_unlock(tox); @@ -1049,7 +1072,7 @@ void tox_get_savedata(const Tox *tox, uint8_t *savedata) tox_unlock(tox); } -static int32_t resolve_bootstrap_node(Tox *_Nullable tox, const char *_Nullable host, uint16_t port, const uint8_t public_key[TOX_PUBLIC_KEY_SIZE], +static int32_t resolve_bootstrap_node(Tox *_Nullable tox, const char *_Nullable host, uint16_t port, const Tox_Dht_Id public_key, IP_Port *_Nonnull *root, Tox_Err_Bootstrap *_Nullable error) { assert(tox != nullptr); @@ -1078,7 +1101,7 @@ static int32_t resolve_bootstrap_node(Tox *_Nullable tox, const char *_Nullable return count; } -bool tox_bootstrap(Tox *tox, const char *host, uint16_t port, const uint8_t public_key[TOX_PUBLIC_KEY_SIZE], Tox_Err_Bootstrap *error) +bool tox_bootstrap(Tox *tox, const char *host, uint16_t port, const Tox_Dht_Id public_key, Tox_Err_Bootstrap *error) { IP_Port *root; const int32_t count = resolve_bootstrap_node(tox, host, port, public_key, &root, error); @@ -1134,7 +1157,7 @@ bool tox_bootstrap(Tox *tox, const char *host, uint16_t port, const uint8_t publ return true; } -bool tox_add_tcp_relay(Tox *tox, const char *host, uint16_t port, const uint8_t public_key[TOX_PUBLIC_KEY_SIZE], +bool tox_add_tcp_relay(Tox *tox, const char *host, uint16_t port, const Tox_Dht_Id public_key, Tox_Err_Bootstrap *error) { IP_Port *root; @@ -1222,7 +1245,7 @@ void tox_iterate(Tox *tox, void *user_data) tox_unlock(tox); } -void tox_self_get_address(const Tox *tox, uint8_t address[TOX_ADDRESS_SIZE]) +void tox_self_get_address(const Tox *tox, Tox_Address address) { assert(tox != nullptr); @@ -1250,7 +1273,7 @@ uint32_t tox_self_get_nospam(const Tox *tox) return ret; } -void tox_self_get_public_key(const Tox *tox, uint8_t public_key[TOX_PUBLIC_KEY_SIZE]) +void tox_self_get_public_key(const Tox *tox, Tox_Public_Key public_key) { assert(tox != nullptr); @@ -1261,7 +1284,7 @@ void tox_self_get_public_key(const Tox *tox, uint8_t public_key[TOX_PUBLIC_KEY_S } } -void tox_self_get_secret_key(const Tox *tox, uint8_t secret_key[TOX_SECRET_KEY_SIZE]) +void tox_self_get_secret_key(const Tox *tox, Tox_Secret_Key secret_key) { assert(tox != nullptr); @@ -1421,7 +1444,7 @@ static void set_friend_error(const Logger *_Nonnull log, int32_t ret, Tox_Err_Fr } } -uint32_t tox_friend_add(Tox *tox, const uint8_t address[TOX_ADDRESS_SIZE], const uint8_t *message, size_t length, +uint32_t tox_friend_add(Tox *tox, const Tox_Address address, const uint8_t *message, size_t length, Tox_Err_Friend_Add *error) { assert(tox != nullptr); @@ -1445,7 +1468,7 @@ uint32_t tox_friend_add(Tox *tox, const uint8_t address[TOX_ADDRESS_SIZE], const return UINT32_MAX; } -uint32_t tox_friend_add_norequest(Tox *tox, const uint8_t public_key[TOX_PUBLIC_KEY_SIZE], Tox_Err_Friend_Add *error) +uint32_t tox_friend_add_norequest(Tox *tox, const Tox_Public_Key public_key, Tox_Err_Friend_Add *error) { assert(tox != nullptr); @@ -1485,7 +1508,7 @@ bool tox_friend_delete(Tox *tox, uint32_t friend_number, Tox_Err_Friend_Delete * return true; } -uint32_t tox_friend_by_public_key(const Tox *tox, const uint8_t public_key[TOX_PUBLIC_KEY_SIZE], Tox_Err_Friend_By_Public_Key *error) +uint32_t tox_friend_by_public_key(const Tox *tox, const Tox_Public_Key public_key, Tox_Err_Friend_By_Public_Key *error) { assert(tox != nullptr); @@ -1508,7 +1531,7 @@ uint32_t tox_friend_by_public_key(const Tox *tox, const uint8_t public_key[TOX_P return (uint32_t)ret; } -bool tox_friend_get_public_key(const Tox *tox, uint32_t friend_number, uint8_t public_key[TOX_PUBLIC_KEY_SIZE], +bool tox_friend_get_public_key(const Tox *tox, uint32_t friend_number, Tox_Public_Key public_key, Tox_Err_Friend_Get_Public_Key *error) { assert(tox != nullptr); @@ -1833,7 +1856,7 @@ void tox_callback_friend_message(Tox *tox, tox_friend_message_cb *callback) tox->friend_message_callback = callback; } -bool tox_hash(uint8_t hash[TOX_HASH_LENGTH], const uint8_t *data, size_t length) +bool tox_hash(Tox_Hash hash, const uint8_t *data, size_t length) { if (hash == nullptr || (data == nullptr && length != 0)) { return false; @@ -1963,7 +1986,7 @@ void tox_callback_file_recv_control(Tox *tox, tox_file_recv_control_cb *callback tox->file_recv_control_callback = callback; } -bool tox_file_get_file_id(const Tox *tox, uint32_t friend_number, uint32_t file_number, uint8_t file_id[TOX_FILE_ID_LENGTH], +bool tox_file_get_file_id(const Tox *tox, uint32_t friend_number, uint32_t file_number, Tox_File_Id file_id, Tox_Err_File_Get *error) { assert(tox != nullptr); @@ -1991,7 +2014,35 @@ bool tox_file_get_file_id(const Tox *tox, uint32_t friend_number, uint32_t file_ 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[TOX_FILE_ID_LENGTH], +Tox_File_Number tox_file_by_id(const Tox *tox, Tox_Friend_Number friend_number, const Tox_File_Id file_id, + Tox_Err_File_By_Id *error) +{ + assert(tox != nullptr); + + if (file_id == nullptr) { + SET_ERROR_PARAMETER(error, TOX_ERR_FILE_BY_ID_NULL); + return UINT32_MAX; + } + + tox_lock(tox); + const int32_t ret = file_by_id(tox->m, friend_number, file_id); + tox_unlock(tox); + + if (ret >= 0) { + SET_ERROR_PARAMETER(error, TOX_ERR_FILE_BY_ID_OK); + return (Tox_File_Number)ret; + } + + if (ret == -1) { + SET_ERROR_PARAMETER(error, TOX_ERR_FILE_BY_ID_FRIEND_NOT_FOUND); + } else { + SET_ERROR_PARAMETER(error, TOX_ERR_FILE_BY_ID_NOT_FOUND); + } + + return UINT32_MAX; +} + +uint32_t tox_file_send(Tox *tox, uint32_t friend_number, uint32_t kind, uint64_t file_size, const Tox_File_Id file_id, const uint8_t *filename, size_t filename_length, Tox_Err_File_Send *error) { assert(tox != nullptr); @@ -2673,7 +2724,7 @@ Tox_Conference_Type tox_conference_get_type(const Tox *tox, uint32_t conference_ return (Tox_Conference_Type)ret; } -bool tox_conference_get_id(const Tox *tox, uint32_t conference_number, uint8_t id[TOX_CONFERENCE_ID_SIZE]) +bool tox_conference_get_id(const Tox *tox, uint32_t conference_number, Tox_Conference_Id id) { assert(tox != nullptr); tox_lock(tox); @@ -2683,13 +2734,13 @@ bool tox_conference_get_id(const Tox *tox, uint32_t conference_number, uint8_t i } // TODO(iphydf): Delete in 0.3.0. -bool tox_conference_get_uid(const Tox *tox, uint32_t conference_number, uint8_t uid[TOX_CONFERENCE_UID_SIZE]) +bool tox_conference_get_uid(const Tox *tox, uint32_t conference_number, Tox_Conference_Uid uid) { assert(tox != nullptr); return tox_conference_get_id(tox, conference_number, uid); } -uint32_t tox_conference_by_id(const Tox *tox, const uint8_t id[TOX_CONFERENCE_ID_SIZE], Tox_Err_Conference_By_Id *error) +uint32_t tox_conference_by_id(const Tox *tox, const Tox_Conference_Id id, Tox_Err_Conference_By_Id *error) { assert(tox != nullptr); @@ -2713,7 +2764,7 @@ uint32_t tox_conference_by_id(const Tox *tox, const uint8_t id[TOX_CONFERENCE_ID } // TODO(iphydf): Delete in 0.3.0. -uint32_t tox_conference_by_uid(const Tox *tox, const uint8_t uid[TOX_CONFERENCE_UID_SIZE], Tox_Err_Conference_By_Uid *error) +uint32_t tox_conference_by_uid(const Tox *tox, const Tox_Conference_Uid uid, Tox_Err_Conference_By_Uid *error) { assert(tox != nullptr); Tox_Err_Conference_By_Id id_error; @@ -2848,7 +2899,7 @@ void tox_callback_friend_lossless_packet(Tox *tox, tox_friend_lossless_packet_cb } } -void tox_self_get_dht_id(const Tox *tox, uint8_t dht_id[TOX_PUBLIC_KEY_SIZE]) +void tox_self_get_dht_id(const Tox *tox, Tox_Dht_Id dht_id) { assert(tox != nullptr); @@ -3050,7 +3101,7 @@ uint32_t tox_group_new(Tox *tox, Tox_Group_Privacy_State privacy_state, const ui return UINT32_MAX; } -uint32_t tox_group_join(Tox *tox, const uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE], const uint8_t *name, size_t name_length, +uint32_t tox_group_join(Tox *tox, const Tox_Group_Chat_Id chat_id, const uint8_t *name, size_t name_length, const uint8_t *password, size_t password_length, Tox_Err_Group_Join *error) { assert(tox != nullptr); @@ -3414,7 +3465,7 @@ uint32_t tox_group_self_get_peer_id(const Tox *tox, uint32_t group_number, Tox_E return gc_peer_id_to_int(ret); } -bool tox_group_self_get_public_key(const Tox *tox, uint32_t group_number, uint8_t public_key[TOX_PUBLIC_KEY_SIZE], +bool tox_group_self_get_public_key(const Tox *tox, uint32_t group_number, Tox_Public_Key public_key, Tox_Err_Group_Self_Query *error) { assert(tox != nullptr); @@ -3540,7 +3591,7 @@ Tox_Group_Role tox_group_peer_get_role(const Tox *tox, uint32_t group_number, ui return (Tox_Group_Role)ret; } -bool tox_group_peer_get_public_key(const Tox *tox, uint32_t group_number, uint32_t peer_id, uint8_t public_key[TOX_PUBLIC_KEY_SIZE], +bool tox_group_peer_get_public_key(const Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Public_Key public_key, Tox_Err_Group_Peer_Query *error) { assert(tox != nullptr); @@ -3731,7 +3782,7 @@ bool tox_group_get_name(const Tox *tox, uint32_t group_number, uint8_t *name, To return true; } -bool tox_group_get_chat_id(const Tox *tox, uint32_t group_number, uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE], Tox_Err_Group_State_Query *error) +bool tox_group_get_chat_id(const Tox *tox, uint32_t group_number, Tox_Group_Chat_Id chat_id, Tox_Err_Group_State_Query *error) { assert(tox != nullptr); @@ -3751,6 +3802,31 @@ bool tox_group_get_chat_id(const Tox *tox, uint32_t group_number, uint8_t chat_i return true; } +Tox_Group_Number tox_group_by_id(const Tox *tox, const Tox_Group_Chat_Id chat_id, Tox_Err_Group_By_Id *error) +{ + assert(tox != nullptr); + + if (chat_id == nullptr) { + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BY_ID_NULL); + return UINT32_MAX; + } + + tox_lock(tox); + const GC_Chat *chat = gc_get_group_by_public_key(tox->m->group_handler, chat_id); + + if (chat == nullptr) { + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BY_ID_NOT_FOUND); + tox_unlock(tox); + return UINT32_MAX; + } + + SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BY_ID_OK); + const uint32_t ret = chat->group_number; + tox_unlock(tox); + + return ret; +} + uint32_t tox_group_get_number_groups(const Tox *tox) { assert(tox != nullptr); diff --git a/toxcore/tox.h b/toxcore/tox.h index 61910cde..21e62724 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h @@ -207,12 +207,14 @@ bool tox_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch); */ /** - * @brief The size of a Tox Public Key in bytes. + * @brief The size of a long term Tox Public Key in bytes. */ #define TOX_PUBLIC_KEY_SIZE 32 uint32_t tox_public_key_size(void); +typedef uint8_t Tox_Public_Key[TOX_PUBLIC_KEY_SIZE]; + /** * @brief The size of a Tox Secret Key in bytes. */ @@ -220,6 +222,21 @@ uint32_t tox_public_key_size(void); uint32_t tox_secret_key_size(void); +typedef uint8_t Tox_Secret_Key[TOX_SECRET_KEY_SIZE]; + +/** + * @brief The size of a Tox DHT Public Key in bytes. + * + * A DHT public key is never the same as the long term public key part of the + * Tox address. It can be long-term (for DHT bootstrap nodes) or short term + * ephemeral (for Tox client nodes). + */ +#define TOX_DHT_ID_SIZE 32 + +uint32_t tox_dht_id_size(void); + +typedef uint8_t Tox_Dht_Id[TOX_DHT_ID_SIZE]; + /** * @brief The size of a Tox Conference unique id in bytes. * @@ -229,6 +246,8 @@ uint32_t tox_secret_key_size(void); uint32_t tox_conference_uid_size(void); +typedef uint8_t Tox_Conference_Uid[TOX_CONFERENCE_UID_SIZE]; + /** * @brief The size of a Tox Conference unique id in bytes. */ @@ -236,6 +255,8 @@ uint32_t tox_conference_uid_size(void); uint32_t tox_conference_id_size(void); +typedef uint8_t Tox_Conference_Id[TOX_CONFERENCE_ID_SIZE]; + /** * @brief The size of the nospam in bytes when written in a Tox address. */ @@ -257,6 +278,8 @@ uint32_t tox_nospam_size(void); uint32_t tox_address_size(void); +typedef uint8_t Tox_Address[TOX_ADDRESS_SIZE]; + /** * @brief Maximum length of a nickname in bytes. * @@ -309,6 +332,8 @@ uint32_t tox_max_custom_packet_size(void); uint32_t tox_hash_length(void); +typedef uint8_t Tox_Hash[TOX_HASH_LENGTH]; + /** * @brief The number of bytes in a file id. */ @@ -316,6 +341,8 @@ uint32_t tox_hash_length(void); uint32_t tox_file_id_length(void); +typedef uint8_t Tox_File_Id[TOX_FILE_ID_LENGTH]; + /** * @brief Maximum file name length for file transfers. * @@ -541,7 +568,7 @@ const char *tox_err_bootstrap_to_string(Tox_Err_Bootstrap value); /** * @brief Sends a "nodes request" to the given bootstrap node with IP, port, - * and public key to setup connections. + * and DHT 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. @@ -550,11 +577,11 @@ const char *tox_err_bootstrap_to_string(Tox_Err_Bootstrap value); * at most TOX_MAX_HOSTNAME_LENGTH chars, including the NUL byte. * @param port The port on the host on which the bootstrap Tox instance is * listening. - * @param public_key The long term public key of the bootstrap node - * (TOX_PUBLIC_KEY_SIZE bytes). + * @param public_key The DHT public key of the bootstrap node + * (TOX_DHT_ID_SIZE bytes). * @return true on success. */ -bool tox_bootstrap(Tox *tox, const char *host, uint16_t port, const uint8_t public_key[TOX_PUBLIC_KEY_SIZE], Tox_Err_Bootstrap *error); +bool tox_bootstrap(Tox *tox, const char *host, uint16_t port, const Tox_Dht_Id public_key, Tox_Err_Bootstrap *error); /** * @brief Adds additional host:port pair as TCP relay. @@ -566,11 +593,11 @@ bool tox_bootstrap(Tox *tox, const char *host, uint16_t port, const uint8_t publ * @param host The hostname or IP address (IPv4 or IPv6) of the TCP relay. * Must be at most TOX_MAX_HOSTNAME_LENGTH chars, including the NUL byte. * @param port The port on the host on which the TCP relay is listening. - * @param public_key The long term public key of the TCP relay - * (TOX_PUBLIC_KEY_SIZE bytes). + * @param public_key The DHT public key of the TCP relay + * (TOX_DHT_ID_SIZE bytes). * @return true on success. */ -bool tox_add_tcp_relay(Tox *tox, const char *host, uint16_t port, const uint8_t public_key[TOX_PUBLIC_KEY_SIZE], Tox_Err_Bootstrap *error); +bool tox_add_tcp_relay(Tox *tox, const char *host, uint16_t port, const Tox_Dht_Id public_key, Tox_Err_Bootstrap *error); /** * @brief Protocols that can be used to connect to the network or friends. @@ -668,7 +695,7 @@ void tox_iterate(Tox *tox, void *user_data); * parameter is NULL, this function has no effect. * @see TOX_ADDRESS_SIZE for the address format. */ -void tox_self_get_address(const Tox *tox, uint8_t address[TOX_ADDRESS_SIZE]); +void tox_self_get_address(const Tox *tox, Tox_Address address); /** * @brief Set the 4-byte nospam part of the address. @@ -693,7 +720,7 @@ uint32_t tox_self_get_nospam(const Tox *tox); * @param public_key A memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If * this parameter is NULL, this function has no effect. */ -void tox_self_get_public_key(const Tox *tox, uint8_t public_key[TOX_PUBLIC_KEY_SIZE]); +void tox_self_get_public_key(const Tox *tox, Tox_Public_Key public_key); /** * @brief Copy the Tox Secret Key from the Tox object. @@ -701,7 +728,7 @@ void tox_self_get_public_key(const Tox *tox, uint8_t public_key[TOX_PUBLIC_KEY_S * @param secret_key A memory region of at least TOX_SECRET_KEY_SIZE bytes. If * this parameter is NULL, this function has no effect. */ -void tox_self_get_secret_key(const Tox *tox, uint8_t secret_key[TOX_SECRET_KEY_SIZE]); +void tox_self_get_secret_key(const Tox *tox, Tox_Secret_Key secret_key); /** @} */ @@ -906,7 +933,7 @@ const char *tox_err_friend_add_to_string(Tox_Err_Friend_Add value); * @return the friend number on success, an unspecified value on failure. */ Tox_Friend_Number tox_friend_add( - Tox *tox, const uint8_t address[TOX_ADDRESS_SIZE], + Tox *tox, const Tox_Address address, const uint8_t message[], size_t length, Tox_Err_Friend_Add *error); @@ -929,7 +956,7 @@ Tox_Friend_Number tox_friend_add( * @see tox_friend_add for a more detailed description of friend numbers. */ Tox_Friend_Number tox_friend_add_norequest( - Tox *tox, const uint8_t public_key[TOX_PUBLIC_KEY_SIZE], Tox_Err_Friend_Add *error); + Tox *tox, const Tox_Public_Key public_key, Tox_Err_Friend_Add *error); typedef enum Tox_Err_Friend_Delete { @@ -995,7 +1022,7 @@ const char *tox_err_friend_by_public_key_to_string(Tox_Err_Friend_By_Public_Key * * @return the friend number on success, an unspecified value on failure. */ -Tox_Friend_Number tox_friend_by_public_key(const Tox *tox, const uint8_t public_key[TOX_PUBLIC_KEY_SIZE], Tox_Err_Friend_By_Public_Key *error); +Tox_Friend_Number tox_friend_by_public_key(const Tox *tox, const Tox_Public_Key public_key, Tox_Err_Friend_By_Public_Key *error); /** * @brief Checks if a friend with the given friend number exists and returns @@ -1049,7 +1076,7 @@ const char *tox_err_friend_get_public_key_to_string(Tox_Err_Friend_Get_Public_Ke * @return true on success. */ bool tox_friend_get_public_key( - const Tox *tox, Tox_Friend_Number friend_number, uint8_t public_key[TOX_PUBLIC_KEY_SIZE], + const Tox *tox, Tox_Friend_Number friend_number, Tox_Public_Key public_key, Tox_Err_Friend_Get_Public_Key *error); typedef enum Tox_Err_Friend_Get_Last_Online { @@ -1448,7 +1475,7 @@ void tox_callback_friend_read_receipt(Tox *tox, tox_friend_read_receipt_cb *call * @param length The size of the message byte array. */ typedef void tox_friend_request_cb( - Tox *tox, const uint8_t public_key[TOX_PUBLIC_KEY_SIZE], + Tox *tox, const Tox_Public_Key public_key, const uint8_t message[], size_t length, void *user_data); @@ -1507,7 +1534,7 @@ typedef uint32_t Tox_File_Number; * * @return true if hash was not NULL. */ -bool tox_hash(uint8_t hash[TOX_HASH_LENGTH], const uint8_t data[], size_t length); +bool tox_hash(Tox_Hash hash, const uint8_t data[], size_t length); /** * @brief A list of pre-defined file kinds. @@ -1516,7 +1543,8 @@ bool tox_hash(uint8_t hash[TOX_HASH_LENGTH], const uint8_t data[], size_t length * These are a hint to the client telling it what use the sender intended for * the file. The `kind` parameter in the send function and recv callback are * `uint32_t`, not Tox_File_Kind, because clients can invent their own file - * kind. Unknown file kinds should be treated as TOX_FILE_KIND_DATA. + * kind. To avoid collisions, new client invented file kinds should avoid the first 256. + * Unknown file kinds should be treated as TOX_FILE_KIND_DATA. */ enum Tox_File_Kind { @@ -1524,7 +1552,7 @@ enum Tox_File_Kind { * Arbitrary file data. Clients can choose to handle it based on the file * name or magic or any other way they choose. */ - TOX_FILE_KIND_DATA, + TOX_FILE_KIND_DATA = 0, /** * Avatar file_id. This consists of tox_hash(image). @@ -1546,7 +1574,18 @@ enum Tox_File_Kind { * When file_size is set to 0 in the transfer request it means that the * client has no avatar. */ - TOX_FILE_KIND_AVATAR, + TOX_FILE_KIND_AVATAR = 1, + + /** + * To be specified. + */ + TOX_FILE_KIND_STICKER = 2, + + /** + * Here the file_id is the specified hash of the data. + */ + TOX_FILE_KIND_SHA1 = 3, + TOX_FILE_KIND_SHA256 = 4, }; @@ -1759,9 +1798,47 @@ const char *tox_err_file_get_to_string(Tox_Err_File_Get value); */ bool tox_file_get_file_id( const Tox *tox, Tox_Friend_Number friend_number, Tox_File_Number file_number, - uint8_t file_id[TOX_FILE_ID_LENGTH], + Tox_File_Id file_id, Tox_Err_File_Get *error); +typedef enum Tox_Err_File_By_Id { + + /** + * The function returned successfully. + */ + TOX_ERR_FILE_BY_ID_OK, + + /** + * One of the arguments to the function was NULL when it was not expected. + */ + TOX_ERR_FILE_BY_ID_NULL, + + /** + * The friend_number passed did not designate a valid friend. + */ + TOX_ERR_FILE_BY_ID_FRIEND_NOT_FOUND, + + /** + * No file transfer with the given ID exists. + */ + TOX_ERR_FILE_BY_ID_NOT_FOUND, + +} Tox_Err_File_By_Id; + +const char *tox_err_file_by_id_to_string(Tox_Err_File_By_Id value); + +/** + * Return the file number associated with the specified friend number and File ID. + * + * @param friend_number The friend number of the friend the file is being sent to or received from. + * @param file_id A memory region of at least TOX_FILE_ID_LENGTH bytes. + * + * @return the file number on success, UINT32_MAX on failure. + */ +Tox_File_Number tox_file_by_id( + const Tox *tox, Tox_Friend_Number friend_number, const Tox_File_Id file_id, + Tox_Err_File_By_Id *error); + /** @} */ /** @{ @@ -1867,7 +1944,7 @@ const char *tox_err_file_send_to_string(Tox_Err_File_Send value); */ Tox_File_Number tox_file_send( Tox *tox, Tox_Friend_Number friend_number, uint32_t kind, uint64_t file_size, - const uint8_t file_id[TOX_FILE_ID_LENGTH], const uint8_t filename[], size_t filename_length, + const Tox_File_Id file_id, const uint8_t filename[], size_t filename_length, Tox_Err_File_Send *error); typedef enum Tox_Err_File_Send_Chunk { @@ -2323,7 +2400,7 @@ bool tox_conference_peer_get_name( */ bool tox_conference_peer_get_public_key( const Tox *tox, Tox_Conference_Number conference_number, Tox_Conference_Peer_Number peer_number, - uint8_t public_key[TOX_PUBLIC_KEY_SIZE], Tox_Err_Conference_Peer_Query *error); + Tox_Public_Key public_key, Tox_Err_Conference_Peer_Query *error); /** * @brief Return true if passed peer_number corresponds to our own. @@ -2378,7 +2455,7 @@ bool tox_conference_offline_peer_get_name( */ bool tox_conference_offline_peer_get_public_key( const Tox *tox, Tox_Conference_Number conference_number, - Tox_Conference_Offline_Peer_Number offline_peer_number, uint8_t public_key[TOX_PUBLIC_KEY_SIZE], Tox_Err_Conference_Peer_Query *error); + Tox_Conference_Offline_Peer_Number offline_peer_number, Tox_Public_Key public_key, Tox_Err_Conference_Peer_Query *error); /** * @brief Return a unix-time timestamp of the last time offline_peer_number was @@ -2707,7 +2784,7 @@ Tox_Conference_Type tox_conference_get_type( * @return true on success. */ bool tox_conference_get_id( - const Tox *tox, Tox_Conference_Number conference_number, uint8_t id[TOX_CONFERENCE_ID_SIZE]); + const Tox *tox, Tox_Conference_Number conference_number, Tox_Conference_Id id); typedef enum Tox_Err_Conference_By_Id { @@ -2738,7 +2815,7 @@ const char *tox_err_conference_by_id_to_string(Tox_Err_Conference_By_Id value); * @return the conference number on success, an unspecified value on failure. */ Tox_Conference_Number tox_conference_by_id( - const Tox *tox, const uint8_t id[TOX_CONFERENCE_ID_SIZE], Tox_Err_Conference_By_Id *error); + const Tox *tox, const Tox_Conference_Id id, Tox_Err_Conference_By_Id *error); #ifndef TOX_HIDE_DEPRECATED /** @@ -2754,7 +2831,7 @@ Tox_Conference_Number tox_conference_by_id( * just renamed). */ bool tox_conference_get_uid( - const Tox *tox, Tox_Conference_Number conference_number, uint8_t uid[TOX_CONFERENCE_UID_SIZE]); + const Tox *tox, Tox_Conference_Number conference_number, Tox_Conference_Uid uid); #endif /* TOX_HIDE_DEPRECATED */ typedef enum Tox_Err_Conference_By_Uid { @@ -2790,7 +2867,7 @@ const char *tox_err_conference_by_uid_to_string(Tox_Err_Conference_By_Uid value) * just renamed). */ Tox_Conference_Number tox_conference_by_uid( - const Tox *tox, const uint8_t uid[TOX_CONFERENCE_UID_SIZE], Tox_Err_Conference_By_Uid *error); + const Tox *tox, const Tox_Conference_Uid uid, Tox_Err_Conference_By_Uid *error); #endif /* TOX_HIDE_DEPRECATED */ /** @} */ @@ -2962,10 +3039,10 @@ const char *tox_err_get_port_to_string(Tox_Err_Get_Port value); * Be aware that every time a new instance is created, the DHT public key * changes, meaning this cannot be used to run a permanent bootstrap node. * - * @param dht_id A memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this + * @param dht_id A memory region of at least TOX_DHT_ID_SIZE bytes. If this * parameter is NULL, this function has no effect. */ -void tox_self_get_dht_id(const Tox *tox, uint8_t dht_id[TOX_PUBLIC_KEY_SIZE]); +void tox_self_get_dht_id(const Tox *tox, Tox_Dht_Id dht_id); /** * @brief Return the UDP port this Tox instance is bound to. @@ -3051,6 +3128,8 @@ uint32_t tox_group_max_password_size(void); uint32_t tox_group_chat_id_size(void); +typedef uint8_t Tox_Group_Chat_Id[TOX_GROUP_CHAT_ID_SIZE]; + /** * Size of a peer public key. */ @@ -3058,6 +3137,8 @@ uint32_t tox_group_chat_id_size(void); uint32_t tox_group_peer_public_key_size(void); +typedef uint8_t Tox_Group_Peer_Public_Key[TOX_GROUP_PEER_PUBLIC_KEY_SIZE]; + /******************************************************************************* * * :: Group chat state enumerators @@ -3317,7 +3398,7 @@ const char *tox_err_group_join_to_string(Tox_Err_Group_Join value); * @return group_number on success, UINT32_MAX on failure. */ Tox_Group_Number tox_group_join( - Tox *tox, const uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE], + Tox *tox, const Tox_Group_Chat_Id chat_id, const uint8_t name[], size_t name_length, const uint8_t password[], size_t password_length, Tox_Err_Group_Join *error); @@ -3633,7 +3714,7 @@ Tox_Group_Peer_Number tox_group_self_get_peer_id(const Tox *tox, Tox_Group_Numbe * * @return true on success. */ -bool tox_group_self_get_public_key(const Tox *tox, Tox_Group_Number group_number, uint8_t public_key[TOX_PUBLIC_KEY_SIZE], +bool tox_group_self_get_public_key(const Tox *tox, Tox_Group_Number group_number, Tox_Public_Key public_key, Tox_Err_Group_Self_Query *error); /******************************************************************************* @@ -3758,7 +3839,7 @@ Tox_Connection tox_group_peer_get_connection_status(const Tox *tox, Tox_Group_Nu */ bool tox_group_peer_get_public_key( const Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id, - uint8_t public_key[TOX_PUBLIC_KEY_SIZE], Tox_Err_Group_Peer_Query *error); + Tox_Public_Key public_key, Tox_Err_Group_Peer_Query *error); /** * @param group_number The group number of the group the name change is intended @@ -3957,9 +4038,39 @@ bool tox_group_get_name( * @return true on success. */ bool tox_group_get_chat_id( - const Tox *tox, Tox_Group_Number group_number, uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE], + const Tox *tox, Tox_Group_Number group_number, Tox_Group_Chat_Id chat_id, Tox_Err_Group_State_Query *error); +typedef enum Tox_Err_Group_By_Id { + /** + * The function returned successfully. + */ + TOX_ERR_GROUP_BY_ID_OK, + + /** + * One of the arguments to the function was NULL when it was not expected. + */ + TOX_ERR_GROUP_BY_ID_NULL, + + /** + * No group with the given ID exists on the group list. + */ + TOX_ERR_GROUP_BY_ID_NOT_FOUND, + +} Tox_Err_Group_By_Id; + +const char *tox_err_group_by_id_to_string(Tox_Err_Group_By_Id value); + +/** + * Return the group number associated with the specified Chat ID. + * + * @param chat_id A byte array containing the Chat ID (TOX_GROUP_CHAT_ID_SIZE). + * + * @return the group number on success, UINT32_MAX on failure. + */ +Tox_Group_Number tox_group_by_id( + const Tox *tox, const Tox_Group_Chat_Id chat_id, Tox_Err_Group_By_Id *error); + /** * Return the number of groups in the Tox chats array. */ diff --git a/toxcore/tox_api.c b/toxcore/tox_api.c index 808cd473..add011cf 100644 --- a/toxcore/tox_api.c +++ b/toxcore/tox_api.c @@ -26,6 +26,10 @@ uint32_t tox_secret_key_size(void) { return TOX_SECRET_KEY_SIZE; } +uint32_t tox_dht_id_size(void) +{ + return TOX_DHT_ID_SIZE; +} uint32_t tox_conference_uid_size(void) { return TOX_CONFERENCE_UID_SIZE; @@ -508,6 +512,24 @@ const char *tox_err_file_get_to_string(Tox_Err_File_Get value) return ""; } +const char *tox_err_file_by_id_to_string(Tox_Err_File_By_Id value) +{ + switch (value) { + case TOX_ERR_FILE_BY_ID_OK: + return "TOX_ERR_FILE_BY_ID_OK"; + + case TOX_ERR_FILE_BY_ID_NULL: + return "TOX_ERR_FILE_BY_ID_NULL"; + + case TOX_ERR_FILE_BY_ID_FRIEND_NOT_FOUND: + return "TOX_ERR_FILE_BY_ID_FRIEND_NOT_FOUND"; + + case TOX_ERR_FILE_BY_ID_NOT_FOUND: + return "TOX_ERR_FILE_BY_ID_NOT_FOUND"; + } + + return ""; +} const char *tox_err_file_send_to_string(Tox_Err_File_Send value) { switch (value) { @@ -883,6 +905,21 @@ const char *tox_err_group_new_to_string(Tox_Err_Group_New value) return ""; } +const char *tox_err_group_by_id_to_string(Tox_Err_Group_By_Id value) +{ + switch (value) { + case TOX_ERR_GROUP_BY_ID_OK: + return "TOX_ERR_GROUP_BY_ID_OK"; + + case TOX_ERR_GROUP_BY_ID_NULL: + return "TOX_ERR_GROUP_BY_ID_NULL"; + + case TOX_ERR_GROUP_BY_ID_NOT_FOUND: + return "TOX_ERR_GROUP_BY_ID_NOT_FOUND"; + } + + return ""; +} const char *tox_err_group_join_to_string(Tox_Err_Group_Join value) { switch (value) { diff --git a/toxcore/tox_attributes.h b/toxcore/tox_attributes.h index fa646db0..2f868f7d 100644 --- a/toxcore/tox_attributes.h +++ b/toxcore/tox_attributes.h @@ -1,12 +1,9 @@ /* SPDX-License-Identifier: GPL-3.0-or-later - * Copyright © 2022-2025 The TokTok team. + * Copyright © 2022-2026 The TokTok team. */ /** * nonnull attributes for GCC/Clang and Cimple. - * - * This file is a modified version of c-toxcore/toxcore/attributes.h with a - * `tox_` prefix added to the macros to avoid conflicts with client code. */ #ifndef C_TOXCORE_TOXCORE_TOX_ATTRIBUTES_H #define C_TOXCORE_TOXCORE_TOX_ATTRIBUTES_H @@ -20,12 +17,6 @@ #define _Nullable #endif -#ifdef SPARSE -#define tox_bitwise __attribute__((bitwise)) -#else -#define tox_bitwise -#endif - //!TOKSTYLE+ #endif /* C_TOXCORE_TOXCORE_TOX_ATTRIBUTES_H */ diff --git a/toxcore/tox_event.h b/toxcore/tox_event.h index ebbf7b35..857e1db1 100644 --- a/toxcore/tox_event.h +++ b/toxcore/tox_event.h @@ -5,6 +5,8 @@ #ifndef C_TOXCORE_TOXCORE_TOX_EVENT_H #define C_TOXCORE_TOXCORE_TOX_EVENT_H +#include + #include "attributes.h" #include "bin_pack.h" #include "bin_unpack.h" @@ -253,6 +255,47 @@ bool tox_event_group_join_fail_unpack(Tox_Event_Group_Join_Fail *_Nonnull *_Nonn bool tox_event_group_moderation_unpack(Tox_Event_Group_Moderation *_Nonnull *_Nonnull event, Bin_Unpack *_Nonnull bu, const Memory *_Nonnull mem); bool tox_event_dht_nodes_response_unpack(Tox_Event_Dht_Nodes_Response *_Nonnull *_Nonnull event, Bin_Unpack *_Nonnull bu, const Memory *_Nonnull mem); +void tox_events_handle_conference_connected_dispatch(Tox *_Nonnull tox, const Tox_Event_Conference_Connected *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_conference_invite_dispatch(Tox *_Nonnull tox, const Tox_Event_Conference_Invite *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_conference_message_dispatch(Tox *_Nonnull tox, const Tox_Event_Conference_Message *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_conference_peer_list_changed_dispatch(Tox *_Nonnull tox, const Tox_Event_Conference_Peer_List_Changed *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_conference_peer_name_dispatch(Tox *_Nonnull tox, const Tox_Event_Conference_Peer_Name *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_conference_title_dispatch(Tox *_Nonnull tox, const Tox_Event_Conference_Title *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_dht_nodes_response_dispatch(Tox *_Nonnull tox, const Tox_Event_Dht_Nodes_Response *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_file_chunk_request_dispatch(Tox *_Nonnull tox, const Tox_Event_File_Chunk_Request *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_file_recv_chunk_dispatch(Tox *_Nonnull tox, const Tox_Event_File_Recv_Chunk *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_file_recv_control_dispatch(Tox *_Nonnull tox, const Tox_Event_File_Recv_Control *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_file_recv_dispatch(Tox *_Nonnull tox, const Tox_Event_File_Recv *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_friend_connection_status_dispatch(Tox *_Nonnull tox, const Tox_Event_Friend_Connection_Status *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_friend_lossless_packet_dispatch(Tox *_Nonnull tox, const Tox_Event_Friend_Lossless_Packet *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_friend_lossy_packet_dispatch(Tox *_Nonnull tox, const Tox_Event_Friend_Lossy_Packet *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_friend_message_dispatch(Tox *_Nonnull tox, const Tox_Event_Friend_Message *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_friend_name_dispatch(Tox *_Nonnull tox, const Tox_Event_Friend_Name *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_friend_read_receipt_dispatch(Tox *_Nonnull tox, const Tox_Event_Friend_Read_Receipt *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_friend_request_dispatch(Tox *_Nonnull tox, const Tox_Event_Friend_Request *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_friend_status_dispatch(Tox *_Nonnull tox, const Tox_Event_Friend_Status *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_friend_status_message_dispatch(Tox *_Nonnull tox, const Tox_Event_Friend_Status_Message *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_friend_typing_dispatch(Tox *_Nonnull tox, const Tox_Event_Friend_Typing *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_self_connection_status_dispatch(Tox *_Nonnull tox, const Tox_Event_Self_Connection_Status *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_peer_name_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Peer_Name *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_peer_status_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Peer_Status *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_topic_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Topic *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_privacy_state_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Privacy_State *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_voice_state_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Voice_State *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_topic_lock_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Topic_Lock *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_peer_limit_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Peer_Limit *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_password_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Password *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_message_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Message *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_private_message_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Private_Message *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_custom_packet_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Custom_Packet *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_custom_private_packet_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Custom_Private_Packet *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_invite_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Invite *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_peer_join_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Peer_Join *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_peer_exit_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Peer_Exit *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_self_join_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Self_Join *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_join_fail_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Join_Fail *_Nonnull event, void *_Nullable user_data); +void tox_events_handle_group_moderation_dispatch(Tox *_Nonnull tox, const Tox_Event_Group_Moderation *_Nonnull event, void *_Nullable user_data); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/toxcore/tox_events.c b/toxcore/tox_events.c index 05aae5a5..b67bc68c 100644 --- a/toxcore/tox_events.c +++ b/toxcore/tox_events.c @@ -177,6 +177,225 @@ Tox_Events *tox_events_load(const Tox_System *sys, const uint8_t *bytes, uint32_ return events; } +void tox_events_dispatch(Tox *tox, Tox_Events *events, void *user_data) +{ + if (events == nullptr) { + return; + } + + const uint32_t size = tox_events_get_size(events); + + for (uint32_t i = 0; i < size; ++i) { + const Tox_Event *event = tox_events_get(events, i); + const Tox_Event_Type type = tox_event_get_type(event); + + switch (type) { + case TOX_EVENT_SELF_CONNECTION_STATUS: { + tox_events_handle_self_connection_status_dispatch(tox, event->data.self_connection_status, user_data); + break; + } + + case TOX_EVENT_FRIEND_REQUEST: { + tox_events_handle_friend_request_dispatch(tox, event->data.friend_request, user_data); + break; + } + + case TOX_EVENT_FRIEND_CONNECTION_STATUS: { + tox_events_handle_friend_connection_status_dispatch(tox, event->data.friend_connection_status, user_data); + break; + } + + case TOX_EVENT_FRIEND_LOSSY_PACKET: { + tox_events_handle_friend_lossy_packet_dispatch(tox, event->data.friend_lossy_packet, user_data); + break; + } + + case TOX_EVENT_FRIEND_LOSSLESS_PACKET: { + tox_events_handle_friend_lossless_packet_dispatch(tox, event->data.friend_lossless_packet, user_data); + break; + } + + case TOX_EVENT_FRIEND_NAME: { + tox_events_handle_friend_name_dispatch(tox, event->data.friend_name, user_data); + break; + } + + case TOX_EVENT_FRIEND_STATUS: { + tox_events_handle_friend_status_dispatch(tox, event->data.friend_status, user_data); + break; + } + + case TOX_EVENT_FRIEND_STATUS_MESSAGE: { + tox_events_handle_friend_status_message_dispatch(tox, event->data.friend_status_message, user_data); + break; + } + + case TOX_EVENT_FRIEND_MESSAGE: { + tox_events_handle_friend_message_dispatch(tox, event->data.friend_message, user_data); + break; + } + + case TOX_EVENT_FRIEND_READ_RECEIPT: { + tox_events_handle_friend_read_receipt_dispatch(tox, event->data.friend_read_receipt, user_data); + break; + } + + case TOX_EVENT_FRIEND_TYPING: { + tox_events_handle_friend_typing_dispatch(tox, event->data.friend_typing, user_data); + break; + } + + case TOX_EVENT_FILE_CHUNK_REQUEST: { + tox_events_handle_file_chunk_request_dispatch(tox, event->data.file_chunk_request, user_data); + break; + } + + case TOX_EVENT_FILE_RECV: { + tox_events_handle_file_recv_dispatch(tox, event->data.file_recv, user_data); + break; + } + + case TOX_EVENT_FILE_RECV_CHUNK: { + tox_events_handle_file_recv_chunk_dispatch(tox, event->data.file_recv_chunk, user_data); + break; + } + + case TOX_EVENT_FILE_RECV_CONTROL: { + tox_events_handle_file_recv_control_dispatch(tox, event->data.file_recv_control, user_data); + break; + } + + case TOX_EVENT_CONFERENCE_INVITE: { + tox_events_handle_conference_invite_dispatch(tox, event->data.conference_invite, user_data); + break; + } + + case TOX_EVENT_CONFERENCE_CONNECTED: { + tox_events_handle_conference_connected_dispatch(tox, event->data.conference_connected, user_data); + break; + } + + case TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED: { + tox_events_handle_conference_peer_list_changed_dispatch(tox, event->data.conference_peer_list_changed, user_data); + break; + } + + case TOX_EVENT_CONFERENCE_PEER_NAME: { + tox_events_handle_conference_peer_name_dispatch(tox, event->data.conference_peer_name, user_data); + break; + } + + case TOX_EVENT_CONFERENCE_TITLE: { + tox_events_handle_conference_title_dispatch(tox, event->data.conference_title, user_data); + break; + } + + case TOX_EVENT_CONFERENCE_MESSAGE: { + tox_events_handle_conference_message_dispatch(tox, event->data.conference_message, user_data); + break; + } + + case TOX_EVENT_GROUP_PEER_NAME: { + tox_events_handle_group_peer_name_dispatch(tox, event->data.group_peer_name, user_data); + break; + } + + case TOX_EVENT_GROUP_PEER_STATUS: { + tox_events_handle_group_peer_status_dispatch(tox, event->data.group_peer_status, user_data); + break; + } + + case TOX_EVENT_GROUP_TOPIC: { + tox_events_handle_group_topic_dispatch(tox, event->data.group_topic, user_data); + break; + } + + case TOX_EVENT_GROUP_PRIVACY_STATE: { + tox_events_handle_group_privacy_state_dispatch(tox, event->data.group_privacy_state, user_data); + break; + } + + case TOX_EVENT_GROUP_VOICE_STATE: { + tox_events_handle_group_voice_state_dispatch(tox, event->data.group_voice_state, user_data); + break; + } + + case TOX_EVENT_GROUP_TOPIC_LOCK: { + tox_events_handle_group_topic_lock_dispatch(tox, event->data.group_topic_lock, user_data); + break; + } + + case TOX_EVENT_GROUP_PEER_LIMIT: { + tox_events_handle_group_peer_limit_dispatch(tox, event->data.group_peer_limit, user_data); + break; + } + + case TOX_EVENT_GROUP_PASSWORD: { + tox_events_handle_group_password_dispatch(tox, event->data.group_password, user_data); + break; + } + + case TOX_EVENT_GROUP_MESSAGE: { + tox_events_handle_group_message_dispatch(tox, event->data.group_message, user_data); + break; + } + + case TOX_EVENT_GROUP_PRIVATE_MESSAGE: { + tox_events_handle_group_private_message_dispatch(tox, event->data.group_private_message, user_data); + break; + } + + case TOX_EVENT_GROUP_CUSTOM_PACKET: { + tox_events_handle_group_custom_packet_dispatch(tox, event->data.group_custom_packet, user_data); + break; + } + + case TOX_EVENT_GROUP_CUSTOM_PRIVATE_PACKET: { + tox_events_handle_group_custom_private_packet_dispatch(tox, event->data.group_custom_private_packet, user_data); + break; + } + + case TOX_EVENT_GROUP_INVITE: { + tox_events_handle_group_invite_dispatch(tox, event->data.group_invite, user_data); + break; + } + + case TOX_EVENT_GROUP_PEER_JOIN: { + tox_events_handle_group_peer_join_dispatch(tox, event->data.group_peer_join, user_data); + break; + } + + case TOX_EVENT_GROUP_PEER_EXIT: { + tox_events_handle_group_peer_exit_dispatch(tox, event->data.group_peer_exit, user_data); + break; + } + + case TOX_EVENT_GROUP_SELF_JOIN: { + tox_events_handle_group_self_join_dispatch(tox, event->data.group_self_join, user_data); + break; + } + + case TOX_EVENT_GROUP_JOIN_FAIL: { + tox_events_handle_group_join_fail_dispatch(tox, event->data.group_join_fail, user_data); + break; + } + + case TOX_EVENT_GROUP_MODERATION: { + tox_events_handle_group_moderation_dispatch(tox, event->data.group_moderation, user_data); + break; + } + + case TOX_EVENT_DHT_NODES_RESPONSE: { + tox_events_handle_dht_nodes_response_dispatch(tox, event->data.dht_nodes_response, user_data); + break; + } + + case TOX_EVENT_INVALID: + break; + } + } +} + bool tox_events_equal(const Tox_System *sys, const Tox_Events *a, const Tox_Events *b) { assert(sys != nullptr); diff --git a/toxcore/tox_events.h b/toxcore/tox_events.h index d82930e4..4acee2f2 100644 --- a/toxcore/tox_events.h +++ b/toxcore/tox_events.h @@ -12,7 +12,11 @@ #ifndef C_TOXCORE_TOXCORE_TOX_EVENTS_H #define C_TOXCORE_TOXCORE_TOX_EVENTS_H +#include +#include + #include "tox.h" +#include "tox_attributes.h" #ifdef __cplusplus extern "C" { @@ -20,347 +24,347 @@ extern "C" { 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); + const Tox_Event_Conference_Connected *_Nonnull 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); +const uint8_t *_Nullable tox_event_conference_invite_get_cookie( + const Tox_Event_Conference_Invite *_Nonnull conference_invite); uint32_t tox_event_conference_invite_get_cookie_length( - const Tox_Event_Conference_Invite *conference_invite); + const Tox_Event_Conference_Invite *_Nonnull conference_invite); Tox_Conference_Type tox_event_conference_invite_get_type( - const Tox_Event_Conference_Invite *conference_invite); + const Tox_Event_Conference_Invite *_Nonnull conference_invite); uint32_t tox_event_conference_invite_get_friend_number( - const Tox_Event_Conference_Invite *conference_invite); + const Tox_Event_Conference_Invite *_Nonnull 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); +const uint8_t *_Nullable tox_event_conference_message_get_message( + const Tox_Event_Conference_Message *_Nonnull conference_message); uint32_t tox_event_conference_message_get_message_length( - const Tox_Event_Conference_Message *conference_message); + const Tox_Event_Conference_Message *_Nonnull conference_message); Tox_Message_Type tox_event_conference_message_get_type( - const Tox_Event_Conference_Message *conference_message); + const Tox_Event_Conference_Message *_Nonnull conference_message); uint32_t tox_event_conference_message_get_conference_number( - const Tox_Event_Conference_Message *conference_message); + const Tox_Event_Conference_Message *_Nonnull conference_message); uint32_t tox_event_conference_message_get_peer_number( - const Tox_Event_Conference_Message *conference_message); + const Tox_Event_Conference_Message *_Nonnull 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); + const Tox_Event_Conference_Peer_List_Changed *_Nonnull 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); +const uint8_t *_Nullable tox_event_conference_peer_name_get_name( + const Tox_Event_Conference_Peer_Name *_Nonnull conference_peer_name); uint32_t tox_event_conference_peer_name_get_name_length( - const Tox_Event_Conference_Peer_Name *conference_peer_name); + const Tox_Event_Conference_Peer_Name *_Nonnull conference_peer_name); uint32_t tox_event_conference_peer_name_get_conference_number( - const Tox_Event_Conference_Peer_Name *conference_peer_name); + const Tox_Event_Conference_Peer_Name *_Nonnull conference_peer_name); uint32_t tox_event_conference_peer_name_get_peer_number( - const Tox_Event_Conference_Peer_Name *conference_peer_name); + const Tox_Event_Conference_Peer_Name *_Nonnull 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); +const uint8_t *_Nullable tox_event_conference_title_get_title( + const Tox_Event_Conference_Title *_Nonnull conference_title); uint32_t tox_event_conference_title_get_title_length( - const Tox_Event_Conference_Title *conference_title); + const Tox_Event_Conference_Title *_Nonnull conference_title); uint32_t tox_event_conference_title_get_conference_number( - const Tox_Event_Conference_Title *conference_title); + const Tox_Event_Conference_Title *_Nonnull conference_title); uint32_t tox_event_conference_title_get_peer_number( - const Tox_Event_Conference_Title *conference_title); + const Tox_Event_Conference_Title *_Nonnull 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); + const Tox_Event_File_Chunk_Request *_Nonnull file_chunk_request); uint32_t tox_event_file_chunk_request_get_file_number( - const Tox_Event_File_Chunk_Request *file_chunk_request); + const Tox_Event_File_Chunk_Request *_Nonnull file_chunk_request); uint32_t tox_event_file_chunk_request_get_friend_number( - const Tox_Event_File_Chunk_Request *file_chunk_request); + const Tox_Event_File_Chunk_Request *_Nonnull file_chunk_request); uint64_t tox_event_file_chunk_request_get_position( - const Tox_Event_File_Chunk_Request *file_chunk_request); + const Tox_Event_File_Chunk_Request *_Nonnull 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); +const uint8_t *_Nullable tox_event_file_recv_get_filename( + const Tox_Event_File_Recv *_Nonnull file_recv); uint32_t tox_event_file_recv_get_filename_length( - const Tox_Event_File_Recv *file_recv); + const Tox_Event_File_Recv *_Nonnull file_recv); uint32_t tox_event_file_recv_get_file_number( - const Tox_Event_File_Recv *file_recv); + const Tox_Event_File_Recv *_Nonnull file_recv); uint64_t tox_event_file_recv_get_file_size( - const Tox_Event_File_Recv *file_recv); + const Tox_Event_File_Recv *_Nonnull file_recv); uint32_t tox_event_file_recv_get_friend_number( - const Tox_Event_File_Recv *file_recv); + const Tox_Event_File_Recv *_Nonnull file_recv); uint32_t tox_event_file_recv_get_kind( - const Tox_Event_File_Recv *file_recv); + const Tox_Event_File_Recv *_Nonnull 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); +const uint8_t *_Nullable tox_event_file_recv_chunk_get_data( + const Tox_Event_File_Recv_Chunk *_Nonnull file_recv_chunk); uint32_t tox_event_file_recv_chunk_get_data_length( - const Tox_Event_File_Recv_Chunk *file_recv_chunk); + const Tox_Event_File_Recv_Chunk *_Nonnull file_recv_chunk); uint32_t tox_event_file_recv_chunk_get_file_number( - const Tox_Event_File_Recv_Chunk *file_recv_chunk); + const Tox_Event_File_Recv_Chunk *_Nonnull file_recv_chunk); uint32_t tox_event_file_recv_chunk_get_friend_number( - const Tox_Event_File_Recv_Chunk *file_recv_chunk); + const Tox_Event_File_Recv_Chunk *_Nonnull file_recv_chunk); uint64_t tox_event_file_recv_chunk_get_position( - const Tox_Event_File_Recv_Chunk *file_recv_chunk); + const Tox_Event_File_Recv_Chunk *_Nonnull 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); + const Tox_Event_File_Recv_Control *_Nonnull file_recv_control); uint32_t tox_event_file_recv_control_get_file_number( - const Tox_Event_File_Recv_Control *file_recv_control); + const Tox_Event_File_Recv_Control *_Nonnull file_recv_control); uint32_t tox_event_file_recv_control_get_friend_number( - const Tox_Event_File_Recv_Control *file_recv_control); + const Tox_Event_File_Recv_Control *_Nonnull 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); + const Tox_Event_Friend_Connection_Status *_Nonnull friend_connection_status); uint32_t tox_event_friend_connection_status_get_friend_number( - const Tox_Event_Friend_Connection_Status *friend_connection_status); + const Tox_Event_Friend_Connection_Status *_Nonnull 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); +const uint8_t *_Nullable tox_event_friend_lossless_packet_get_data( + const Tox_Event_Friend_Lossless_Packet *_Nonnull friend_lossless_packet); uint32_t tox_event_friend_lossless_packet_get_data_length( - const Tox_Event_Friend_Lossless_Packet *friend_lossless_packet); + const Tox_Event_Friend_Lossless_Packet *_Nonnull friend_lossless_packet); uint32_t tox_event_friend_lossless_packet_get_friend_number( - const Tox_Event_Friend_Lossless_Packet *friend_lossless_packet); + const Tox_Event_Friend_Lossless_Packet *_Nonnull 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); +const uint8_t *_Nullable tox_event_friend_lossy_packet_get_data( + const Tox_Event_Friend_Lossy_Packet *_Nonnull friend_lossy_packet); uint32_t tox_event_friend_lossy_packet_get_data_length( - const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet); + const Tox_Event_Friend_Lossy_Packet *_Nonnull friend_lossy_packet); uint32_t tox_event_friend_lossy_packet_get_friend_number( - const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet); + const Tox_Event_Friend_Lossy_Packet *_Nonnull 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); + const Tox_Event_Friend_Message *_Nonnull friend_message); Tox_Message_Type tox_event_friend_message_get_type( - const Tox_Event_Friend_Message *friend_message); + const Tox_Event_Friend_Message *_Nonnull 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); + const Tox_Event_Friend_Message *_Nonnull friend_message); +const uint8_t *_Nullable tox_event_friend_message_get_message( + const Tox_Event_Friend_Message *_Nonnull 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); +const uint8_t *_Nullable tox_event_friend_name_get_name( + const Tox_Event_Friend_Name *_Nonnull friend_name); uint32_t tox_event_friend_name_get_name_length( - const Tox_Event_Friend_Name *friend_name); + const Tox_Event_Friend_Name *_Nonnull friend_name); uint32_t tox_event_friend_name_get_friend_number( - const Tox_Event_Friend_Name *friend_name); + const Tox_Event_Friend_Name *_Nonnull 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); + const Tox_Event_Friend_Read_Receipt *_Nonnull friend_read_receipt); uint32_t tox_event_friend_read_receipt_get_message_id( - const Tox_Event_Friend_Read_Receipt *friend_read_receipt); + const Tox_Event_Friend_Read_Receipt *_Nonnull 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); +const uint8_t *_Nullable tox_event_friend_request_get_message( + const Tox_Event_Friend_Request *_Nonnull friend_request); +const uint8_t *_Nonnull tox_event_friend_request_get_public_key( + const Tox_Event_Friend_Request *_Nonnull friend_request); uint32_t tox_event_friend_request_get_message_length( - const Tox_Event_Friend_Request *friend_request); + const Tox_Event_Friend_Request *_Nonnull 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); + const Tox_Event_Friend_Status *_Nonnull friend_status); uint32_t tox_event_friend_status_get_friend_number( - const Tox_Event_Friend_Status *friend_status); + const Tox_Event_Friend_Status *_Nonnull 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); +const uint8_t *_Nullable tox_event_friend_status_message_get_message( + const Tox_Event_Friend_Status_Message *_Nonnull friend_status_message); uint32_t tox_event_friend_status_message_get_message_length( - const Tox_Event_Friend_Status_Message *friend_status_message); + const Tox_Event_Friend_Status_Message *_Nonnull friend_status_message); uint32_t tox_event_friend_status_message_get_friend_number( - const Tox_Event_Friend_Status_Message *friend_status_message); + const Tox_Event_Friend_Status_Message *_Nonnull 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); + const Tox_Event_Friend_Typing *_Nonnull friend_typing); uint32_t tox_event_friend_typing_get_friend_number( - const Tox_Event_Friend_Typing *friend_typing); + const Tox_Event_Friend_Typing *_Nonnull 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); + const Tox_Event_Self_Connection_Status *_Nonnull self_connection_status); typedef struct Tox_Event_Group_Peer_Name Tox_Event_Group_Peer_Name; uint32_t tox_event_group_peer_name_get_group_number( - const Tox_Event_Group_Peer_Name *group_peer_name); + const Tox_Event_Group_Peer_Name *_Nonnull group_peer_name); uint32_t tox_event_group_peer_name_get_peer_id( - const Tox_Event_Group_Peer_Name *group_peer_name); -const uint8_t *tox_event_group_peer_name_get_name( - const Tox_Event_Group_Peer_Name *group_peer_name); + const Tox_Event_Group_Peer_Name *_Nonnull group_peer_name); +const uint8_t *_Nullable tox_event_group_peer_name_get_name( + const Tox_Event_Group_Peer_Name *_Nonnull group_peer_name); uint32_t tox_event_group_peer_name_get_name_length( - const Tox_Event_Group_Peer_Name *group_peer_name); + const Tox_Event_Group_Peer_Name *_Nonnull group_peer_name); typedef struct Tox_Event_Group_Peer_Status Tox_Event_Group_Peer_Status; uint32_t tox_event_group_peer_status_get_group_number( - const Tox_Event_Group_Peer_Status *group_peer_status); + const Tox_Event_Group_Peer_Status *_Nonnull group_peer_status); uint32_t tox_event_group_peer_status_get_peer_id( - const Tox_Event_Group_Peer_Status *group_peer_status); + const Tox_Event_Group_Peer_Status *_Nonnull group_peer_status); Tox_User_Status tox_event_group_peer_status_get_status( - const Tox_Event_Group_Peer_Status *group_peer_status); + const Tox_Event_Group_Peer_Status *_Nonnull group_peer_status); typedef struct Tox_Event_Group_Topic Tox_Event_Group_Topic; uint32_t tox_event_group_topic_get_group_number( - const Tox_Event_Group_Topic *group_topic); + const Tox_Event_Group_Topic *_Nonnull group_topic); uint32_t tox_event_group_topic_get_peer_id( - const Tox_Event_Group_Topic *group_topic); -const uint8_t *tox_event_group_topic_get_topic( - const Tox_Event_Group_Topic *group_topic); + const Tox_Event_Group_Topic *_Nonnull group_topic); +const uint8_t *_Nullable tox_event_group_topic_get_topic( + const Tox_Event_Group_Topic *_Nonnull group_topic); uint32_t tox_event_group_topic_get_topic_length( - const Tox_Event_Group_Topic *group_topic); + const Tox_Event_Group_Topic *_Nonnull group_topic); typedef struct Tox_Event_Group_Privacy_State Tox_Event_Group_Privacy_State; uint32_t tox_event_group_privacy_state_get_group_number( - const Tox_Event_Group_Privacy_State *group_privacy_state); + const Tox_Event_Group_Privacy_State *_Nonnull group_privacy_state); Tox_Group_Privacy_State tox_event_group_privacy_state_get_privacy_state( - const Tox_Event_Group_Privacy_State *group_privacy_state); + const Tox_Event_Group_Privacy_State *_Nonnull group_privacy_state); typedef struct Tox_Event_Group_Voice_State Tox_Event_Group_Voice_State; uint32_t tox_event_group_voice_state_get_group_number( - const Tox_Event_Group_Voice_State *group_voice_state); + const Tox_Event_Group_Voice_State *_Nonnull group_voice_state); Tox_Group_Voice_State tox_event_group_voice_state_get_voice_state( - const Tox_Event_Group_Voice_State *group_voice_state); + const Tox_Event_Group_Voice_State *_Nonnull group_voice_state); typedef struct Tox_Event_Group_Topic_Lock Tox_Event_Group_Topic_Lock; uint32_t tox_event_group_topic_lock_get_group_number( - const Tox_Event_Group_Topic_Lock *group_topic_lock); + const Tox_Event_Group_Topic_Lock *_Nonnull group_topic_lock); Tox_Group_Topic_Lock tox_event_group_topic_lock_get_topic_lock( - const Tox_Event_Group_Topic_Lock *group_topic_lock); + const Tox_Event_Group_Topic_Lock *_Nonnull group_topic_lock); typedef struct Tox_Event_Group_Peer_Limit Tox_Event_Group_Peer_Limit; uint32_t tox_event_group_peer_limit_get_group_number( - const Tox_Event_Group_Peer_Limit *group_peer_limit); + const Tox_Event_Group_Peer_Limit *_Nonnull group_peer_limit); uint32_t tox_event_group_peer_limit_get_peer_limit( - const Tox_Event_Group_Peer_Limit *group_peer_limit); + const Tox_Event_Group_Peer_Limit *_Nonnull group_peer_limit); typedef struct Tox_Event_Group_Password Tox_Event_Group_Password; uint32_t tox_event_group_password_get_group_number( - const Tox_Event_Group_Password *group_password); -const uint8_t *tox_event_group_password_get_password( - const Tox_Event_Group_Password *group_password); + const Tox_Event_Group_Password *_Nonnull group_password); +const uint8_t *_Nullable tox_event_group_password_get_password( + const Tox_Event_Group_Password *_Nonnull group_password); uint32_t tox_event_group_password_get_password_length( - const Tox_Event_Group_Password *group_password); + const Tox_Event_Group_Password *_Nonnull group_password); typedef struct Tox_Event_Group_Message Tox_Event_Group_Message; uint32_t tox_event_group_message_get_group_number( - const Tox_Event_Group_Message *group_message); + const Tox_Event_Group_Message *_Nonnull group_message); uint32_t tox_event_group_message_get_peer_id( - const Tox_Event_Group_Message *group_message); + const Tox_Event_Group_Message *_Nonnull group_message); Tox_Message_Type tox_event_group_message_get_message_type( - const Tox_Event_Group_Message *group_message); -const uint8_t *tox_event_group_message_get_message( - const Tox_Event_Group_Message *group_message); + const Tox_Event_Group_Message *_Nonnull group_message); +const uint8_t *_Nullable tox_event_group_message_get_message( + const Tox_Event_Group_Message *_Nonnull group_message); uint32_t tox_event_group_message_get_message_length( - const Tox_Event_Group_Message *group_message); + const Tox_Event_Group_Message *_Nonnull group_message); uint32_t tox_event_group_message_get_message_id( - const Tox_Event_Group_Message *group_message); + const Tox_Event_Group_Message *_Nonnull group_message); typedef struct Tox_Event_Group_Private_Message Tox_Event_Group_Private_Message; uint32_t tox_event_group_private_message_get_group_number( - const Tox_Event_Group_Private_Message *group_private_message); + const Tox_Event_Group_Private_Message *_Nonnull group_private_message); uint32_t tox_event_group_private_message_get_peer_id( - const Tox_Event_Group_Private_Message *group_private_message); + const Tox_Event_Group_Private_Message *_Nonnull group_private_message); Tox_Message_Type tox_event_group_private_message_get_message_type( - const Tox_Event_Group_Private_Message *group_private_message); -const uint8_t *tox_event_group_private_message_get_message( - const Tox_Event_Group_Private_Message *group_private_message); + const Tox_Event_Group_Private_Message *_Nonnull group_private_message); +const uint8_t *_Nullable tox_event_group_private_message_get_message( + const Tox_Event_Group_Private_Message *_Nonnull group_private_message); uint32_t tox_event_group_private_message_get_message_length( - const Tox_Event_Group_Private_Message *group_private_message); + const Tox_Event_Group_Private_Message *_Nonnull group_private_message); uint32_t tox_event_group_private_message_get_message_id( - const Tox_Event_Group_Private_Message *group_private_message); + const Tox_Event_Group_Private_Message *_Nonnull group_private_message); typedef struct Tox_Event_Group_Custom_Packet Tox_Event_Group_Custom_Packet; uint32_t tox_event_group_custom_packet_get_group_number( - const Tox_Event_Group_Custom_Packet *group_custom_packet); + const Tox_Event_Group_Custom_Packet *_Nonnull group_custom_packet); uint32_t tox_event_group_custom_packet_get_peer_id( - const Tox_Event_Group_Custom_Packet *group_custom_packet); -const uint8_t *tox_event_group_custom_packet_get_data( - const Tox_Event_Group_Custom_Packet *group_custom_packet); + const Tox_Event_Group_Custom_Packet *_Nonnull group_custom_packet); +const uint8_t *_Nullable tox_event_group_custom_packet_get_data( + const Tox_Event_Group_Custom_Packet *_Nonnull group_custom_packet); uint32_t tox_event_group_custom_packet_get_data_length( - const Tox_Event_Group_Custom_Packet *group_custom_packet); + const Tox_Event_Group_Custom_Packet *_Nonnull group_custom_packet); typedef struct Tox_Event_Group_Custom_Private_Packet Tox_Event_Group_Custom_Private_Packet; uint32_t tox_event_group_custom_private_packet_get_group_number( - const Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet); + const Tox_Event_Group_Custom_Private_Packet *_Nonnull group_custom_private_packet); uint32_t tox_event_group_custom_private_packet_get_peer_id( - const Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet); -const uint8_t *tox_event_group_custom_private_packet_get_data( - const Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet); + const Tox_Event_Group_Custom_Private_Packet *_Nonnull group_custom_private_packet); +const uint8_t *_Nullable tox_event_group_custom_private_packet_get_data( + const Tox_Event_Group_Custom_Private_Packet *_Nonnull group_custom_private_packet); uint32_t tox_event_group_custom_private_packet_get_data_length( - const Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet); + const Tox_Event_Group_Custom_Private_Packet *_Nonnull group_custom_private_packet); typedef struct Tox_Event_Group_Invite Tox_Event_Group_Invite; uint32_t tox_event_group_invite_get_friend_number( - const Tox_Event_Group_Invite *group_invite); -const uint8_t *tox_event_group_invite_get_invite_data( - const Tox_Event_Group_Invite *group_invite); + const Tox_Event_Group_Invite *_Nonnull group_invite); +const uint8_t *_Nullable tox_event_group_invite_get_invite_data( + const Tox_Event_Group_Invite *_Nonnull group_invite); uint32_t tox_event_group_invite_get_invite_data_length( - const Tox_Event_Group_Invite *group_invite); -const uint8_t *tox_event_group_invite_get_group_name( - const Tox_Event_Group_Invite *group_invite); + const Tox_Event_Group_Invite *_Nonnull group_invite); +const uint8_t *_Nullable tox_event_group_invite_get_group_name( + const Tox_Event_Group_Invite *_Nonnull group_invite); uint32_t tox_event_group_invite_get_group_name_length( - const Tox_Event_Group_Invite *group_invite); + const Tox_Event_Group_Invite *_Nonnull group_invite); typedef struct Tox_Event_Group_Peer_Join Tox_Event_Group_Peer_Join; uint32_t tox_event_group_peer_join_get_group_number( - const Tox_Event_Group_Peer_Join *group_peer_join); + const Tox_Event_Group_Peer_Join *_Nonnull group_peer_join); uint32_t tox_event_group_peer_join_get_peer_id( - const Tox_Event_Group_Peer_Join *group_peer_join); + const Tox_Event_Group_Peer_Join *_Nonnull group_peer_join); typedef struct Tox_Event_Group_Peer_Exit Tox_Event_Group_Peer_Exit; uint32_t tox_event_group_peer_exit_get_group_number( - const Tox_Event_Group_Peer_Exit *group_peer_exit); + const Tox_Event_Group_Peer_Exit *_Nonnull group_peer_exit); uint32_t tox_event_group_peer_exit_get_peer_id( - const Tox_Event_Group_Peer_Exit *group_peer_exit); + const Tox_Event_Group_Peer_Exit *_Nonnull group_peer_exit); Tox_Group_Exit_Type tox_event_group_peer_exit_get_exit_type( - const Tox_Event_Group_Peer_Exit *group_peer_exit); -const uint8_t *tox_event_group_peer_exit_get_name( - const Tox_Event_Group_Peer_Exit *group_peer_exit); + const Tox_Event_Group_Peer_Exit *_Nonnull group_peer_exit); +const uint8_t *_Nullable tox_event_group_peer_exit_get_name( + const Tox_Event_Group_Peer_Exit *_Nonnull group_peer_exit); uint32_t tox_event_group_peer_exit_get_name_length( - const Tox_Event_Group_Peer_Exit *group_peer_exit); -const uint8_t *tox_event_group_peer_exit_get_part_message( - const Tox_Event_Group_Peer_Exit *group_peer_exit); + const Tox_Event_Group_Peer_Exit *_Nonnull group_peer_exit); +const uint8_t *_Nullable tox_event_group_peer_exit_get_part_message( + const Tox_Event_Group_Peer_Exit *_Nonnull group_peer_exit); uint32_t tox_event_group_peer_exit_get_part_message_length( - const Tox_Event_Group_Peer_Exit *group_peer_exit); + const Tox_Event_Group_Peer_Exit *_Nonnull group_peer_exit); typedef struct Tox_Event_Group_Self_Join Tox_Event_Group_Self_Join; uint32_t tox_event_group_self_join_get_group_number( - const Tox_Event_Group_Self_Join *group_self_join); + const Tox_Event_Group_Self_Join *_Nonnull group_self_join); typedef struct Tox_Event_Group_Join_Fail Tox_Event_Group_Join_Fail; uint32_t tox_event_group_join_fail_get_group_number( - const Tox_Event_Group_Join_Fail *group_join_fail); + const Tox_Event_Group_Join_Fail *_Nonnull group_join_fail); Tox_Group_Join_Fail tox_event_group_join_fail_get_fail_type( - const Tox_Event_Group_Join_Fail *group_join_fail); + const Tox_Event_Group_Join_Fail *_Nonnull group_join_fail); typedef struct Tox_Event_Group_Moderation Tox_Event_Group_Moderation; uint32_t tox_event_group_moderation_get_group_number( - const Tox_Event_Group_Moderation *group_moderation); + const Tox_Event_Group_Moderation *_Nonnull group_moderation); uint32_t tox_event_group_moderation_get_source_peer_id( - const Tox_Event_Group_Moderation *group_moderation); + const Tox_Event_Group_Moderation *_Nonnull group_moderation); uint32_t tox_event_group_moderation_get_target_peer_id( - const Tox_Event_Group_Moderation *group_moderation); + const Tox_Event_Group_Moderation *_Nonnull group_moderation); Tox_Group_Mod_Event tox_event_group_moderation_get_mod_type( - const Tox_Event_Group_Moderation *group_moderation); + const Tox_Event_Group_Moderation *_Nonnull group_moderation); typedef struct Tox_Event_Dht_Nodes_Response Tox_Event_Dht_Nodes_Response; -const uint8_t *tox_event_dht_nodes_response_get_public_key( - const Tox_Event_Dht_Nodes_Response *dht_nodes_response); -const uint8_t *tox_event_dht_nodes_response_get_ip( - const Tox_Event_Dht_Nodes_Response *dht_nodes_response); +const uint8_t *_Nonnull tox_event_dht_nodes_response_get_public_key( + const Tox_Event_Dht_Nodes_Response *_Nonnull dht_nodes_response); +const char *_Nullable tox_event_dht_nodes_response_get_ip( + const Tox_Event_Dht_Nodes_Response *_Nonnull dht_nodes_response); uint32_t tox_event_dht_nodes_response_get_ip_length( - const Tox_Event_Dht_Nodes_Response *dht_nodes_response); + const Tox_Event_Dht_Nodes_Response *_Nonnull dht_nodes_response); uint16_t tox_event_dht_nodes_response_get_port( - const Tox_Event_Dht_Nodes_Response *dht_nodes_response); + const Tox_Event_Dht_Nodes_Response *_Nonnull dht_nodes_response); typedef enum Tox_Event_Type { TOX_EVENT_SELF_CONNECTION_STATUS = 0, @@ -415,7 +419,7 @@ typedef enum Tox_Event_Type { TOX_EVENT_INVALID = 255, } Tox_Event_Type; -const char *tox_event_type_to_string(Tox_Event_Type type); +const char *_Nonnull tox_event_type_to_string(Tox_Event_Type type); /** * A single Tox core event. @@ -427,88 +431,88 @@ const char *tox_event_type_to_string(Tox_Event_Type type); */ typedef struct Tox_Event Tox_Event; -Tox_Event_Type tox_event_get_type(const Tox_Event *event); +Tox_Event_Type tox_event_get_type(const Tox_Event *_Nonnull event); -const Tox_Event_Conference_Connected *tox_event_get_conference_connected( - const Tox_Event *event); -const Tox_Event_Conference_Invite *tox_event_get_conference_invite( - const Tox_Event *event); -const Tox_Event_Conference_Message *tox_event_get_conference_message( - const Tox_Event *event); -const Tox_Event_Conference_Peer_List_Changed *tox_event_get_conference_peer_list_changed( - const Tox_Event *event); -const Tox_Event_Conference_Peer_Name *tox_event_get_conference_peer_name( - const Tox_Event *event); -const Tox_Event_Conference_Title *tox_event_get_conference_title( - const Tox_Event *event); -const Tox_Event_File_Chunk_Request *tox_event_get_file_chunk_request( - const Tox_Event *event); -const Tox_Event_File_Recv_Chunk *tox_event_get_file_recv_chunk( - const Tox_Event *event); -const Tox_Event_File_Recv_Control *tox_event_get_file_recv_control( - const Tox_Event *event); -const Tox_Event_File_Recv *tox_event_get_file_recv( - const Tox_Event *event); -const Tox_Event_Friend_Connection_Status *tox_event_get_friend_connection_status( - const Tox_Event *event); -const Tox_Event_Friend_Lossless_Packet *tox_event_get_friend_lossless_packet( - const Tox_Event *event); -const Tox_Event_Friend_Lossy_Packet *tox_event_get_friend_lossy_packet( - const Tox_Event *event); -const Tox_Event_Friend_Message *tox_event_get_friend_message( - const Tox_Event *event); -const Tox_Event_Friend_Name *tox_event_get_friend_name( - const Tox_Event *event); -const Tox_Event_Friend_Read_Receipt *tox_event_get_friend_read_receipt( - const Tox_Event *event); -const Tox_Event_Friend_Request *tox_event_get_friend_request( - const Tox_Event *event); -const Tox_Event_Friend_Status_Message *tox_event_get_friend_status_message( - const Tox_Event *event); -const Tox_Event_Friend_Status *tox_event_get_friend_status( - const Tox_Event *event); -const Tox_Event_Friend_Typing *tox_event_get_friend_typing( - const Tox_Event *event); -const Tox_Event_Self_Connection_Status *tox_event_get_self_connection_status( - const Tox_Event *event); -const Tox_Event_Group_Peer_Name *tox_event_get_group_peer_name( - const Tox_Event *event); -const Tox_Event_Group_Peer_Status *tox_event_get_group_peer_status( - const Tox_Event *event); -const Tox_Event_Group_Topic *tox_event_get_group_topic( - const Tox_Event *event); -const Tox_Event_Group_Privacy_State *tox_event_get_group_privacy_state( - const Tox_Event *event); -const Tox_Event_Group_Voice_State *tox_event_get_group_voice_state( - const Tox_Event *event); -const Tox_Event_Group_Topic_Lock *tox_event_get_group_topic_lock( - const Tox_Event *event); -const Tox_Event_Group_Peer_Limit *tox_event_get_group_peer_limit( - const Tox_Event *event); -const Tox_Event_Group_Password *tox_event_get_group_password( - const Tox_Event *event); -const Tox_Event_Group_Message *tox_event_get_group_message( - const Tox_Event *event); -const Tox_Event_Group_Private_Message *tox_event_get_group_private_message( - const Tox_Event *event); -const Tox_Event_Group_Custom_Packet *tox_event_get_group_custom_packet( - const Tox_Event *event); -const Tox_Event_Group_Custom_Private_Packet *tox_event_get_group_custom_private_packet( - const Tox_Event *event); -const Tox_Event_Group_Invite *tox_event_get_group_invite( - const Tox_Event *event); -const Tox_Event_Group_Peer_Join *tox_event_get_group_peer_join( - const Tox_Event *event); -const Tox_Event_Group_Peer_Exit *tox_event_get_group_peer_exit( - const Tox_Event *event); -const Tox_Event_Group_Self_Join *tox_event_get_group_self_join( - const Tox_Event *event); -const Tox_Event_Group_Join_Fail *tox_event_get_group_join_fail( - const Tox_Event *event); -const Tox_Event_Group_Moderation *tox_event_get_group_moderation( - const Tox_Event *event); -const Tox_Event_Dht_Nodes_Response *tox_event_get_dht_nodes_response( - const Tox_Event *event); +const Tox_Event_Conference_Connected *_Nullable tox_event_get_conference_connected( + const Tox_Event *_Nonnull event); +const Tox_Event_Conference_Invite *_Nullable tox_event_get_conference_invite( + const Tox_Event *_Nonnull event); +const Tox_Event_Conference_Message *_Nullable tox_event_get_conference_message( + const Tox_Event *_Nonnull event); +const Tox_Event_Conference_Peer_List_Changed *_Nullable tox_event_get_conference_peer_list_changed( + const Tox_Event *_Nonnull event); +const Tox_Event_Conference_Peer_Name *_Nullable tox_event_get_conference_peer_name( + const Tox_Event *_Nonnull event); +const Tox_Event_Conference_Title *_Nullable tox_event_get_conference_title( + const Tox_Event *_Nonnull event); +const Tox_Event_File_Chunk_Request *_Nullable tox_event_get_file_chunk_request( + const Tox_Event *_Nonnull event); +const Tox_Event_File_Recv_Chunk *_Nullable tox_event_get_file_recv_chunk( + const Tox_Event *_Nonnull event); +const Tox_Event_File_Recv_Control *_Nullable tox_event_get_file_recv_control( + const Tox_Event *_Nonnull event); +const Tox_Event_File_Recv *_Nullable tox_event_get_file_recv( + const Tox_Event *_Nonnull event); +const Tox_Event_Friend_Connection_Status *_Nullable tox_event_get_friend_connection_status( + const Tox_Event *_Nonnull event); +const Tox_Event_Friend_Lossless_Packet *_Nullable tox_event_get_friend_lossless_packet( + const Tox_Event *_Nonnull event); +const Tox_Event_Friend_Lossy_Packet *_Nullable tox_event_get_friend_lossy_packet( + const Tox_Event *_Nonnull event); +const Tox_Event_Friend_Message *_Nullable tox_event_get_friend_message( + const Tox_Event *_Nonnull event); +const Tox_Event_Friend_Name *_Nullable tox_event_get_friend_name( + const Tox_Event *_Nonnull event); +const Tox_Event_Friend_Read_Receipt *_Nullable tox_event_get_friend_read_receipt( + const Tox_Event *_Nonnull event); +const Tox_Event_Friend_Request *_Nullable tox_event_get_friend_request( + const Tox_Event *_Nonnull event); +const Tox_Event_Friend_Status_Message *_Nullable tox_event_get_friend_status_message( + const Tox_Event *_Nonnull event); +const Tox_Event_Friend_Status *_Nullable tox_event_get_friend_status( + const Tox_Event *_Nonnull event); +const Tox_Event_Friend_Typing *_Nullable tox_event_get_friend_typing( + const Tox_Event *_Nonnull event); +const Tox_Event_Self_Connection_Status *_Nullable tox_event_get_self_connection_status( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Peer_Name *_Nullable tox_event_get_group_peer_name( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Peer_Status *_Nullable tox_event_get_group_peer_status( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Topic *_Nullable tox_event_get_group_topic( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Privacy_State *_Nullable tox_event_get_group_privacy_state( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Voice_State *_Nullable tox_event_get_group_voice_state( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Topic_Lock *_Nullable tox_event_get_group_topic_lock( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Peer_Limit *_Nullable tox_event_get_group_peer_limit( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Password *_Nullable tox_event_get_group_password( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Message *_Nullable tox_event_get_group_message( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Private_Message *_Nullable tox_event_get_group_private_message( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Custom_Packet *_Nullable tox_event_get_group_custom_packet( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Custom_Private_Packet *_Nullable tox_event_get_group_custom_private_packet( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Invite *_Nullable tox_event_get_group_invite( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Peer_Join *_Nullable tox_event_get_group_peer_join( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Peer_Exit *_Nullable tox_event_get_group_peer_exit( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Self_Join *_Nullable tox_event_get_group_self_join( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Join_Fail *_Nullable tox_event_get_group_join_fail( + const Tox_Event *_Nonnull event); +const Tox_Event_Group_Moderation *_Nullable tox_event_get_group_moderation( + const Tox_Event *_Nonnull event); +const Tox_Event_Dht_Nodes_Response *_Nullable tox_event_get_dht_nodes_response( + const Tox_Event *_Nonnull event); /** * Container object for all Tox core events. @@ -517,8 +521,8 @@ const Tox_Event_Dht_Nodes_Response *tox_event_get_dht_nodes_response( */ typedef struct Tox_Events Tox_Events; -uint32_t tox_events_get_size(const Tox_Events *events); -const Tox_Event *tox_events_get(const Tox_Events *events, uint32_t index); +uint32_t tox_events_get_size(const Tox_Events *_Nullable events); +const Tox_Event *_Nullable tox_events_get(const Tox_Events *_Nullable events, uint32_t index); /** * Initialise the events recording system. @@ -528,7 +532,7 @@ const Tox_Event *tox_events_get(const Tox_Events *events, uint32_t index); * 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); +void tox_events_init(Tox *_Nonnull tox); typedef enum Tox_Err_Events_Iterate { /** @@ -564,7 +568,17 @@ typedef enum Tox_Err_Events_Iterate { * * @return the recorded events structure. */ -Tox_Events *tox_events_iterate(Tox *tox, bool fail_hard, Tox_Err_Events_Iterate *error); +Tox_Events *_Nullable tox_events_iterate(Tox *_Nonnull tox, bool fail_hard, Tox_Err_Events_Iterate *_Nullable error); + +/** + * Dispatch all events in the events object to the registered callbacks in the + * Tox instance. + * + * @param tox The Tox instance to dispatch to. + * @param events The events object to dispatch. + * @param user_data The user data to pass to the callbacks. + */ +void tox_events_dispatch(Tox *_Nonnull tox, Tox_Events *_Nullable events, void *_Nullable user_data); /** * Frees all memory associated with the events structure. @@ -572,16 +586,16 @@ Tox_Events *tox_events_iterate(Tox *tox, bool fail_hard, Tox_Err_Events_Iterate * 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); +void tox_events_free(Tox_Events *_Nullable events); -uint32_t tox_events_bytes_size(const Tox_Events *events); -bool tox_events_get_bytes(const Tox_Events *events, uint8_t *bytes); +uint32_t tox_events_bytes_size(const Tox_Events *_Nullable events); +bool tox_events_get_bytes(const Tox_Events *_Nullable events, uint8_t *_Nonnull bytes); typedef struct Tox_System Tox_System; -Tox_Events *tox_events_load(const Tox_System *sys, const uint8_t *bytes, uint32_t bytes_size); +Tox_Events *_Nullable tox_events_load(const Tox_System *_Nonnull sys, const uint8_t *_Nonnull bytes, uint32_t bytes_size); -bool tox_events_equal(const Tox_System *sys, const Tox_Events *a, const Tox_Events *b); +bool tox_events_equal(const Tox_System *_Nonnull sys, const Tox_Events *_Nullable a, const Tox_Events *_Nullable b); #ifdef __cplusplus } /* extern "C" */ diff --git a/toxcore/tox_events_fuzz_test.cc b/toxcore/tox_events_fuzz_test.cc index 87c89903..d5f8d2ab 100644 --- a/toxcore/tox_events_fuzz_test.cc +++ b/toxcore/tox_events_fuzz_test.cc @@ -19,20 +19,20 @@ using tox::test::SimulatedEnvironment; void TestUnpack(Fuzz_Data data) { // 2 bytes: size of the events data - CONSUME_OR_RETURN(const uint8_t *events_size_bytes, data, sizeof(uint16_t)); - uint16_t events_size; - std::memcpy(&events_size, events_size_bytes, sizeof(uint16_t)); + CONSUME_OR_RETURN(const std::uint8_t *events_size_bytes, data, sizeof(std::uint16_t)); + std::uint16_t events_size; + std::memcpy(&events_size, events_size_bytes, sizeof(std::uint16_t)); // events_size bytes: events data (max 64K) - CONSUME_OR_RETURN(const uint8_t *events_data, data, events_size); + CONSUME_OR_RETURN(const std::uint8_t *events_data, data, events_size); if (data.empty()) { - // If there's no more input, no malloc failure paths can possibly be + // If there's no more input, no std::malloc failure paths can possibly be // tested, so we ignore this input. return; } - // rest of the fuzz data is input for malloc + // rest of the fuzz data is input for std::malloc SimulatedEnvironment env; auto node = env.create_node(33445); configure_fuzz_memory_source(env.fake_memory(), data); @@ -83,7 +83,7 @@ void TestUnpack(Fuzz_Data data) Tox_Events *events = tox_events_load(&node->system, events_data, events_size); if (events) { - std::vector packed(tox_events_bytes_size(events)); + std::vector packed(tox_events_bytes_size(events)); tox_events_get_bytes(events, packed.data()); tox_dispatch_invoke(dispatch, events, nullptr); @@ -94,8 +94,8 @@ void TestUnpack(Fuzz_Data data) } // namespace -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size); +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { TestUnpack(Fuzz_Data(data, size)); return 0; diff --git a/toxcore/tox_events_test.cc b/toxcore/tox_events_test.cc index b2854ffd..21af14cc 100644 --- a/toxcore/tox_events_test.cc +++ b/toxcore/tox_events_test.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include "crypto_core.h" @@ -20,7 +21,7 @@ TEST(ToxEvents, UnpackRandomDataDoesntCrash) SimulatedEnvironment env; auto node = env.create_node(33445); ASSERT_NE(node->system.rng, nullptr); - std::array data; + std::array data; random_bytes(node->system.rng, data.data(), data.size()); tox_events_free(tox_events_load(&node->system, data.data(), data.size())); } @@ -29,8 +30,8 @@ TEST(ToxEvents, UnpackEmptyDataFails) { SimulatedEnvironment env; auto node = env.create_node(33445); - std::array data; - Tox_Events *events = tox_events_load(&node->system, data.end(), 0); + std::array data; + Tox_Events *events = tox_events_load(&node->system, data.data(), 0); EXPECT_EQ(events, nullptr); } @@ -38,7 +39,7 @@ TEST(ToxEvents, UnpackEmptyArrayCreatesEmptyEvents) { SimulatedEnvironment env; auto node = env.create_node(33445); - std::array data{0x90}; // empty msgpack array + std::array data{0x90}; // empty msgpack array Tox_Events *events = tox_events_load(&node->system, data.data(), data.size()); ASSERT_NE(events, nullptr); EXPECT_EQ(tox_events_get_size(events), 0); @@ -47,10 +48,10 @@ TEST(ToxEvents, UnpackEmptyArrayCreatesEmptyEvents) TEST(ToxEvents, NullEventsPacksToEmptyArray) { - std::array bytes; + std::array bytes; ASSERT_EQ(tox_events_bytes_size(nullptr), bytes.size()); tox_events_get_bytes(nullptr, bytes.data()); - EXPECT_EQ(bytes, (std::array{0x90})); + EXPECT_EQ(bytes, (std::array{0x90})); } TEST(ToxEvents, PackedEventsCanBeUnpacked) @@ -58,13 +59,13 @@ TEST(ToxEvents, PackedEventsCanBeUnpacked) SimulatedEnvironment env; auto node = env.create_node(33445); // [[0, 1]] == Tox_Self_Connection_Status { .connection_status = TOX_CONNECTION_TCP } - std::array packed{0x91, 0x92, 0xcc, 0x00, 0xcc, 0x01}; + std::array packed{0x91, 0x92, 0xcc, 0x00, 0xcc, 0x01}; Tox_Events *events = tox_events_load(&node->system, packed.data(), packed.size()); ASSERT_NE(events, nullptr); - std::array bytes; + std::array bytes; ASSERT_EQ(tox_events_bytes_size(events), bytes.size()); tox_events_get_bytes(events, bytes.data()); - EXPECT_EQ(bytes, (std::array{0x91, 0x92, 0x00, 0x01})); + EXPECT_EQ(bytes, (std::array{0x91, 0x92, 0x00, 0x01})); tox_events_free(events); } @@ -72,7 +73,7 @@ TEST(ToxEvents, DealsWithHugeMsgpackArrays) { SimulatedEnvironment env; auto node = env.create_node(33445); - std::vector data{0xdd, 0xff, 0xff, 0xff, 0xff}; + std::vector data{0xdd, 0xff, 0xff, 0xff, 0xff}; EXPECT_EQ(tox_events_load(&node->system, data.data(), data.size()), nullptr); } diff --git a/toxcore/tox_memory.c b/toxcore/tox_memory.c deleted file mode 100644 index a2d6ad8a..00000000 --- a/toxcore/tox_memory.c +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later - * Copyright © 2016-2025 The TokTok team. - * Copyright © 2013 Tox project. - */ -#include "tox_memory.h" - -#include - -#include "ccompat.h" -#include "tox_memory_impl.h" // IWYU pragma: keep - -Tox_Memory *tox_memory_new(const Tox_Memory_Funcs *funcs, void *user_data) -{ - const Tox_Memory bootstrap = {funcs, user_data}; - - Tox_Memory *mem = (Tox_Memory *)tox_memory_alloc(&bootstrap, sizeof(Tox_Memory)); - - if (mem == nullptr) { - return nullptr; - } - - *mem = bootstrap; - - return mem; -} - -void tox_memory_free(Tox_Memory *mem) -{ - if (mem == nullptr) { - return; - } - - tox_memory_dealloc(mem, mem); -} - -void *tox_memory_malloc(const Tox_Memory *mem, uint32_t size) -{ - void *const ptr = mem->funcs->malloc_callback(mem->user_data, size); - return ptr; -} - -void *tox_memory_alloc(const Tox_Memory *mem, uint32_t size) -{ - void *const ptr = tox_memory_malloc(mem, size); - if (ptr != nullptr) { - memset(ptr, 0, size); - } - return ptr; -} - -void *tox_memory_realloc(const Tox_Memory *mem, void *ptr, uint32_t size) -{ - void *const new_ptr = mem->funcs->realloc_callback(mem->user_data, ptr, size); - return new_ptr; -} - -void tox_memory_dealloc(const Tox_Memory *mem, void *ptr) -{ - mem->funcs->dealloc_callback(mem->user_data, ptr); -} diff --git a/toxcore/tox_memory.h b/toxcore/tox_memory.h deleted file mode 100644 index c872bf4d..00000000 --- a/toxcore/tox_memory.h +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later - * Copyright © 2016-2025 The TokTok team. - * Copyright © 2013 Tox project. - */ - -/** - * Memory allocation and deallocation functions. - */ -#ifndef C_TOXCORE_TOXCORE_TOX_MEMORY_H -#define C_TOXCORE_TOXCORE_TOX_MEMORY_H - -#include // uint*_t - -#include "tox_attributes.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** @brief Functions wrapping standard C memory allocation functions. */ -typedef struct Tox_Memory_Funcs Tox_Memory_Funcs; - -/** - * @brief A dynamic memory allocator. - */ -typedef struct Tox_Memory Tox_Memory; - -/** - * @brief Allocates a new allocator using itself to allocate its own memory. - * - * The passed `user_data` is stored and passed to allocator callbacks. It must - * outlive the `Tox_Memory` object, since it may be used by the callback invoked - * in `tox_memory_free`. - * - * @return NULL if allocation fails. - */ -Tox_Memory *_Nullable tox_memory_new(const Tox_Memory_Funcs *_Nonnull funcs, void *_Nullable user_data); - -/** - * @brief Destroys the allocator using its own deallocation function. - * - * The stored `user_data` will not be deallocated. - */ -void tox_memory_free(Tox_Memory *_Nullable mem); - -/** - * @brief Allocate an array of a given size for built-in types. - * - * The array will not be initialised. Supported built-in types are - * `uint8_t`, `int8_t`, and `int16_t`. - */ -void *_Nullable tox_memory_malloc(const Tox_Memory *_Nonnull mem, uint32_t size); - -/** - * @brief Allocate a single zero-initialised object. - * - * Always use as `(T *)tox_memory_alloc(mem, sizeof(T))`. Unlike `calloc`, this - * does not support allocating arrays. Use `malloc` and `memset` for that. - * - * @param mem The memory allocator. - * @param size Size in bytes of each element. - */ -void *_Nullable tox_memory_alloc(const Tox_Memory *_Nonnull mem, uint32_t size); - -/** @brief Resize a memory chunk vector. */ -void *_Nullable tox_memory_realloc(const Tox_Memory *_Nonnull mem, void *_Nullable ptr, uint32_t size); - -/** @brief Free an array, object, or object vector. */ -void tox_memory_dealloc(const Tox_Memory *_Nonnull mem, void *_Nullable ptr); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* C_TOXCORE_TOXCORE_TOX_MEMORY_H */ diff --git a/toxcore/tox_memory_impl.h b/toxcore/tox_memory_impl.h deleted file mode 100644 index 789c57f9..00000000 --- a/toxcore/tox_memory_impl.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later - * Copyright © 2016-2025 The TokTok team. - * Copyright © 2013 Tox project. - */ - -/** - * Datatypes, functions and includes for the core networking. - */ -#ifndef C_TOXCORE_TOXCORE_TOX_MEMORY_IMPL_H -#define C_TOXCORE_TOXCORE_TOX_MEMORY_IMPL_H - -#include // uint*_t - -#include "tox_memory.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** @brief Allocate a byte array, similar to malloc. */ -typedef void *tox_memory_malloc_cb(void *self, uint32_t size); -/** @brief Reallocate a byte array, similar to realloc. */ -typedef void *tox_memory_realloc_cb(void *self, void *ptr, uint32_t size); -/** - * @brief Deallocate a byte or object array, similar to free. - * - * Note that `tox_memory_free` will use this callback to deallocate itself, so - * once the deallocation is done, the allocator data structures can no longer be - * referenced. - */ -typedef void tox_memory_dealloc_cb(void *self, void *ptr); - -/** @brief Functions wrapping standard C memory allocation functions. */ -struct Tox_Memory_Funcs { - tox_memory_malloc_cb *malloc_callback; - tox_memory_realloc_cb *realloc_callback; - tox_memory_dealloc_cb *dealloc_callback; -}; - -struct Tox_Memory { - const Tox_Memory_Funcs *funcs; - void *user_data; -}; - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* C_TOXCORE_TOXCORE_TOX_MEMORY_IMPL_H */ diff --git a/toxcore/tox_pack.h b/toxcore/tox_pack.h index 56ef94de..b98eaa8f 100644 --- a/toxcore/tox_pack.h +++ b/toxcore/tox_pack.h @@ -5,6 +5,8 @@ #ifndef C_TOXCORE_TOXCORE_TOX_PACK_H #define C_TOXCORE_TOXCORE_TOX_PACK_H +#include + #include "attributes.h" #include "bin_pack.h" #include "tox.h" diff --git a/toxcore/tox_private.c b/toxcore/tox_private.c index 12aabc87..c40a0dec 100644 --- a/toxcore/tox_private.c +++ b/toxcore/tox_private.c @@ -19,10 +19,12 @@ #include "group_common.h" #include "logger.h" #include "mem.h" +#include "net.h" #include "net_crypto.h" #include "net_profile.h" #include "network.h" #include "os_memory.h" +#include "os_network.h" #include "os_random.h" #include "tox.h" #include "tox_struct.h" // IWYU pragma: keep diff --git a/toxcore/tox_private.h b/toxcore/tox_private.h index a23674ec..d50e1bad 100644 --- a/toxcore/tox_private.h +++ b/toxcore/tox_private.h @@ -10,8 +10,8 @@ #include #include -#include "attributes.h" #include "tox.h" +#include "tox_attributes.h" #include "tox_options.h" #ifdef __cplusplus @@ -20,12 +20,16 @@ extern "C" { typedef uint64_t tox_mono_time_cb(void *_Nullable user_data); +typedef struct Random Random; +typedef struct Network Network; +typedef struct Memory Memory; + typedef struct Tox_System { tox_mono_time_cb *_Nullable mono_time_callback; void *_Nullable mono_time_user_data; - const struct Tox_Random *_Nullable rng; - const struct Network *_Nullable ns; - const struct Tox_Memory *_Nullable mem; + const Random *_Nullable rng; + const Network *_Nullable ns; + const Memory *_Nullable mem; } Tox_System; Tox_System tox_default_system(void); diff --git a/toxcore/tox_random.c b/toxcore/tox_random.c deleted file mode 100644 index 8f1432d4..00000000 --- a/toxcore/tox_random.c +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later - * Copyright © 2022-2025 The TokTok team. - */ -#include "tox_random.h" - -#include "ccompat.h" -#include "tox_memory.h" -#include "tox_random_impl.h" - -Tox_Random *tox_random_new(const Tox_Random_Funcs *funcs, void *user_data, const Tox_Memory *mem) -{ - Tox_Random *rng = (Tox_Random *)tox_memory_alloc(mem, sizeof(Tox_Random)); - - if (rng == nullptr) { - return nullptr; - } - - rng->funcs = funcs; - rng->user_data = user_data; - - rng->mem = mem; - - return rng; -} - -void tox_random_free(Tox_Random *rng) -{ - if (rng == nullptr || rng->mem == nullptr) { - return; - } - tox_memory_dealloc(rng->mem, rng); -} - -void tox_random_bytes(const Tox_Random *rng, uint8_t *bytes, uint32_t length) -{ - rng->funcs->bytes_callback(rng->user_data, bytes, length); -} - -uint32_t tox_random_uniform(const Tox_Random *rng, uint32_t upper_bound) -{ - return rng->funcs->uniform_callback(rng->user_data, upper_bound); -} diff --git a/toxcore/tox_random.h b/toxcore/tox_random.h deleted file mode 100644 index 3339a41c..00000000 --- a/toxcore/tox_random.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later - * Copyright © 2022-2025 The TokTok team. - */ - -#ifndef C_TOXCORE_TOXCORE_TOX_RANDOM_H -#define C_TOXCORE_TOXCORE_TOX_RANDOM_H - -#include -#include - -#include "tox_attributes.h" -#include "tox_memory.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct Tox_Random_Funcs Tox_Random_Funcs; - -typedef struct Tox_Random Tox_Random; - -Tox_Random *_Nullable tox_random_new(const Tox_Random_Funcs *_Nonnull funcs, void *_Nullable user_data, const Tox_Memory *_Nonnull mem); - -void tox_random_free(Tox_Random *_Nullable rng); - -void tox_random_bytes(const Tox_Random *_Nonnull rng, uint8_t *_Nonnull bytes, uint32_t length); -uint32_t tox_random_uniform(const Tox_Random *_Nonnull rng, uint32_t upper_bound); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* C_TOXCORE_TOXCORE_TOX_RANDOM_H */ diff --git a/toxcore/tox_struct.h b/toxcore/tox_struct.h index 078a9b37..3a8e8b3f 100644 --- a/toxcore/tox_struct.h +++ b/toxcore/tox_struct.h @@ -18,6 +18,7 @@ extern "C" { #endif struct Tox { + struct Logger *_Nonnull log; struct Messenger *_Nonnull m; Mono_Time *_Nonnull mono_time; Tox_System sys; diff --git a/toxcore/tox_test.cc b/toxcore/tox_test.cc index 6bc5a753..7579c4e3 100644 --- a/toxcore/tox_test.cc +++ b/toxcore/tox_test.cc @@ -8,6 +8,7 @@ #include #include +#include "attributes.h" #include "crypto_core.h" #include "os_random.h" #include "tox_log_level.h" @@ -18,14 +19,14 @@ namespace { using tox::test::SimulatedEnvironment; -static void set_random_name_and_status_message( - Tox *tox, const Random *rng, uint8_t *name, uint8_t *status_message) +static void set_random_name_and_status_message(Tox *_Nonnull tox, const Random *_Nonnull rng, + std::uint8_t *_Nonnull name, std::uint8_t *_Nonnull status_message) { - for (uint16_t i = 0; i < tox_max_name_length(); ++i) { + for (std::uint16_t i = 0; i < tox_max_name_length(); ++i) { name[i] = random_u08(rng); } - for (uint16_t i = 0; i < tox_max_status_message_length(); ++i) { + for (std::uint16_t i = 0; i < tox_max_status_message_length(); ++i) { status_message[i] = random_u08(rng); } } @@ -75,7 +76,7 @@ TEST(Tox, BootstrapErrorCodes) ASSERT_NE(tox, nullptr); Tox_Err_Bootstrap err; - std::array pk; + std::array pk; tox_bootstrap(tox, "127.0.0.1", 0, pk.data(), &err); EXPECT_EQ(err, TOX_ERR_BOOTSTRAP_BAD_PORT); @@ -92,8 +93,8 @@ TEST(Tox, OneTest) ASSERT_NE(options, nullptr); tox_options_set_log_callback(options, - [](Tox *tox, Tox_Log_Level level, const char *file, uint32_t line, const char *func, - const char *message, void *user_data) { + [](Tox *_Nonnull tox, Tox_Log_Level level, const char *_Nonnull file, std::uint32_t line, + const char *_Nonnull func, const char *_Nonnull message, void *_Nullable user_data) { fprintf(stderr, "[%c] %s:%u(%s): %s\n", tox_log_level_to_string(level)[0], file, line, func, message); }); @@ -102,11 +103,11 @@ TEST(Tox, OneTest) tox_options_set_start_port(options, 33545); tox_options_set_end_port(options, 33545 + 2000); - std::vector name(tox_max_name_length()); - std::vector status_message(tox_max_status_message_length()); + std::vector name(tox_max_name_length()); + std::vector status_message(tox_max_status_message_length()); - std::vector name2(tox_max_name_length()); - std::vector status_message2(tox_max_status_message_length()); + std::vector name2(tox_max_name_length()); + std::vector status_message2(tox_max_status_message_length()); auto node1 = env.create_node(33545); Tox_Options_Testing testing_opts1 = {}; @@ -126,16 +127,16 @@ TEST(Tox, OneTest) ASSERT_NE(tox2, nullptr); set_random_name_and_status_message(tox2, rng, name2.data(), status_message2.data()); - std::array address; + std::array address; tox_self_get_address(tox1, address.data()); Tox_Err_Friend_Add error; - uint32_t ret - = tox_friend_add(tox1, address.data(), reinterpret_cast("m"), 1, &error); + std::uint32_t ret = tox_friend_add( + tox1, address.data(), reinterpret_cast("m"), 1, &error); EXPECT_EQ(error, TOX_ERR_FRIEND_ADD_OWN_KEY) << "Adding own address worked."; EXPECT_EQ(ret, UINT32_MAX); tox_self_get_address(tox2, address.data()); - std::vector message(tox_max_friend_request_length() + 1); + std::vector message(tox_max_friend_request_length() + 1); ret = tox_friend_add(tox1, address.data(), nullptr, 0, &error); EXPECT_EQ(error, TOX_ERR_FRIEND_ADD_NULL) << "Sending request with no message worked."; EXPECT_EQ(ret, UINT32_MAX); @@ -147,7 +148,8 @@ TEST(Tox, OneTest) EXPECT_EQ(ret, UINT32_MAX); address[0]++; - ret = tox_friend_add(tox1, address.data(), reinterpret_cast("m"), 1, &error); + ret = tox_friend_add( + tox1, address.data(), reinterpret_cast("m"), 1, &error); EXPECT_EQ(error, TOX_ERR_FRIEND_ADD_BAD_CHECKSUM) << "Adding address with bad checksum worked."; EXPECT_EQ(ret, UINT32_MAX); @@ -170,7 +172,7 @@ TEST(Tox, OneTest) << "Can't set status message of length " << tox_max_status_message_length(); tox_self_get_address(tox1, address.data()); - std::vector data(tox_get_savedata_size(tox1)); + std::vector data(tox_get_savedata_size(tox1)); tox_get_savedata(tox1, data.data()); tox_kill(tox2); @@ -185,22 +187,22 @@ TEST(Tox, OneTest) EXPECT_EQ(tox_self_get_status_message_size(tox2), status_message.size()) << "Wrong status message size"; - std::vector name_loaded(tox_max_name_length()); + std::vector name_loaded(tox_max_name_length()); tox_self_get_name(tox2, name_loaded.data()); EXPECT_EQ(name, name_loaded) << "Wrong name."; - std::vector status_message_loaded(tox_max_status_message_length()); + std::vector status_message_loaded(tox_max_status_message_length()); tox_self_get_status_message(tox2, status_message_loaded.data()); EXPECT_EQ(status_message, status_message_loaded) << "Wrong status message."; - std::array address2; + std::array address2; tox_self_get_address(tox2, address2.data()); EXPECT_EQ(address2, address) << "Wrong address."; - std::vector new_name(tox_max_name_length()); + std::vector new_name(tox_max_name_length()); tox_self_get_name(tox2, new_name.data()); EXPECT_EQ(name, new_name) << "Wrong name"; - std::array sk; + std::array sk; tox_self_get_secret_key(tox2, sk.data()); tox_kill(tox2); @@ -210,12 +212,12 @@ TEST(Tox, OneTest) tox2 = tox_new_testing(options, &err_n, &testing_opts2, nullptr); ASSERT_EQ(err_n, TOX_ERR_NEW_OK) << "Load failed"; tox_self_set_nospam(tox2, tox_self_get_nospam(tox1)); - std::array address3; + std::array address3; tox_self_get_address(tox2, address3.data()); EXPECT_EQ(address3, address) << "Wrong public key."; - std::array pk; + std::array pk; tox_self_get_public_key(tox2, pk.data()); - std::array pk_from_addr; + std::array pk_from_addr; std::copy(address.begin(), address.begin() + TOX_PUBLIC_KEY_SIZE, pk_from_addr.begin()); EXPECT_EQ(pk, pk_from_addr) << "Wrong public key."; diff --git a/toxcore/tox_unpack.h b/toxcore/tox_unpack.h index b9b885b1..36741f6a 100644 --- a/toxcore/tox_unpack.h +++ b/toxcore/tox_unpack.h @@ -5,6 +5,8 @@ #ifndef C_TOXCORE_TOXCORE_TOX_UNPACK_H #define C_TOXCORE_TOXCORE_TOX_UNPACK_H +#include + #include "attributes.h" #include "bin_unpack.h" #include "tox.h" diff --git a/toxencryptsave/BUILD.bazel b/toxencryptsave/BUILD.bazel index b2c46a37..807ad39b 100644 --- a/toxencryptsave/BUILD.bazel +++ b/toxencryptsave/BUILD.bazel @@ -20,8 +20,10 @@ cc_library( ":defines", "//c-toxcore/toxcore:ccompat", "//c-toxcore/toxcore:crypto_core", + "//c-toxcore/toxcore:mem", "//c-toxcore/toxcore:os_memory", "//c-toxcore/toxcore:os_random", + "//c-toxcore/toxcore:rng", "@libsodium", ], ) diff --git a/toxencryptsave/toxencryptsave.c b/toxencryptsave/toxencryptsave.c index e79e0f24..80b76485 100644 --- a/toxencryptsave/toxencryptsave.c +++ b/toxencryptsave/toxencryptsave.c @@ -18,6 +18,7 @@ #include "../toxcore/mem.h" #include "../toxcore/os_memory.h" #include "../toxcore/os_random.h" +#include "../toxcore/rng.h" #include "defines.h" static_assert(TOX_PASS_SALT_LENGTH == crypto_pwhash_scryptsalsa208sha256_SALTBYTES, @@ -55,7 +56,7 @@ struct Tox_Pass_Key { void tox_pass_key_free(Tox_Pass_Key *key) { - if (key == NULL) { + if (key == nullptr) { return; }